f3d icon indicating copy to clipboard operation
f3d copied to clipboard

GLB file fails to open

Open Meakk opened this issue 4 weeks ago • 8 comments

Describe the bug The following file is failing:

world.zip

I created the file so it's F3D licensed.

To Reproduce Steps to reproduce the behavior:

  1. Open the file using f3d --no-config world.glb
  2. 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.

vtk.patch

Meakk avatar Dec 02 '25 19:12 Meakk

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 ?

mwestphal avatar Dec 03 '25 06:12 mwestphal

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?

snoyer avatar Dec 03 '25 07:12 snoyer

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.

Meakk avatar Dec 03 '25 08:12 Meakk

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)

snoyer avatar Dec 03 '25 09:12 snoyer

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?

Meakk avatar Dec 03 '25 09:12 Meakk

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 BIN chunk, MUST be the first element of buffers array and it MUST have its buffer.uri property undefined. When such a buffer exists, a BIN chunk MUST be present.

snoyer avatar Dec 03 '25 09:12 snoyer

Ok thanks I missed that part. It makes sense now. I'll submit a patch to VTK.

Meakk avatar Dec 03 '25 10:12 Meakk

https://gitlab.kitware.com/vtk/vtk/-/merge_requests/12678

Meakk avatar Dec 03 '25 13:12 Meakk

Will need a VTK bump, ill take care of it

mwestphal avatar Dec 14 '25 08:12 mwestphal

fixed now

mwestphal avatar Dec 18 '25 06:12 mwestphal