Răsfoiți Sursa

Merge pull request #1034 from ascandal/feature/gltf-compact-skins

glTF compact skins
Kim Kulling 9 ani în urmă
părinte
comite
5cd16a0aa6
3 a modificat fișierele cu 146 adăugiri și 65 ștergeri
  1. 2 0
      code/glTFAsset.h
  2. 138 64
      code/glTFExporter.cpp
  3. 6 1
      code/glTFExporter.h

+ 2 - 0
code/glTFAsset.h

@@ -811,6 +811,8 @@ namespace glTF
         Ref<Skin>  skin;                          //!< The ID of the skin referenced by this node.
         std::string jointName;                    //!< Name used when this node is a joint in a skin.
 
+        Ref<Node> parent;                         //!< This is not part of the glTF specification. Used as a helper.
+
         Node() {}
         void Read(Value& obj, Asset& r);
     };

+ 138 - 64
code/glTFExporter.cpp

@@ -132,7 +132,7 @@ glTFExporter::glTFExporter(const char* filename, IOSystem* pIOSystem, const aiSc
     ExportMaterials();
 
     if (mScene->mRootNode) {
-        ExportNode(mScene->mRootNode);
+        ExportNodeHierarchy(mScene->mRootNode);
     }
 
     ExportMeshes();
@@ -164,6 +164,14 @@ static void CopyValue(const aiMatrix4x4& v, glTF::mat4& o)
     o[12] = v.a4; o[13] = v.b4; o[14] = v.c4; o[15] = v.d4;
 }
 
