GLB file fails to open
Describe the bug The following file is failing:
I created the file so it's F3D licensed.
To Reproduce Steps to reproduce the behavior:
- Open the file using
f3d --no-config world.glb - An error is logged
Expected behavior The file should open
System Information:
- OS: All
- GPU and GPU driver: irrelevant
F3D Information 3.3 and master
Additional context The following patch seems to fix the problem, but more investigation is needed to find if it's correct with respect to the specifications.
Looks like the textures are wrong:
Loading files:
/home/glow/data/tmp/world.glb
ERROR: In vtkPNGReader.cxx, line 185
vtkPNGReader (0x557ed2422970): Stream is too short, could not read the header
ERROR: In vtkPNGReader.cxx, line 301
vtkPNGReader (0x557ed2422970): Invalid MemoryBuffer header: not a PNG file
ERROR: In vtkExecutive.cxx, line 729
vtkCompositeDataPipeline (0x557ed2674d80): Algorithm vtkPNGReader (0x557ed2422970) returned failure for request: vtkInformation (0x557ed2677230)
Debug: Off
Modified Time: 15110
Reference Count: 1
Registered Events: (none)
Request: REQUEST_INFORMATION
ALGORITHM_AFTER_FORWARD: 1
FORWARD_DIRECTION: 0
ERROR: In vtkGLTFImporter.cxx, line 565
vtkF3DGLTFDracoImporter (0x557ed2424670): Error loading model data
Some of these files could not be loaded: failed to load scene
/home/glow/data/tmp/world.glb
3Dviewer is able to read it but no texture appears. Are you sure this file is valid ?
Are you sure this file is valid ?
It's a sub-optimal GLB file because it has both a binary buffer (for the geometry) and a data-URI buffer (for the texture) but I don't think it's invalid. The validator from https://gltf-viewer.donmccurdy.com/ shows the data-URI buffer as a warning.
From a quick glance at @Meakk's patch it looks like VTK assumes GLB files have only 1 single buffer which is populated from the binary chunk?
Indeed, the validators online report it's valid. It's not clear to me what's VTK doing. When the file is a binary, the main buffer is saved first, then an empty buffer is saved, followed by the remaining buffers.
The problem is the buffer views are pointing to buffer indices with a wrong offset by 1 because of this empty buffer. Skipping the empty buffer fixes the offset problem.
I don't have much more information than that.
Relevant spec chapter? https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-buffer
When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
When the file is a binary, the main buffer is saved first, then an empty buffer is saved, followed by the remaining buffers. The problem is the buffer views are pointing to buffer indices with a wrong offset by 1 because of this empty buffer. Skipping the empty buffer fixes the offset problem. I don't have much more information than that.
When the glb file has a BIN chunk the JSON chunk should indeed have an "empty" buffer (with the corresponding byte length but no URI) for the binary data to go into. Sounds like VTK adds this empty buffer even though it has already been done. (that would mean there's always an extra empty buffer in the loaded data, but that doesn't matter when there's not extra buffers afterwards)
if that's what's going on an alternative patch could be:
diff --git a/IO/Geometry/vtkGLTFDocumentLoaderInternals.cxx b/IO/Geometry/vtkGLTFDocumentLoaderInternals.cxx
index ded3b1f2d7..3c0815e2d1 100644
--- a/IO/Geometry/vtkGLTFDocumentLoaderInternals.cxx
+++ b/IO/Geometry/vtkGLTFDocumentLoaderInternals.cxx
@@ -68,6 +68,7 @@ bool vtkGLTFDocumentLoaderInternals::LoadBuffers(bool firstBufferIsGLB)
nlohmann::json bufferRoot =
nlohmann::json::parse(this->Self->GetInternalModel()->BufferMetaData);
// Load buffers from disk
+ int bufferIndex = 0;
for (const auto& glTFBuffer : bufferRoot)
{
std::vector<char> buffer;
@@ -79,20 +80,27 @@ bool vtkGLTFDocumentLoaderInternals::LoadBuffers(bool firstBufferIsGLB)
"Invalid first buffer value for glb file. No buffer was loaded from the file.");
return false;
}
- if (firstBufferIsGLB && this->Self->GetInternalModel()->Buffers.size() == 1 &&
- !buffer.empty())
+ if (firstBufferIsGLB && bufferIndex == 0 && !buffer.empty())
{
vtkErrorWithObjectMacro(
this->Self, "Invalid first buffer value for glb file. buffer.uri should be undefined");
return false;
}
- this->Self->GetInternalModel()->Buffers.emplace_back(std::move(buffer));
+ if (firstBufferIsGLB && bufferIndex == 0 && buffer.empty())
+ {
+ /* buffer will alraedy have been loaded from the GLB BIN chunck */
+ }
+ else
+ {
+ this->Self->GetInternalModel()->Buffers.emplace_back(std::move(buffer));
+ }
}
else
{
vtkErrorWithObjectMacro(this->Self, "Could not load Buffer from JSON.");
return false;
}
+ bufferIndex++;
}
}
catch (nlohmann::json::parse_error& e)
(quick and dirty for minimal diff, the special case is only for the first buffer so it could be taken out of the loop)
When the glb file has a BIN chunk the JSON chunk should indeed have an "empty" buffer (with the corresponding byte length but no URI) for the binary data to go into.
Have you read that somewhere in the spec?
Turns out it doesn't mention the length but it's here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#glb-stored-buffer
A buffer with data provided by the GLB-stored
BINchunk, MUST be the first element ofbuffersarray and it MUST have itsbuffer.uriproperty undefined. When such a buffer exists, a BIN chunk MUST be present.
Ok thanks I missed that part. It makes sense now. I'll submit a patch to VTK.
https://gitlab.kitware.com/vtk/vtk/-/merge_requests/12678
Will need a VTK bump, ill take care of it
fixed now