Browse Source

GLTF Importer: Build a list of the actual vertices so it works well with shared attribute lists

Florian Born 2 năm trước cách đây
mục cha
commit
dfd70b5c10

+ 1 - 1
code/AssetLib/glTF2/glTF2Asset.h

@@ -565,7 +565,7 @@ struct Accessor : public Object {
     inline size_t GetMaxByteSize();
     inline size_t GetMaxByteSize();
 
 
     template <class T>
     template <class T>
-    void ExtractData(T *&outData);
+    size_t ExtractData(T *&outData, const std::vector<unsigned int> *remappingIndices = nullptr);
 
 
     void WriteData(size_t count, const void *src_buffer, size_t src_stride);
     void WriteData(size_t count, const void *src_buffer, size_t src_stride);
     void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
     void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);

+ 11 - 8
code/AssetLib/glTF2/glTF2Asset.inl

@@ -962,14 +962,15 @@ inline size_t Accessor::GetMaxByteSize() {
 }
 }
 
 
 template <class T>
 template <class T>
-void Accessor::ExtractData(T *&outData) {
+size_t Accessor::ExtractData(T *&outData, const std::vector<unsigned int> *remappingIndices) {
     uint8_t *data = GetPointer();
     uint8_t *data = GetPointer();
     if (!data) {
     if (!data) {
         throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name));
         throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name));
     }
     }
 
 
+    const size_t usedCount = (remappingIndices != nullptr) ? remappingIndices->size() : count;
     const size_t elemSize = GetElementSize();
     const size_t elemSize = GetElementSize();
-    const size_t totalSize = elemSize * count;
+    const size_t totalSize = elemSize * usedCount;
 
 
     const size_t stride = GetStride();
     const size_t stride = GetStride();
 
 
@@ -980,18 +981,20 @@ void Accessor::ExtractData(T *&outData) {
     }
     }
 
 
     const size_t maxSize = GetMaxByteSize();
     const size_t maxSize = GetMaxByteSize();
-    if (count * stride > maxSize) {
-        throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
+    if (usedCount * stride > maxSize) {
+        throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
     }
     }
 
 
-    outData = new T[count];
-    if (stride == elemSize && targetElemSize == elemSize) {
+    outData = new T[usedCount];
+    if (remappingIndices == nullptr && stride == elemSize && targetElemSize == elemSize) {
         memcpy(outData, data, totalSize);
         memcpy(outData, data, totalSize);
     } else {
     } else {
-        for (size_t i = 0; i < count; ++i) {
-            memcpy(outData + i, data + i * stride, elemSize);
+        for (size_t i = 0; i < usedCount; ++i) {
+            size_t srcIdx = remappingIndices != nullptr ? static_cast<size_t>((*remappingIndices)[i]) : i;
+            memcpy(outData + i, data + srcIdx * stride, elemSize);
         }
         }
     }
     }
