Browse Source

Generate skinning information if animated model has non-skinned subgeometries. Closes #1227.

Lasse Öörni 9 years ago
parent
commit
49ce2984fe
1 changed files with 110 additions and 27 deletions
  1. 110 27
      Source/Tools/AssetImporter/AssetImporter.cpp

+ 110 - 27
Source/Tools/AssetImporter/AssetImporter.cpp

@@ -156,7 +156,7 @@ unsigned GetMeshIndex(aiMesh* mesh);
 unsigned GetBoneIndex(OutModel& model, const String& boneName);
 unsigned GetBoneIndex(OutModel& model, const String& boneName);
 aiBone* GetMeshBone(OutModel& model, const String& boneName);
 aiBone* GetMeshBone(OutModel& model, const String& boneName);
 Matrix3x4 GetOffsetMatrix(OutModel& model, const String& boneName);
 Matrix3x4 GetOffsetMatrix(OutModel& model, const String& boneName);
-void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappings, Vector<PODVector<unsigned char> >&
+void GetBlendData(OutModel& model, aiMesh* mesh, aiNode* meshNode, PODVector<unsigned>& boneMappings, Vector<PODVector<unsigned char> >&
     blendIndices, Vector<PODVector<float> >& blendWeights);
     blendIndices, Vector<PODVector<float> >& blendWeights);
 String GetMeshMaterialName(aiMesh* mesh);
 String GetMeshMaterialName(aiMesh* mesh);
 String GetMaterialTextureName(const String& nameIn);
 String GetMaterialTextureName(const String& nameIn);
@@ -169,7 +169,7 @@ void WriteLargeIndices(unsigned*& dest, aiMesh* mesh, unsigned index, unsigned o
 void WriteVertex(float*& dest, aiMesh* mesh, unsigned index, unsigned elementMask, BoundingBox& box,
 void WriteVertex(float*& dest, aiMesh* mesh, unsigned index, unsigned elementMask, BoundingBox& box,
     const Matrix3x4& vertexTransform, const Matrix3& normalTransform, Vector<PODVector<unsigned char> >& blendIndices,
     const Matrix3x4& vertexTransform, const Matrix3& normalTransform, Vector<PODVector<unsigned char> >& blendIndices,
     Vector<PODVector<float> >& blendWeights);
     Vector<PODVector<float> >& blendWeights);
-unsigned GetElementMask(aiMesh* mesh);
+unsigned GetElementMask(aiMesh* mesh, bool isSkinned);
 
 
 aiNode* GetNode(const String& name, aiNode* rootNode, bool caseSensitive = true);
 aiNode* GetNode(const String& name, aiNode* rootNode, bool caseSensitive = true);
 aiMatrix4x4 GetDerivedTransform(aiNode* node, aiNode* rootNode, bool rootInclusive = true);
 aiMatrix4x4 GetDerivedTransform(aiNode* node, aiNode* rootNode, bool rootInclusive = true);
@@ -587,6 +587,16 @@ void CollectBones(OutModel& model, bool animationOnly)
     HashSet<aiNode*> necessary;
     HashSet<aiNode*> necessary;
     HashSet<aiNode*> rootNodes;
     HashSet<aiNode*> rootNodes;
 
 
+    bool haveSkinnedMeshes = false;
+    for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+    {
+        if (model.meshes_[i]->HasBones())
+        {
+            haveSkinnedMeshes = true;
+            break;
+        }
+    }
+
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     {
     {
         aiMesh* mesh = model.meshes_[i];
         aiMesh* mesh = model.meshes_[i];
@@ -616,8 +626,29 @@ void CollectBones(OutModel& model, bool animationOnly)
             if (rootNodes.Find(rootNode) == rootNodes.End())
             if (rootNodes.Find(rootNode) == rootNodes.End())
                 rootNodes.Insert(rootNode);
                 rootNodes.Insert(rootNode);
         }
         }
+
+        // When model is partially skinned, include the attachment nodes of the rigid meshes in the skeleton
+        if (haveSkinnedMeshes && !mesh->mNumBones)
+        {
+            aiNode* boneNode = meshNode;
+            necessary.Insert(boneNode);
+            rootNode = boneNode;
+
+            for (;;)
+            {
+                boneNode = boneNode->mParent;
+                if (!boneNode || ((boneNode == meshNode || boneNode == meshParentNode) && !animationOnly))
+                    break;
+                rootNode = boneNode;
+                necessary.Insert(boneNode);
+            }
+
+            if (rootNodes.Find(rootNode) == rootNodes.End())
+                rootNodes.Insert(rootNode);
+        }
     }
     }
 
 