+static void CopyValue(const aiMatrix4x4& v, aiMatrix4x4& o)
+{
+    o.a1 = v.a1; o.a2 = v.a2; o.a3 = v.a3; o.a4 = v.a4;
+    o.b1 = v.b1; o.b2 = v.b2; o.b3 = v.b3; o.b4 = v.b4;
+    o.c1 = v.c1; o.c2 = v.c2; o.c3 = v.c3; o.c4 = v.c4;
+    o.d1 = v.d1; o.d2 = v.d2; o.d3 = v.d3; o.d4 = v.d4;
+}
+
 static void IdentityMatrix4(glTF::mat4& o)
 {
     o[ 0] = 1; o[ 1] = 0; o[ 2] = 0; o[ 3] = 0;
@@ -390,50 +398,37 @@ bool FindMeshNode(Ref<Node>& nodeIn, Ref<Node>& meshNode, std::string meshID)
 
 /*
  * Find the root joint of the skeleton.
+ * Starts will any joint node and traces up the tree,
+ * until a parent is found that does not have a jointName.
+ * Returns the first parent Ref<Node> found that does not have a jointName.
  */
 Ref<Node> FindSkeletonRootJoint(Ref<Skin>& skinRef)
 {
-    Ref<Node> candidateNodeRef;
-    Ref<Node> testNodeRef;
-
-    for (unsigned int i = 0; i < skinRef->jointNames.size(); ++i) {
-        candidateNodeRef = skinRef->jointNames[i];
-        bool candidateIsRoot = true;
-
-        for (unsigned int j = 0; j < skinRef->jointNames.size(); ++j) {
-            if (i == j) continue;
+    Ref<Node> startNodeRef;
+    Ref<Node> parentNodeRef;
 
-            testNodeRef = skinRef->jointNames[j];
-            for (unsigned int k = 0; k < testNodeRef->children.size(); ++k) {
-                std::string childNodeRefID = testNodeRef->children[k]->id;
-
-                if (childNodeRefID.compare(candidateNodeRef->id) == 0) {
-                    candidateIsRoot = false;
-                }
-            }
-        }
+    // Arbitrarily use the first joint to start the search.
+    startNodeRef = skinRef->jointNames[0];
+    parentNodeRef = skinRef->jointNames[0];
 
-        if(candidateIsRoot == true) {
-            return candidateNodeRef;
-        }
-    }
+    do {
+        startNodeRef = parentNodeRef;
+        parentNodeRef = startNodeRef->parent;
+    } while (!parentNodeRef->jointName.empty());
 
-    return candidateNodeRef;
+    return parentNodeRef;
 }
 
-void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref<Mesh>& meshRef, Ref<Buffer>& bufferRef)
+void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref<Mesh>& meshRef, Ref<Buffer>& bufferRef, Ref<Skin>& skinRef, std::vector<aiMatrix4x4>& inverseBindMatricesData)
 {
-    std::string skinName = aim->mName.C_Str();
-    skinName = mAsset.FindUniqueID(skinName, "skin");
-    Ref<Skin> skinRef = mAsset.skins.Create(skinName);
-    skinRef->name = skinName;
-
-    mat4* inverseBindMatricesData = new mat4[aim->mNumBones];
+    if (aim->mNumBones < 1) {
+        return;
+    }
 
     // Store the vertex joint and weight data.
     vec4* vertexJointData = new vec4[aim->mNumVertices];
     vec4* vertexWeightData = new vec4[aim->mNumVertices];
-    unsigned int* jointsPerVertex = new unsigned int[aim->mNumVertices];
+    int* jointsPerVertex = new int[aim->mNumVertices];
     for (size_t i = 0; i < aim->mNumVertices; ++i) {
         jointsPerVertex[i] = 0;
         for (size_t j = 0; j < 4; ++j) {
@@ -448,49 +443,49 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref<Mesh>& meshRef, Ref<Buffer
         // aib->mName   =====>  skinRef->jointNames
         // Find the node with id = mName.
         Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str());
-        nodeRef->jointName = "joint_" + to_string(idx_bone);
-        skinRef->jointNames.push_back(nodeRef);
+        nodeRef->jointName = nodeRef->id;
+
+        unsigned int jointNamesIndex;
+        bool addJointToJointNames = true;
+        for (int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) {
+            if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) {
+                addJointToJointNames = false;
+                jointNamesIndex = idx_joint;
+            }
+        }
 
-        // Identity Matrix   =====>  skinRef->bindShapeMatrix
-        // Temporary. Hard-coded identity matrix here
-        skinRef->bindShapeMatrix.isPresent = true;
-        IdentityMatrix4(skinRef->bindShapeMatrix.value);
+        if (addJointToJointNames) {
+            skinRef->jointNames.push_back(nodeRef);
 
-        // aib->mOffsetMatrix   =====>  skinRef->inverseBindMatrices
-        CopyValue(aib->mOffsetMatrix, inverseBindMatricesData[idx_bone]);
+            // aib->mOffsetMatrix   =====>  skinRef->inverseBindMatrices
+            aiMatrix4x4 tmpMatrix4;
+            CopyValue(aib->mOffsetMatrix, tmpMatrix4);
+            inverseBindMatricesData.push_back(tmpMatrix4);
+            jointNamesIndex = inverseBindMatricesData.size() - 1;
+        }
 
         // aib->mWeights   =====>  vertexWeightData
         for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) {
-            aiVertexWeight tmpVertWeight = aib->mWeights[idx_weights];
-            vertexJointData[tmpVertWeight.mVertexId][jointsPerVertex[tmpVertWeight.mVertexId]] = idx_bone;
-            vertexWeightData[tmpVertWeight.mVertexId][jointsPerVertex[tmpVertWeight.mVertexId]] = tmpVertWeight.mWeight;
+            unsigned int vertexId = aib->mWeights[idx_weights].mVertexId;
+            float vertWeight      = aib->mWeights[idx_weights].mWeight;
 
-            jointsPerVertex[tmpVertWeight.mVertexId] += 1;
-        }
+            // A vertex can only have at most four joint weights. Ignore all others.
+            if (jointsPerVertex[vertexId] > 3) { continue; }
 
-    } // End: for-loop mNumMeshes
+            vertexJointData[vertexId][jointsPerVertex[vertexId]] = jointNamesIndex;
+            vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
 
-    // Create the Accessor for skinRef->inverseBindMatrices
-    Ref<Accessor> invBindMatrixAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumBones, inverseBindMatricesData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT);
-    if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor;
+            jointsPerVertex[vertexId] += 1;
+        }
 
+    } // End: for-loop mNumMeshes
 
     Mesh::Primitive& p = meshRef->primitives.back();
-    Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumVertices, vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
+    Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aim->mNumVertices, vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
     if (vertexJointAccessor) p.attributes.joint.push_back(vertexJointAccessor);
 
-    Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumVertices, vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
+    Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinRef->id, bufferRef, aim->mNumVertices, vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
     if (vertexWeightAccessor) p.attributes.weight.push_back(vertexWeightAccessor);
-
-
-    // Find node that contains this mesh and add "skeletons" and "skin" attributes to that node.
-    Ref<Node> rootNode = mAsset.nodes.Get(unsigned(0));
-    Ref<Node> meshNode;
-    FindMeshNode(rootNode, meshNode, meshRef->id);
-
-    Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
-    meshNode->skeletons.push_back(rootJoint);
-    meshNode->skin = skinRef;
 }
 
 void glTFExporter::ExportMeshes()
@@ -520,6 +515,26 @@ void glTFExporter::ExportMeshes()
        b = mAsset->buffers.Create(bufferId);
     }
 
