فهرست منبع

Merge pull request #5090 from FlorianBorn71/FixSkinnedWeightsAfterVertexRemappingOptimization

Skinning weights in gltf were broken by PR#5003 (vertex remapping)
Kim Kulling 2 سال پیش
والد
کامیت
feb0303fd0
2فایلهای تغییر یافته به همراه43 افزوده شده و 34 حذف شده
  1. 39 33
      code/AssetLib/glTF2/glTF2Importer.cpp
  2. 4 1
      code/AssetLib/glTF2/glTF2Importer.h

+ 39 - 33
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -100,8 +100,6 @@ glTF2Importer::glTF2Importer() :
     // empty
 }
 
-glTF2Importer::~glTF2Importer() = default;
-
 const aiImporterDesc *glTF2Importer::GetInfo() const {
     return &desc;
 }
@@ -443,10 +441,10 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
 #endif // ASSIMP_BUILD_DEBUG
 
 template <typename T>
-aiColor4D *GetVertexColorsForType(Ref<Accessor> input) {
+aiColor4D *GetVertexColorsForType(Ref<Accessor> input, std::vector<unsigned int> *vertexRemappingTable) {
     constexpr float max = std::numeric_limits<T>::max();
     aiColor4t<T> *colors;
-    input->ExtractData(colors);
+    input->ExtractData(colors, vertexRemappingTable);
     auto output = new aiColor4D[input->count];
     for (size_t i = 0; i < input->count; i++) {
         output[i] = aiColor4D(
@@ -461,20 +459,26 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
     ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes");
     std::vector<std::unique_ptr<aiMesh>> meshes;
 
-    unsigned int k = 0;
     meshOffsets.clear();
+    meshOffsets.reserve(r.meshes.Size() + 1);
+    mVertexRemappingTables.clear();
 
+    // Count the number of aiMeshes
+    unsigned int num_aiMeshes = 0;
+    for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
+        meshOffsets.push_back(num_aiMeshes);
+        num_aiMeshes += unsigned(r.meshes[m].primitives.size());
+    }
+    meshOffsets.push_back(num_aiMeshes); // add a last element so we can always do meshOffsets[n+1] - meshOffsets[n]
 
-    std::vector<unsigned int> usedVertexIndices;
     std::vector<unsigned int> reverseMappingIndices;
     std::vector<unsigned int> indexBuffer;
+    meshes.reserve(num_aiMeshes);
+    mVertexRemappingTables.resize(num_aiMeshes);
 
     for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
         Mesh &mesh = r.meshes[m];
 
-        meshOffsets.push_back(k);
-        k += unsigned(mesh.primitives.size());
-
         for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
             Mesh::Primitive &prim = mesh.primitives[p];
 
@@ -488,14 +492,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 
             // Extract used vertices:
             bool useIndexBuffer = prim.indices;
-            std::vector<unsigned int>* vertexRemappingTable = nullptr;
+            std::vector<unsigned int> *vertexRemappingTable = 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
-                vertexRemappingTable = &usedVertexIndices;
+                vertexRemappingTable = &mVertexRemappingTables[meshes.size()];
+                vertexRemappingTable->reserve(count / 3); // this is a very rough heuristic to reduce re-allocations
                 Accessor::Indexer data = prim.indices->GetIndexer();
                 if (!data.IsValid()) {
                     throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
@@ -515,8 +519,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                         reverseMappingIndices.resize(index + 1, unusedIndex);
                     }
                     if (reverseMappingIndices[index] == unusedIndex) {
-                        reverseMappingIndices[index] = static_cast<unsigned int>(usedVertexIndices.size());
-                        usedVertexIndices.push_back(index);
+                        reverseMappingIndices[index] = static_cast<unsigned int>(vertexRemappingTable->size());
+                        vertexRemappingTable->push_back(index);
                     }
                     indexBuffer[i] = reverseMappingIndices[index];
                 }
@@ -597,9 +601,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                     attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable);
                 } else {
                     if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
-                        aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
+                        aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c], vertexRemappingTable);
                     } else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
-                        aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]);
+                        aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c], vertexRemappingTable);
                     }
                 }
             }
@@ -875,8 +879,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
         }
     }
 
-    meshOffsets.push_back(k);
-
     CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
 }
 
@@ -1009,7 +1011,8 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
     }
 }
 