+    return usedCount;
 }
 }
 
 
 inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) {
 inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) {

+ 66 - 34
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -453,6 +453,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
     unsigned int k = 0;
     unsigned int k = 0;
     meshOffsets.clear();
     meshOffsets.clear();
 
 
+
+    const unsigned int unusedIndex = ~0;
+    std::vector<unsigned int> usedVertexIndices;
+    std::vector<unsigned int> reverseMappingIndices;
+    std::vector<unsigned int> indexBuffer;
+
     for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
     for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
         Mesh &mesh = r.meshes[m];
         Mesh &mesh = r.meshes[m];
 
 
@@ -462,6 +468,36 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
         for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
         for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
             Mesh::Primitive &prim = mesh.primitives[p];
             Mesh::Primitive &prim = mesh.primitives[p];
 
 
+            // extract used vertices:
+            bool useIndexBuffer = prim.indices;
+            std::vector<unsigned int>* usedVertexIndicesPtr = nullptr;
+            if (useIndexBuffer) {
+                size_t count = prim.indices->count;
+                indexBuffer.resize(count);
+                usedVertexIndices.clear();
+                reverseMappingIndices.clear();
+                usedVertexIndices.reserve(count / 3); // this is a very rough heuristic to reduce re-allocations
+                usedVertexIndicesPtr = &usedVertexIndices;
+                Accessor::Indexer data = prim.indices->GetIndexer();
+                if (!data.IsValid()) {
+                    throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
+                }
+
+                // Build the vertex remapping table and the modified index buffer (used later instead of the original one)
+                // In case no index buffer is used, the original vertex arrays are being used so no remapping is required in the first place.
+                for (unsigned int i = 0; i < count; ++i) {
+                    unsigned int index = data.GetUInt(i);
+                    if (index >= reverseMappingIndices.size()) {
+                        reverseMappingIndices.resize(index + 1, unusedIndex);
+                    }
+                    if (reverseMappingIndices[index] == unusedIndex) {
+                        reverseMappingIndices[index] = static_cast<unsigned int>(usedVertexIndices.size());
+                        usedVertexIndices.push_back(index);
+                    }
+                    indexBuffer[i] = reverseMappingIndices[index];
+                }
+            }
+
             aiMesh *aim = new aiMesh();
             aiMesh *aim = new aiMesh();
             meshes.push_back(std::unique_ptr<aiMesh>(aim));
             meshes.push_back(std::unique_ptr<aiMesh>(aim));
 
 
@@ -493,26 +529,27 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 
 
             Mesh::Primitive::Attributes &attr = prim.attributes;
             Mesh::Primitive::Attributes &attr = prim.attributes;
 
 
+            size_t numAllVertices = 0;
             if (!attr.position.empty() && attr.position[0]) {
             if (!attr.position.empty() && attr.position[0]) {
-                aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->count);
-                attr.position[0]->ExtractData(aim->mVertices);
+                    numAllVertices = attr.position[0]->count;
+                    aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->ExtractData(aim->mVertices, usedVertexIndicesPtr));
             }
             }
 
 
             if (!attr.normal.empty() && attr.normal[0]) {
             if (!attr.normal.empty() && attr.normal[0]) {
-                if (attr.normal[0]->count != aim->mNumVertices) {
+                    if (attr.normal[0]->count != numAllVertices) {
                     DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored.");
                     DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored.");
                 } else {
                 } else {
-                    attr.normal[0]->ExtractData(aim->mNormals);
+                    attr.normal[0]->ExtractData(aim->mNormals, usedVertexIndicesPtr);
 
 
                     // only extract tangents if normals are present
                     // only extract tangents if normals are present
                     if (!attr.tangent.empty() && attr.tangent[0]) {
                     if (!attr.tangent.empty() && attr.tangent[0]) {
-                        if (attr.tangent[0]->count != aim->mNumVertices) {
+                        if (attr.tangent[0]->count != numAllVertices) {
                             DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored.");
                             DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored.");
                         } else {
                         } else {
                             // generate bitangents from normals and tangents according to spec
                             // generate bitangents from normals and tangents according to spec
                             Tangent *tangents = nullptr;
                             Tangent *tangents = nullptr;
 
 
-                            attr.tangent[0]->ExtractData(tangents);
+                            attr.tangent[0]->ExtractData(tangents, usedVertexIndicesPtr);
 
 
                             aim->mTangents = new aiVector3D[aim->mNumVertices];
                             aim->mTangents = new aiVector3D[aim->mNumVertices];
                             aim->mBitangents = new aiVector3D[aim->mNumVertices];
                             aim->mBitangents = new aiVector3D[aim->mNumVertices];
@@ -529,7 +566,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
             }
             }
 
 
             for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
             for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
-                if (attr.color[c]->count != aim->mNumVertices) {
+                if (attr.color[c]->count != numAllVertices) {
                     DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
                     DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
                             "\" does not match the vertex count");
                             "\" does not match the vertex count");
                     continue;
                     continue;
@@ -537,7 +574,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 
 
                 auto componentType = attr.color[c]->componentType;
                 auto componentType = attr.color[c]->componentType;
                 if (componentType == glTF2::ComponentType_FLOAT) {
                 if (componentType == glTF2::ComponentType_FLOAT) {
-                    attr.color[c]->ExtractData(aim->mColors[c]);
+                    attr.color[c]->ExtractData(aim->mColors[c], usedVertexIndicesPtr);
                 } else {
                 } else {
                     if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
                     if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
                         aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
                         aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
@@ -552,13 +589,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                     continue;
                     continue;
                 }
                 }
 
 
-                if (attr.texcoord[tc]->count != aim->mNumVertices) {
+                if (attr.texcoord[tc]->count != numAllVertices) {
                     DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
                     DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
                             "\" does not match the vertex count");
                             "\" does not match the vertex count");
                     continue;
                     continue;
                 }
                 }
 
 
-                attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
+                attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc], usedVertexIndicesPtr);
                 aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
                 aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
 
 
                 aiVector3D *values = aim->mTextureCoords[tc];
                 aiVector3D *values = aim->mTextureCoords[tc];
@@ -583,11 +620,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                     Mesh::Primitive::Target &target = targets[i];
                     Mesh::Primitive::Target &target = targets[i];
 
 
                     if (needPositions) {
                     if (needPositions) {
-                        if (target.position[0]->count != aim->mNumVertices) {
+                        if (target.position[0]->count != numAllVertices) {
                             ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
                             ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
                         } else {
                         } else {
                             aiVector3D *positionDiff = nullptr;
                             aiVector3D *positionDiff = nullptr;
-                            target.position[0]->ExtractData(positionDiff);
+                            target.position[0]->ExtractData(positionDiff, usedVertexIndicesPtr);
                             for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
                             for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
                                 aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
                                 aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
                             }
                             }
@@ -595,11 +632,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                         }
                         }
                     }
                     }
                     if (needNormals) {
                     if (needNormals) {
-                        if (target.normal[0]->count != aim->mNumVertices) {
+                        if (target.normal[0]->count != numAllVertices) {
                             ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
                             ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
                         } else {
                         } else {
                             aiVector3D *normalDiff = nullptr;
                             aiVector3D *normalDiff = nullptr;
-                            target.normal[0]->ExtractData(normalDiff);
+                            target.normal[0]->ExtractData(normalDiff, usedVertexIndicesPtr);
                             for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
                             for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
                                 aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
                                 aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
                             }
                             }
@@ -610,14 +647,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                         if (!aiAnimMesh.HasNormals()) {
                         if (!aiAnimMesh.HasNormals()) {
                             // prevent nullptr access to aiAnimMesh.mNormals below when no normals are available
                             // prevent nullptr access to aiAnimMesh.mNormals below when no normals are available
                             ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals.");
                             ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals.");
-                        } else if (target.tangent[0]->count != aim->mNumVertices) {
+                        } else if (target.tangent[0]->count != numAllVertices) {
                             ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
                             ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
                         } else {
                         } else {
                             Tangent *tangent = nullptr;
                             Tangent *tangent = nullptr;
-                            attr.tangent[0]->ExtractData(tangent);
+                            attr.tangent[0]->ExtractData(tangent, usedVertexIndicesPtr);
 
 
                             aiVector3D *tangentDiff = nullptr;
                             aiVector3D *tangentDiff = nullptr;
-                            target.tangent[0]->ExtractData(tangentDiff);
+                            target.tangent[0]->ExtractData(tangentDiff, usedVertexIndicesPtr);
 
 
                             for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
                             for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
                                 tangent[vertexId].xyz += tangentDiff[vertexId];
                                 tangent[vertexId].xyz += tangentDiff[vertexId];
@@ -641,20 +678,15 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
             aiFace *facePtr = nullptr;
             aiFace *facePtr = nullptr;
             size_t nFaces = 0;
             size_t nFaces = 0;
 
 
-            if (prim.indices) {
-                size_t count = prim.indices->count;
-
-                Accessor::Indexer data = prim.indices->GetIndexer();
-                if (!data.IsValid()) {
-                    throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
-                }
+            if (useIndexBuffer) {
+                size_t count = indexBuffer.size();
 
 
                 switch (prim.mode) {
                 switch (prim.mode) {
                 case PrimitiveMode_POINTS: {
                 case PrimitiveMode_POINTS: {
                     nFaces = count;
                     nFaces = count;
                     facePtr = faces = new aiFace[nFaces];
                     facePtr = faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; ++i) {
                     for (unsigned int i = 0; i < count; ++i) {
-                        SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
+                        SetFaceAndAdvance1(facePtr, aim->mNumVertices, indexBuffer[i]);
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -667,7 +699,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                     }
                     }
                     facePtr = faces = new aiFace[nFaces];
                     facePtr = faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; i += 2) {
                     for (unsigned int i = 0; i < count; i += 2) {
-                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1]);
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -676,12 +708,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 case PrimitiveMode_LINE_STRIP: {
                 case PrimitiveMode_LINE_STRIP: {
                     nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
                     nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
                     facePtr = faces = new aiFace[nFaces];
                     facePtr = faces = new aiFace[nFaces];
-                    SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
+                    SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1]);
                     for (unsigned int i = 2; i < count; ++i) {
                     for (unsigned int i = 2; i < count; ++i) {
-                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i - 1], indexBuffer[i]);
                     }
                     }
                     if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
                     if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
-                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[static_cast<int>(count) - 1], faces[0].mIndices[0]);
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -694,7 +726,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                     }
                     }
                     facePtr = faces = new aiFace[nFaces];
                     facePtr = faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; i += 3) {
                     for (unsigned int i = 0; i < count; i += 3) {
-                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -705,10 +737,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                         // The ordering is to ensure that the triangles are all drawn with the same orientation
                         // The ordering is to ensure that the triangles are all drawn with the same orientation
                         if ((i + 1) % 2 == 0) {
                         if ((i + 1) % 2 == 0) {
                             // For even n, vertices n + 1, n, and n + 2 define triangle n
                             // For even n, vertices n + 1, n, and n + 2 define triangle n
-                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
+                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i + 1], indexBuffer[i], indexBuffer[i + 2]);
                         } else {
                         } else {
                             // For odd n, vertices n, n+1, and n+2 define triangle n
                             // For odd n, vertices n, n+1, and n+2 define triangle n
-                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
                         }
                         }
                     }
                     }
                     break;
                     break;
@@ -716,9 +748,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 case PrimitiveMode_TRIANGLE_FAN:
                 case PrimitiveMode_TRIANGLE_FAN:
                     nFaces = count - 2;
                     nFaces = count - 2;
                     facePtr = faces = new aiFace[nFaces];
                     facePtr = faces = new aiFace[nFaces];
-                    SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+                    SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1], indexBuffer[2]);
                     for (unsigned int i = 1; i < nFaces; ++i) {
                     for (unsigned int i = 1; i < nFaces; ++i) {
-                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[i + 1], indexBuffer[i + 2]);
                     }
                     }
                     break;
                     break;
                 }
                 }