Skip to content

Serious glTF importer bugs #42

Closed
Closed
@mosra

Description

@mosra

@Squareys @NussknackerXXL

I'm quite disappointed by state of the glTF importer code, so the tone of the following might not be all 💮 and 🌞. Sorry in advance.

As reported by https://twitter.com/steeve/status/997138966754230272, the glTF importer plugin at its current state doesn't seem to work at all for any model in https://github.com/KhronosGroup/glTF-Sample-Models. I wonder what it was tested on?

I managed to fix it to display at least the mesh data correctly for Box and and AnimatedBox (patch below, not commited anywhere as it needs corresponding tests), but it still fails really bad on all other models. For the Cesium Milk Truck only the wheels are displayed and with weird texture coordinates, for example. Here's a patch that fixes at least something:

diff --git a/src/MagnumPlugins/TinyGltfImporter/TinyGltfImporter.cpp b/src/MagnumPlugins/TinyGltfImporter/TinyGltfImporter.cpp
index 690ddc0..cf8f7b2 100644
--- a/src/MagnumPlugins/TinyGltfImporter/TinyGltfImporter.cpp
+++ b/src/MagnumPlugins/TinyGltfImporter/TinyGltfImporter.cpp
@@ -317,9 +317,9 @@ Containers::Optional<MeshData3D> TinyGltfImporter::doMesh3D(const UnsignedInt id
                 return Containers::NullOpt;
             }
 
-            const size_t numPositions = bufferView.byteLength/sizeof(Vector3);
+            const size_t numPositions = accessor.count;
             positions.reserve(numPositions);
-            std::copy_n(reinterpret_cast<const Vector3*>(buffer.data.data() + bufferView.byteOffset), numPositions, std::back_inserter(positions));
+            std::copy_n(reinterpret_cast<const Vector3*>(buffer.data.data() + bufferView.byteOffset + accessor.byteOffset), numPositions, std::back_inserter(positions));
 
         } else if(attribute.first == "NORMAL") {
             if(accessor.type != TINYGLTF_TYPE_VEC3) {
@@ -327,9 +327,9 @@ Containers::Optional<MeshData3D> TinyGltfImporter::doMesh3D(const UnsignedInt id
                 return Containers::NullOpt;
             }
 
-            const size_t numNormals = bufferView.byteLength/sizeof(Vector3);
+            const size_t numNormals = accessor.count;
             normals.reserve(numNormals);
-            std::copy_n(reinterpret_cast<const Vector3*>(buffer.data.data() + bufferView.byteOffset), numNormals, std::back_inserter(normals));
+            std::copy_n(reinterpret_cast<const Vector3*>(buffer.data.data() + bufferView.byteOffset + accessor.byteOffset), numNormals, std::back_inserter(normals));
 
         /* Texture coordinate attribute ends with _0, _1 ... */
         } else if(Utility::String::beginsWith(attribute.first, "TEXCOORD")) {
@@ -341,9 +341,9 @@ Containers::Optional<MeshData3D> TinyGltfImporter::doMesh3D(const UnsignedInt id
             textureLayers.emplace_back();
             std::vector<Vector2>& textureCoordinates = textureLayers.back();
 
-            const size_t numTextureCoordinates = bufferView.byteLength/sizeof(Vector2);
+            const size_t numTextureCoordinates = accessor.count;
             textureCoordinates.reserve(numTextureCoordinates);
-            std::copy_n(reinterpret_cast<const Vector2*>(buffer.data.data() + bufferView.byteOffset), numTextureCoordinates, std::back_inserter(textureCoordinates));
+            std::copy_n(reinterpret_cast<const Vector2*>(buffer.data.data() + bufferView.byteOffset + accessor.byteOffset), numTextureCoordinates, std::back_inserter(textureCoordinates));
 
         /* Color attribute ends with _0, _1 ... */
         } else if(Utility::String::beginsWith(attribute.first, "COLOR")) {
@@ -383,13 +383,13 @@ Containers::Optional<MeshData3D> TinyGltfImporter::doMesh3D(const UnsignedInt id
     }
 
     std::vector<UnsignedInt> indices;
-    const UnsignedByte* start = idxBuffer.data.data() + idxBufferView.byteOffset;
+    const UnsignedByte* start = idxBuffer.data.data() + idxBufferView.byteOffset + idxAccessor.byteOffset;
     if(idxAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
-        std::copy_n(start, idxBufferView.byteLength, std::back_inserter(indices));
+        std::copy_n(start, idxAccessor.count, std::back_inserter(indices));
     } else if(idxAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
-        std::copy_n(reinterpret_cast<const UnsignedShort*>(start), idxBufferView.byteLength/sizeof(UnsignedShort), std::back_inserter(indices));
+        std::copy_n(reinterpret_cast<const UnsignedShort*>(start), idxAccessor.count, std::back_inserter(indices));
     } else if(idxAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
-        std::copy_n(reinterpret_cast<const UnsignedInt*>(start), idxBufferView.byteLength/sizeof(UnsignedInt), std::back_inserter(indices));
+        std::copy_n(reinterpret_cast<const UnsignedInt*>(start), idxAccessor.count, std::back_inserter(indices));
     } else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
 
     return MeshData3D(meshPrimitive, std::move(indices), {std::move(positions)}, {std::move(normals)}, textureLayers, colorLayers, &mesh);

So, what's needed:

  • look at this tutorial and understand how buffers and views and accessors work. The code had it totally wrong and the test data did not cover any of that at all.
  • As far as I know, the official glTF repositories provide test data for all the corner cases. Use them, please.
  • Only the 8-bit indices are tested, never the 16-bit or 32-bit indices (and they have the same problem re views and accessors). These need to be fixed and tested too, the patch does that a bit, but probably not fully.
  • Assimp is able to import at least some material properties (color and roughness from PBR materials), so maybe a similar thing could be done here (until we have PBR import). Otherwise we get more reports about everything being plain gray.
  • In Tinygltf importer fixes #41 I asked for testing and fixing proper handling of file opening failure. It was not done. Opening a nonexistent file still blows up somewhere later in the code. (I admit I should have triple checked before merging, sorry.)

This is quite important, as the latest release was marketed as having glTF support. This is far from that. I hope you have time for this, otherwise I need to drop whatever I am doing now and take over this plugin, as a lot of things will rely on it in the near future.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions