Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WriteBinary doesn't keep the order of textures #842

Closed
gz65555 opened this issue Mar 6, 2023 · 6 comments
Closed

WriteBinary doesn't keep the order of textures #842

gz65555 opened this issue Mar 6, 2023 · 6 comments
Labels
question wontfix This will not be worked on

Comments

@gz65555
Copy link

gz65555 commented Mar 6, 2023

Describe the bug
I use glTF-Transform to transform a glTF file to glb format, but the order of the textures of glb is not consistent with the original glTF.

To Reproduce
This is an original glTF
helmet.gltf.zip
The field of textures is:

{
  "textures": [
    { "sampler": 0, "source": 0 },
    { "sampler": 0, "source": 1 },
    { "sampler": 0, "source": 2 },
    { "sampler": 0, "source": 3 },
    { "sampler": 0, "source": 4 }
  ]
} 

The code of transform is:

const resources = {};
await Promise.all(
  [...json.buffers, ...json.images].map((item) =>
    fetch(item.uri)
      .then((res) => res.arrayBuffer())
      .then((ab) => (resources[item.uri] = new Uint8Array(ab)))
  )
);

const doc = await io.readJSON({
  json,
  resources
});

const result = await io.writeBinary(doc);

The result I parsed is:
image

And the result file is:
helmet.glb.zip

Expected behavior
The order of the glb texture field should be the same as the source glTF.

Versions:

  • Version: [3.0.4]
  • Environment: [Browser]
@gz65555 gz65555 added the bug Something isn't working label Mar 6, 2023
@donmccurdy
Copy link
Owner

Hi @gz65555 — this is by design. glTF Transform combines the glTF concepts of "images" and "textures" with its internal representation to make editing simpler, and therefore only the order of images, not textures can be preserved. I'd recommend using something other than indices (names, extras, or URI?) to identify textures if you can. If you're building custom extensions, you will need to register those extensions in glTF Transform to preserve references to textures.

I think the specific use case you're mentioning here — converting from glTF to GLB — could also be solved by packing the data without going through the internal glTF Transform representation. Support for that is discussed in #746.

@gz65555
Copy link
Author

gz65555 commented Mar 7, 2023

Thank you for your answer, the material I created myself needs to reference the texture in glTF.

In the texture information, name/extras are not reliable, and the material of glTF itself also uses indices as identity. Also, glTF is designed to separate images and textures.

I think this is a design issue, can I submit a pull request to fix it?

@donmccurdy
Copy link
Owner

I think this is a design issue, can I submit a pull request to fix it?

Sorry, but I don't know of any solution preserving texture indices that I could accept as a PR.

glTF Transform needs to know about references to objects like textures. It converts indices to references when loading, so it can track them. Without knowing where a texture is used, for example, it can't determine the right compression settings when using the KTX2 compression. I understand you're only trying to pack a glTF file into a GLB file here, which is much simpler than the broader goals of this library, but I think the changes you describe are going to create larger problems for the project. For full use of the glTF Transform library it is necessary to provide an implementation if your custom material extension, as shown here: https://gltf-transform.donmccurdy.com/extensions.html#custom-extensions.

I would be open to creating a JSONToBinary( { json, resources } ) method that would do what you need, without unpacking the model into glTF Transforms internal representation. You can find most of the necessary code in:

const header = new Uint32Array([0x46546c67, 2, 12]);
const jsonText = JSON.stringify(json);
const jsonChunkData = BufferUtils.pad(BufferUtils.encodeText(jsonText), 0x20);
const jsonChunkHeader = BufferUtils.toView(new Uint32Array([jsonChunkData.byteLength, 0x4e4f534a]));
const jsonChunk = BufferUtils.concat([jsonChunkHeader, jsonChunkData]);
header[header.length - 1] += jsonChunk.byteLength;
const binBuffer = Object.values(resources)[0];
if (!binBuffer || !binBuffer.byteLength) {
return BufferUtils.concat([BufferUtils.toView(header), jsonChunk]);
}
const binChunkData = BufferUtils.pad(binBuffer, 0x00);
const binChunkHeader = BufferUtils.toView(new Uint32Array([binChunkData.byteLength, 0x004e4942]));
const binChunk = BufferUtils.concat([binChunkHeader, binChunkData]);
header[header.length - 1] += binChunk.byteLength;
return BufferUtils.concat([BufferUtils.toView(header), jsonChunk, binChunk]);

@gz65555
Copy link
Author

gz65555 commented Mar 7, 2023

Yes, adding an images field is too complex.

I use gltf-transform not only to convert glTF to glb, I also use quantize, mesh-opt and other functions. glTF-transform is a very nice tool for dealing with glTF.

I have used a custom extension to customize the material replacement, but it didn't solve the problem. Let me figure out how to get around this problem.

Thanks anyway.

@donmccurdy
Copy link
Owner

A custom extension will keep the material attached to the correct textures, even when the texture indices change. The material will be updated at export with the correct texture index. If you want to check the draft implementation of KHR_materials_anisotropy you can see how that generally works, and how to add your own extension.

@donmccurdy
Copy link
Owner

Merging into #746.

@donmccurdy donmccurdy closed this as not planned Won't fix, can't repro, duplicate, stale Mar 8, 2023
@donmccurdy donmccurdy added question wontfix This will not be worked on and removed bug Something isn't working labels Mar 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

2 participants