+
     // If we find multiple root nodes, try to remedy by going back in the parent chain and finding a common parent
     // If we find multiple root nodes, try to remedy by going back in the parent chain and finding a common parent
     if (rootNodes.Size() > 1)
     if (rootNodes.Size() > 1)
     {
     {
@@ -841,13 +872,13 @@ void BuildAndSaveModel(OutModel& model)
 
 
     bool combineBuffers = true;
     bool combineBuffers = true;
     // Check if buffers can be combined (same vertex element mask, under 65535 vertices)
     // Check if buffers can be combined (same vertex element mask, under 65535 vertices)
-    unsigned elementMask = GetElementMask(model.meshes_[0]);
+    unsigned elementMask = GetElementMask(model.meshes_[0], model.bones_.Size() > 0);
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     {
     {
         if (GetNumValidFaces(model.meshes_[i]))
         if (GetNumValidFaces(model.meshes_[i]))
         {
         {
             ++numValidGeometries;
             ++numValidGeometries;
-            if (i > 0 && GetElementMask(model.meshes_[i]) != elementMask)
+            if (i > 0 && GetElementMask(model.meshes_[i], model.bones_.Size() > 0) != elementMask)
                 combineBuffers = false;
                 combineBuffers = false;
         }
         }
     }
     }
@@ -880,7 +911,7 @@ void BuildAndSaveModel(OutModel& model)
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     {
     {
         aiMesh* mesh = model.meshes_[i];
         aiMesh* mesh = model.meshes_[i];
-        unsigned elementMask = GetElementMask(mesh);
+        unsigned elementMask = GetElementMask(mesh, model.bones_.Size() > 0);
         unsigned validFaces = GetNumValidFaces(mesh);
         unsigned validFaces = GetNumValidFaces(mesh);
         if (!validFaces)
         if (!validFaces)
             continue;
             continue;
@@ -954,7 +985,7 @@ void BuildAndSaveModel(OutModel& model)
         Vector<PODVector<float> > blendWeights;
         Vector<PODVector<float> > blendWeights;
         PODVector<unsigned> boneMappings;
         PODVector<unsigned> boneMappings;
         if (model.bones_.Size())
         if (model.bones_.Size())
-            GetBlendData(model, mesh, boneMappings, blendIndices, blendWeights);
+            GetBlendData(model, mesh, model.meshNodes_[i], boneMappings, blendIndices, blendWeights);
 
 
         float* dest = (float*)((unsigned char*)vertexData + startVertexOffset * vb->GetVertexSize());
         float* dest = (float*)((unsigned char*)vertexData + startVertexOffset * vb->GetVertexSize());
         for (unsigned j = 0; j < mesh->mNumVertices; ++j)
         for (unsigned j = 0; j < mesh->mNumVertices; ++j)
@@ -1985,10 +2016,24 @@ Matrix3x4 GetOffsetMatrix(OutModel& model, const String& boneName)
             }
             }
         }
         }
     }
     }
+
+    // Fallback for rigid skinning for which actual offset matrix information doesn't exist
+    for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+    {
+        aiMesh* mesh = model.meshes_[i];
+        aiNode* node = model.meshNodes_[i];
+        if (!mesh->HasBones() && boneName == node->mName.data)
+        {
+            aiMatrix4x4 nodeDerivedInverse = GetMeshBakingTransform(node, model.rootNode_);
+            nodeDerivedInverse.Inverse();
+            return ToMatrix3x4(nodeDerivedInverse);
+        }
+    }
+
     return Matrix3x4::IDENTITY;
     return Matrix3x4::IDENTITY;
 }
 }
 
 
-void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappings, Vector<PODVector<unsigned char> >&
+void GetBlendData(OutModel& model, aiMesh* mesh, aiNode* meshNode, PODVector<unsigned>& boneMappings, Vector<PODVector<unsigned char> >&
     blendIndices, Vector<PODVector<float> >& blendWeights)
     blendIndices, Vector<PODVector<float> >& blendWeights)
 {
 {
     blendIndices.Resize(mesh->mNumVertices);
     blendIndices.Resize(mesh->mNumVertices);
@@ -2005,37 +2050,75 @@ void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappin
                 "that each stay at " + String(maxBones_) + " bones or below."
                 "that each stay at " + String(maxBones_) + " bones or below."
             );
             );
         }
         }