-static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) {
+static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map, std::vector<unsigned int>* vertexRemappingTablePtr) {
+
     Mesh::Primitive::Attributes &attr = primitive.attributes;
     if (attr.weight.empty() || attr.joint.empty()) {
         return;
@@ -1018,14 +1021,14 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
         return;
     }
 
-    size_t num_vertices = attr.weight[0]->count;
+    size_t num_vertices = 0;
 
     struct Weights {
         float values[4];
     };
     Weights **weights = new Weights*[attr.weight.size()];
     for (size_t w = 0; w < attr.weight.size(); ++w) {
-        attr.weight[w]->ExtractData(weights[w]);
+        num_vertices = attr.weight[w]->ExtractData(weights[w], vertexRemappingTablePtr);
     }
 
     struct Indices8 {
@@ -1039,12 +1042,12 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
     if (attr.joint[0]->GetElementSize() == 4) {
         indices8 = new Indices8*[attr.joint.size()];
         for (size_t j = 0; j < attr.joint.size(); ++j) {
-            attr.joint[j]->ExtractData(indices8[j]);
+            attr.joint[j]->ExtractData(indices8[j], vertexRemappingTablePtr);
         }
     } else {
         indices16 = new Indices16 *[attr.joint.size()];
         for (size_t j = 0; j < attr.joint.size(); ++j) {
-            attr.joint[j]->ExtractData(indices16[j]);
+            attr.joint[j]->ExtractData(indices16[j], vertexRemappingTablePtr);
         }
     }
     //
@@ -1109,7 +1112,7 @@ void ParseExtras(aiMetadata* metadata, const Extras& extras) {
     }
 }
 
-aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
+aiNode *glTF2Importer::ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr) {
     Node &node = *ptr;
 
     aiNode *ainode = new aiNode(GetNodeName(node));
@@ -1121,7 +1124,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
             std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr);
 
             for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
-                aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]);
+                aiNode *child = ImportNode(r, node.children[i]);
                 child->mParent = ainode;
                 ainode->mChildren[i] = child;
             }
@@ -1154,11 +1157,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
 
             if (node.skin) {
                 for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
-                    aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo];
+                    unsigned int aiMeshIdx = meshOffsets[mesh_idx] + primitiveNo;
+                    aiMesh *mesh = mScene->mMeshes[aiMeshIdx];
                     unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
+                    std::vector<unsigned int> *vertexRemappingTablePtr = mVertexRemappingTables[aiMeshIdx].empty() ? nullptr : &mVertexRemappingTables[aiMeshIdx];
 
                     std::vector<std::vector<aiVertexWeight>> weighting(numBones);
-                    BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
+                    BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting, vertexRemappingTablePtr);
 
                     mesh->mNumBones = static_cast<unsigned int>(numBones);
                     mesh->mBones = new aiBone *[mesh->mNumBones];
@@ -1175,7 +1180,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
                     // mapping which makes things doubly-slow.
 
                     mat4 *pbindMatrices = nullptr;
-                    node.skin->inverseBindMatrices->ExtractData(pbindMatrices);
+                    node.skin->inverseBindMatrices->ExtractData(pbindMatrices, nullptr);
 
                     for (uint32_t i = 0; i < numBones; ++i) {
                         const std::vector<aiVertexWeight> &weights = weighting[i];
@@ -1221,11 +1226,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
         }
 
         if (node.camera) {
-            pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
+            mScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
         }
 
         if (node.light) {
-            pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
+            mScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
 
             // range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
             // it is added to meta data of parent node, because there is no other place to put it
@@ -1257,7 +1262,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
     // The root nodes
     unsigned int numRootNodes = unsigned(rootNodes.size());
     if (numRootNodes == 1) { // a single root node: use it
-        mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
+        mScene->mRootNode = ImportNode(r, rootNodes[0]);
     } else if (numRootNodes > 1) { // more than one root node: create a fake root
         aiNode *root = mScene->mRootNode = new aiNode("ROOT");
 
@@ -1265,7 +1270,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
         std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr);
 
         for (unsigned int i = 0; i < numRootNodes; ++i) {
-            aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
+            aiNode *node = ImportNode(r, rootNodes[i]);
             node->mParent = root;
             root->mChildren[root->mNumChildren++] = node;
         }
@@ -1666,6 +1671,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
 
     // clean all member arrays
     meshOffsets.clear();
+    mVertexRemappingTables.clear();
     mEmbeddedTexIdxs.clear();
 
     this->mScene = pScene;

+ 4 - 1
code/AssetLib/glTF2/glTF2Importer.h

@@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_GLTF2IMPORTER_H_INC
 
 #include <assimp/BaseImporter.h>
+#include <AssetLib/glTF2/glTF2Asset.h>
 
 struct aiNode;
 
@@ -59,7 +60,7 @@ namespace Assimp {
 class glTF2Importer : public BaseImporter {
 public:
     glTF2Importer();
-    ~glTF2Importer() override;
+    ~glTF2Importer() override = default;
     bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
 
 protected:
@@ -76,10 +77,12 @@ private:
     void ImportNodes(glTF2::Asset &a);
     void ImportAnimations(glTF2::Asset &a);
     void ImportCommonMetadata(glTF2::Asset &a);
+    aiNode *ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr);
 
 private:
     std::vector<unsigned int> meshOffsets;
     std::vector<int> mEmbeddedTexIdxs;
+    std::vector<std::vector<unsigned int>> mVertexRemappingTables; // for each converted aiMesh in the scene, it stores a list of vertices that are actually used
     aiScene *mScene;
 
     /// An instance of rapidjson::IRemoteSchemaDocumentProvider