-
Notifications
You must be signed in to change notification settings - Fork 62
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
Improved binary read and write support #7
Conversation
…er packages, so tests will show up on IDEs that don't have the NUnit extension installed.
|
||
var buffer = jsonChunk.Buffers[0]; | ||
Assert.IsTrue(buffer.ByteLength <= binChunk.Length); | ||
// should we check padding as with jsonChunk? some reference files fail! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See KhronosGroup/glTF#1026. We should clarify spec on this issue.
Added a test that demonstrates how to load the buffers and the images for any gltf.
I took the liberty to add two additional helper methods to help load buffers and images, regardless of how they're stored. These methods greatly simplify loading all the data of a gltf as shown in the unit test.
Both methods work the same way, you pass the model, the file path from where the model was originally loaded, and the index of the buffer or image. Internally, they will handle embedded or buffered content, so how the content has been stored is transparent to the end user. |
glTFLoader/Interface.cs
Outdated
|
||
if (buffer.Uri == null) return LoadBinaryBuffer(gltfFilePath); | ||
|
||
if (buffer.Uri.StartsWith("data:application/octet-stream;base64")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to replace this with EMBEDDEDOCTETSTREAM
?
glTFLoader/Interface.cs
Outdated
{ | ||
string content = null; | ||
|
||
if (image.Uri.StartsWith(EMBEDDEDPNG)) content = image.Uri.Substring(EMBEDDEDPNG.Length + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
glTF only support PNG and JPG. None of the other formats are supported, so you don't need the other mime types.
glTFLoader/Interface.cs
Outdated
@@ -17,6 +17,13 @@ public static class Interface | |||
const uint JSON = 0x4E4F534A; | |||
const uint BIN = 0x004E4942; | |||
|
|||
const string EMBEDDEDOCTETSTREAM = "data:application/octet-stream;base64"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend adding the trailing comma to this constant so that you are actually checking for the comma instead of ignoring the character using + 1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okey, I'll do that change.
Btw, detecting embedded images is quite tricky and I'm aware there's room for improvement. For example, I'm sure some people will want to use hardware specific images, like DDS or ASTC , I don't know if there's specific mime types for these formats... in which case, is it possible to find images stored as data:application/octet-stream;base64 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There have been lots of discussions around this already, but the short answer, at least for core glTF 2.0, is that hardware specific textures are not supported. Thus, you don't have to worry about them for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okey then.
About the Interface API, there's a few more methods I would like to add, but I don't know if you think it's too much already...
Usually, when I work with file formats, I try to make everything to work with Streams, certainly, it gets complicated when you have multiple files around. The use case is when you try to load a file from within a ZIP or any other package container (which is quite common to use in videogames)
The idea I had in mind was to replicate the previous methods used to load buffers and images, but using lambdas, so the end user could intercept/inject file accesses to whatever package container is using.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do that in a separate change.
glTFLoader/Interface.cs
Outdated
throw new NotImplementedException($"The first chunk must be format 'JSON': {chunkFormat}"); | ||
if ((chunkLength & 3) != 0) | ||
{ | ||
throw new NotImplementedException($"The second chunk must be padded to 4 bytes: {chunkLength}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NotImplementedException seems weird for this. Perhaps use the InvalidDataException you are using elsewhere?
glTFLoader/Interface.cs
Outdated
uint chunkFormat = binaryReader.ReadUInt32(); | ||
if (chunkFormat != BIN) | ||
{ | ||
throw new NotImplementedException($"The second chunk must be format 'BIN': {chunkFormat}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
glTFLoader/Interface.cs
Outdated
fullLength += 8 + buffer.Length + binPadding; | ||
|
||
binaryWriter.Write(GLTF); | ||
binaryWriter.Write((UInt32)2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This might be good in a constant also
glTFLoader/Interface.cs
Outdated
} | ||
|
||
return Encoding.UTF8.GetString(binaryReader.ReadBytes((int)chunkLength)); | ||
uint chunkFormat = binaryReader.ReadUInt32(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This technically is incorrect. The spec does not explicitly say that the second chunk must be the BIN chunk. It says this:
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#chunks
Client implementations must ignore chunks with unknown types.
This is to support extensions that add additional chunks to the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okey, going to change all that 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also need to consider when the BIN chunk is not present.
} | ||
} | ||
|
||
public static void SaveBinaryModel(this Gltf model, byte[] buffer, BinaryWriter binaryWriter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider allowing buffer to be null which will then not write out the BIN chunk. BIN chunk is not always required for all glTF files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, looks good.
Added support for writing glb files without binary chunk Added image and buffer loader funcions that use lambdas, to support loading gltf/glb from package files.
Okey, I think all the changes are in place, I might think to add a few more unit tests, but so far, in terms of loading/saving, I've covered most offline scenarios. |
|
||
if (buffer.Uri == null) return externalReferenceSolver(null); | ||
|
||
if (buffer.Uri.StartsWith(EMBEDDEDOCTETSTREAM)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While spec says that binary buffers use application/octet-stream
, some tools may start using application/gltf-buffer
after IANA approval. See KhronosGroup/glTF#944.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can fix this once the spec is updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's something easy to add
glTFLoader/Interface.cs
Outdated
{ | ||
string content = null; | ||
|
||
if (image.Uri.StartsWith(EMBEDDEDPNG)) content = image.Uri.Substring(EMBEDDEDPNG.Length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You still have more types than just PNG and JPG. Remove BMP, GIF, and TIFF here and the constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean, only PNG and JPG are supported?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well... that surprised me!, I guess it's a matter of time they include more formats
I'll have a look at documenting some of my methods in a few days, time to bed! |
Good, if I have enough time I might do some tuff here too. Looks funny here 😃 |
I've just noticed there's a bug I introduced in the unit tests... I've just created a new fork... I'll fix that one and also add some documentation... pull request soon |
Something that was missing from the current interface was the ability to read the binary buffer chunk from a glb file, and the ability to write glb files.
Notice that with the current API, we need to open the file two times, because the json chunk and the binary chunk are read separately (as if they were separate files)
I added these functions, along with a test that reads and writes binary files.
Also, I added an additional check for the byte padding for the json chunk.
I noticed some glb files don't have any padding on the binary buffer chunk. I understand it's because it's the last chunk... but, if in the future more chunk types are added, this might be an issue.
Finally, I added a unit test for the new methods.