+    //----------------------------------------
+    // Initialize variables for the skin
+    bool createSkin = false;
+    for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
+        const aiMesh* aim = mScene->mMeshes[idx_mesh];
+        if(aim->HasBones()) {
+            createSkin = true;
+            break;
+        }
+    }
+
+    Ref<Skin> skinRef;
+    std::string skinName = mAsset->FindUniqueID("skin", "skin");
+    std::vector<aiMatrix4x4> inverseBindMatricesData;
+    if(createSkin) {
+        skinRef = mAsset->skins.Create(skinName);
+        skinRef->name = skinName;
+    }
+    //----------------------------------------
+
 	for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
 		const aiMesh* aim = mScene->mMeshes[idx_mesh];
 
@@ -616,7 +631,7 @@ void glTFExporter::ExportMeshes()
 
     /*************** Skins ****************/
     if(aim->HasBones()) {
-        ExportSkin(*mAsset, aim, m, b);
+        ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
     }
 
 		/****************** Compression ******************/
@@ -711,9 +726,41 @@ void glTFExporter::ExportMeshes()
 #endif
 		}// if(comp_allow)
 	}// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i)
+
+    //----------------------------------------
+    // Finish the skin
+    // Create the Accessor for skinRef->inverseBindMatrices
+    if (createSkin) {
+        mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()];
+        for (int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) {
+            CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]);
+        }
+
+        Ref<Accessor> invBindMatrixAccessor = ExportData(*mAsset, skinName, b, inverseBindMatricesData.size(), invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT);
+        if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor;
+
+        // Identity Matrix   =====>  skinRef->bindShapeMatrix
+        // Temporary. Hard-coded identity matrix here
+        skinRef->bindShapeMatrix.isPresent = true;
+        IdentityMatrix4(skinRef->bindShapeMatrix.value);
+
+        // Find node that contains this mesh and add "skeletons" and "skin" attributes to that node.
+        Ref<Node> rootNode = mAsset->nodes.Get(unsigned(0));
+        Ref<Node> meshNode;
+        std::string meshID = mAsset->meshes.Get(unsigned(0))->id;
+        FindMeshNode(rootNode, meshNode, meshID);
+
+        Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
+        meshNode->skeletons.push_back(rootJoint);
+        meshNode->skin = skinRef;
+    }
 }
 
-unsigned int glTFExporter::ExportNode(const aiNode* n)
+/*
+ * Export the root node of the node hierarchy.
+ * Calls ExportNode for all children.
+ */
+unsigned int glTFExporter::ExportNodeHierarchy(const aiNode* n)
 {
     Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
 
@@ -727,7 +774,34 @@ unsigned int glTFExporter::ExportNode(const aiNode* n)
     }
 
     for (unsigned int i = 0; i < n->mNumChildren; ++i) {
-        unsigned int idx = ExportNode(n->mChildren[i]);
+        unsigned int idx = ExportNode(n->mChildren[i], node);
+        node->children.push_back(mAsset->nodes.Get(idx));
+    }
+
+    return node.GetIndex();
+}
+
+/*
+ * Export node and recursively calls ExportNode for all children.
+ * Since these nodes are not the root node, we also export the parent Ref<Node>
+ */
+unsigned int glTFExporter::ExportNode(const aiNode* n, Ref<Node>& parent)
+{
+    Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
+
+    node->parent = parent;
+
+    if (!n->mTransformation.IsIdentity()) {
+        node->matrix.isPresent = true;
+        CopyValue(n->mTransformation, node->matrix.value);
+    }
+
+    for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
+        node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i]));
+    }
+
+    for (unsigned int i = 0; i < n->mNumChildren; ++i) {
+        unsigned int idx = ExportNode(n->mChildren[i], node);
         node->children.push_back(mAsset->nodes.Get(idx));
     }
 

+ 6 - 1
code/glTFExporter.h

@@ -58,8 +58,12 @@ struct aiMaterial;
 
 namespace glTF
 {
+    template<class T>
+    class Ref;
+
     class Asset;
     struct TexProperty;
+    struct Node;
 }
 
 namespace Assimp
@@ -98,7 +102,8 @@ namespace Assimp
         void ExportMetadata();
         void ExportMaterials();
         void ExportMeshes();
-        unsigned int ExportNode(const aiNode* node);
+        unsigned int ExportNodeHierarchy(const aiNode* n);
+        unsigned int ExportNode(const aiNode* node, glTF::Ref<glTF::Node>& parent);
         void ExportScene();
         void ExportAnimations();
     };