-        boneMappings.Resize(mesh->mNumBones);
-        for (unsigned i = 0; i < mesh->mNumBones; ++i)
+        if (mesh->mNumBones > 0)
         {
         {
-            aiBone* bone = mesh->mBones[i];
-            String boneName = FromAIString(bone->mName);
+            boneMappings.Resize(mesh->mNumBones);
+            for (unsigned i = 0; i < mesh->mNumBones; ++i)
+            {
+                aiBone* bone = mesh->mBones[i];
+                String boneName = FromAIString(bone->mName);
+                unsigned globalIndex = GetBoneIndex(model, boneName);
+                if (globalIndex == M_MAX_UNSIGNED)
+                    ErrorExit("Bone " + boneName + " not found");
+                boneMappings[i] = globalIndex;
+                for (unsigned j = 0; j < bone->mNumWeights; ++j)
+                {
+                    unsigned vertex = bone->mWeights[j].mVertexId;
+                    blendIndices[vertex].Push(i);
+                    blendWeights[vertex].Push(bone->mWeights[j].mWeight);
+                }
+            }
+        }
+        else
+        {
+            // If mesh does not have skinning information, implement rigid skinning so that it stays compatible with AnimatedModel
+            String boneName = FromAIString(meshNode->mName);
             unsigned globalIndex = GetBoneIndex(model, boneName);
             unsigned globalIndex = GetBoneIndex(model, boneName);
             if (globalIndex == M_MAX_UNSIGNED)
             if (globalIndex == M_MAX_UNSIGNED)
-                ErrorExit("Bone " + boneName + " not found");
-            boneMappings[i] = globalIndex;
-            for (unsigned j = 0; j < bone->mNumWeights; ++j)
+                PrintLine("Warning: bone " + boneName + " not found, skipping rigid skinning");
+            else
             {
             {
-                unsigned vertex = bone->mWeights[j].mVertexId;
-                blendIndices[vertex].Push(i);
-                blendWeights[vertex].Push(bone->mWeights[j].mWeight);
+                boneMappings.Push(globalIndex);
+                for (unsigned i = 0; i < mesh->mNumVertices; ++i)
+                {
+                    blendIndices[i].Push(0);
+                    blendWeights[i].Push(1.0f);
+                }
             }
             }
         }
         }
     }
     }
     else
     else
     {
     {
-        for (unsigned i = 0; i < mesh->mNumBones; ++i)
+        if (mesh->mNumBones > 0)
         {
         {
-            aiBone* bone = mesh->mBones[i];
-            String boneName = FromAIString(bone->mName);
+            for (unsigned i = 0; i < mesh->mNumBones; ++i)
+            {
+                aiBone* bone = mesh->mBones[i];
+                String boneName = FromAIString(bone->mName);
+                unsigned globalIndex = GetBoneIndex(model, boneName);
+                if (globalIndex == M_MAX_UNSIGNED)
+                    ErrorExit("Bone " + boneName + " not found");
+                for (unsigned j = 0; j < bone->mNumWeights; ++j)
+                {
+                    unsigned vertex = bone->mWeights[j].mVertexId;
+                    blendIndices[vertex].Push(globalIndex);
+                    blendWeights[vertex].Push(bone->mWeights[j].mWeight);
+                }
+            }
+        }
+        else
+        {
+            String boneName = FromAIString(meshNode->mName);
             unsigned globalIndex = GetBoneIndex(model, boneName);
             unsigned globalIndex = GetBoneIndex(model, boneName);
             if (globalIndex == M_MAX_UNSIGNED)
             if (globalIndex == M_MAX_UNSIGNED)
-                ErrorExit("Bone " + boneName + " not found");
-            for (unsigned j = 0; j < bone->mNumWeights; ++j)
+                PrintLine("Warning: bone " + boneName + " not found, skipping rigid skinning");
+            else
             {
             {
-                unsigned vertex = bone->mWeights[j].mVertexId;
-                blendIndices[vertex].Push(globalIndex);
-                blendWeights[vertex].Push(bone->mWeights[j].mWeight);
+                for (unsigned i = 0; i < mesh->mNumVertices; ++i)
+                {
+                    blendIndices[i].Push(globalIndex);
+                    blendWeights[i].Push(1.0f);
+                }
             }
             }
         }
         }
     }
     }
@@ -2230,7 +2313,7 @@ void WriteVertex(float*& dest, aiMesh* mesh, unsigned index, unsigned elementMas
     }
     }
 }
 }
 
 
-unsigned GetElementMask(aiMesh* mesh)
+unsigned GetElementMask(aiMesh* mesh, bool isSkinned)
 {
 {
     unsigned elementMask = MASK_POSITION;
     unsigned elementMask = MASK_POSITION;
     if (mesh->HasNormals())
     if (mesh->HasNormals())
@@ -2243,7 +2326,7 @@ unsigned GetElementMask(aiMesh* mesh)
         elementMask |= MASK_TEXCOORD1;
         elementMask |= MASK_TEXCOORD1;
     if (mesh->GetNumUVChannels() > 1)
     if (mesh->GetNumUVChannels() > 1)
         elementMask |= MASK_TEXCOORD2;
         elementMask |= MASK_TEXCOORD2;
-    if (mesh->HasBones())
+    if (isSkinned)
         elementMask |= (MASK_BLENDWEIGHTS | MASK_BLENDINDICES);
         elementMask |= (MASK_BLENDWEIGHTS | MASK_BLENDINDICES);
     return elementMask;
     return elementMask;
 }
 }