From 57a00d5faad172440447ac0a8e52ea8866ce6e93 Mon Sep 17 00:00:00 2001 From: Angelo Scandaliato Date: Tue, 11 Oct 2016 15:28:04 -0700 Subject: [PATCH 1/5] find skeleton root joint and only allow four joint weights per vertex --- code/glTFAsset.h | 2 + code/glTFExporter.cpp | 86 +++++++++++++++++++++++++++---------------- code/glTFExporter.h | 7 +++- 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/code/glTFAsset.h b/code/glTFAsset.h index 247dfd148..2e49b0d8f 100644 --- a/code/glTFAsset.h +++ b/code/glTFAsset.h @@ -811,6 +811,8 @@ namespace glTF Ref 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 parent; //!< This is not part of the glTF specification. Used as a helper. + Node() {} void Read(Value& obj, Asset& r); }; diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 90c74f30e..b474b47ca 100644 --- a/code/glTFExporter.cpp +++ b/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(); @@ -391,39 +391,33 @@ bool FindMeshNode(Ref& nodeIn, Ref& 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 found that does not have a jointName. */ Ref FindSkeletonRootJoint(Ref& skinRef) { - Ref candidateNodeRef; - Ref testNodeRef; + Ref startNodeRef; + Ref parentNodeRef; - for (unsigned int i = 0; i < skinRef->jointNames.size(); ++i) { - candidateNodeRef = skinRef->jointNames[i]; - bool candidateIsRoot = true; + // Arbitrarily use the first joint to start the search. + startNodeRef = skinRef->jointNames[0]; + parentNodeRef = skinRef->jointNames[0]; - for (unsigned int j = 0; j < skinRef->jointNames.size(); ++j) { - if (i == j) continue; + do { + startNodeRef = parentNodeRef; + parentNodeRef = startNodeRef->parent; + } while (!parentNodeRef->jointName.empty()); - 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; - } - } - } - - if(candidateIsRoot == true) { - return candidateNodeRef; - } - } - - return candidateNodeRef; + return parentNodeRef; } void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, Ref& bufferRef) { + if (aim->mNumBones < 1) { + return; + } + std::string skinName = aim->mName.C_Str(); skinName = mAsset.FindUniqueID(skinName, "skin"); Ref skinRef = mAsset.skins.Create(skinName); @@ -434,7 +428,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, RefmNumVertices]; 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) { @@ -462,11 +456,16 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, RefmWeights =====> 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; } + + vertexJointData[vertexId][jointsPerVertex[vertexId]] = idx_bone; + vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; + + jointsPerVertex[vertexId] += 1; } } // End: for-loop mNumMeshes @@ -714,7 +713,8 @@ void glTFExporter::ExportMeshes() }// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) } -unsigned int glTFExporter::ExportNode(const aiNode* n) + +unsigned int glTFExporter::ExportNodeHierarchy(const aiNode* n) { Ref node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); @@ -728,7 +728,31 @@ 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(); +} + + +unsigned int glTFExporter::ExportNode(const aiNode* n, Ref& parent) +{ + Ref 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)); } diff --git a/code/glTFExporter.h b/code/glTFExporter.h index 0f9c169e5..49df9193e 100644 --- a/code/glTFExporter.h +++ b/code/glTFExporter.h @@ -58,8 +58,12 @@ struct aiMaterial; namespace glTF { + template + 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& parent); void ExportScene(); void ExportAnimations(); }; From dc1e11c8be8e0d389d2171634287112f5d89a20c Mon Sep 17 00:00:00 2001 From: Angelo Scandaliato Date: Tue, 11 Oct 2016 15:33:52 -0700 Subject: [PATCH 2/5] added some comments --- code/glTFExporter.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index b474b47ca..5f53c4ee7 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -713,7 +713,10 @@ void glTFExporter::ExportMeshes() }// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) } - +/* + * Export the root node of the node hierarchy. + * Calls ExportNode for all children. + */ unsigned int glTFExporter::ExportNodeHierarchy(const aiNode* n) { Ref node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); @@ -735,7 +738,10 @@ unsigned int glTFExporter::ExportNodeHierarchy(const aiNode* n) 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 + */ unsigned int glTFExporter::ExportNode(const aiNode* n, Ref& parent) { Ref node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); From 1c03aebfb843da76a9d3d45b7f6acad291708f55 Mon Sep 17 00:00:00 2001 From: Angelo Scandaliato Date: Thu, 13 Oct 2016 19:49:59 -0700 Subject: [PATCH 3/5] test combining skins --- code/glTFExporter.cpp | 98 ++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 5f53c4ee7..eb6c73ce7 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -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; @@ -412,19 +420,12 @@ Ref FindSkeletonRootJoint(Ref& skinRef) return parentNodeRef; } -void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, Ref& bufferRef) +void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, Ref& bufferRef, Ref& skinRef, std::vector& inverseBindMatricesData) { if (aim->mNumBones < 1) { return; } - std::string skinName = aim->mName.C_Str(); - skinName = mAsset.FindUniqueID(skinName, "skin"); - Ref skinRef = mAsset.skins.Create(skinName); - skinRef->name = skinName; - - mat4* inverseBindMatricesData = new mat4[aim->mNumBones]; - // Store the vertex joint and weight data. vec4* vertexJointData = new vec4[aim->mNumVertices]; vec4* vertexWeightData = new vec4[aim->mNumVertices]; @@ -443,16 +444,23 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, RefmName =====> skinRef->jointNames // Find the node with id = mName. Ref nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); - nodeRef->jointName = "joint_" + std::to_string(idx_bone); - skinRef->jointNames.push_back(nodeRef); + nodeRef->jointName = nodeRef->id; //"joint_" + std::to_string(idx_bone); - // Identity Matrix =====> skinRef->bindShapeMatrix - // Temporary. Hard-coded identity matrix here - skinRef->bindShapeMatrix.isPresent = true; - IdentityMatrix4(skinRef->bindShapeMatrix.value); + 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; + } + } - // aib->mOffsetMatrix =====> skinRef->inverseBindMatrices - CopyValue(aib->mOffsetMatrix, inverseBindMatricesData[idx_bone]); + if (addJointToJointNames) { + skinRef->jointNames.push_back(nodeRef); + + // aib->mOffsetMatrix =====> skinRef->inverseBindMatrices + aiMatrix4x4 tmpMatrix4; + CopyValue(aib->mOffsetMatrix, tmpMatrix4); + inverseBindMatricesData.push_back(tmpMatrix4); + } // aib->mWeights =====> vertexWeightData for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) { @@ -470,27 +478,12 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, RefinverseBindMatrices - Ref invBindMatrixAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumBones, inverseBindMatricesData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT); - if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor; - - Mesh::Primitive& p = meshRef->primitives.back(); - Ref vertexJointAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumVertices, vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); + Ref vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aim->mNumVertices, vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); if (vertexJointAccessor) p.attributes.joint.push_back(vertexJointAccessor); - Ref vertexWeightAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumVertices, vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); + Ref 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 rootNode = mAsset.nodes.Get(unsigned(0)); - Ref meshNode; - FindMeshNode(rootNode, meshNode, meshRef->id); - - Ref rootJoint = FindSkeletonRootJoint(skinRef); - meshNode->skeletons.push_back(rootJoint); - meshNode->skin = skinRef; } void glTFExporter::ExportMeshes() @@ -520,6 +513,20 @@ void glTFExporter::ExportMeshes() b = mAsset->buffers.Create(bufferId); } + //---------------------------------------- + // For the skin + std::string skinName = mAsset->FindUniqueID("skin", "skin"); + Ref skinRef = mAsset->skins.Create(skinName); + skinRef->name = skinName; + // std::vector inverseBindMatricesData; + std::vector inverseBindMatricesData; + + // Identity Matrix =====> skinRef->bindShapeMatrix + // Temporary. Hard-coded identity matrix here + skinRef->bindShapeMatrix.isPresent = true; + IdentityMatrix4(skinRef->bindShapeMatrix.value); + //---------------------------------------- + for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) { const aiMesh* aim = mScene->mMeshes[idx_mesh]; @@ -616,7 +623,7 @@ void glTFExporter::ExportMeshes() /*************** Skins ****************/ if(aim->HasBones()) { - ExportSkin(*mAsset, aim, m, b); + ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData); } /****************** Compression ******************/ @@ -711,6 +718,29 @@ 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 + 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 invBindMatrixAccessor = ExportData(*mAsset, skinName, b, inverseBindMatricesData.size(), invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT); + if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor; + + // Find node that contains this mesh and add "skeletons" and "skin" attributes to that node. + Ref rootNode = mAsset->nodes.Get(unsigned(0)); + Ref meshNode; + std::string meshID = mAsset->meshes.Get(unsigned(0))->id; + FindMeshNode(rootNode, meshNode, meshID); + + Ref rootJoint = FindSkeletonRootJoint(skinRef); + meshNode->skeletons.push_back(rootJoint); + meshNode->skin = skinRef; + //---------------------------------------- } /* From 9c4bb1c9bc900ed37ada1e9529df93acb4b8bf22 Mon Sep 17 00:00:00 2001 From: Angelo Scandaliato Date: Fri, 14 Oct 2016 15:47:21 -0700 Subject: [PATCH 4/5] find the correct index in jointNames --- code/glTFExporter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index eb6c73ce7..3c13db894 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -446,10 +446,12 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, Ref nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); nodeRef->jointName = nodeRef->id; //"joint_" + std::to_string(idx_bone); + 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; } } @@ -460,6 +462,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, RefmOffsetMatrix, tmpMatrix4); inverseBindMatricesData.push_back(tmpMatrix4); + jointNamesIndex = inverseBindMatricesData.size() - 1; } // aib->mWeights =====> vertexWeightData @@ -470,7 +473,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref& meshRef, Ref 3) { continue; } - vertexJointData[vertexId][jointsPerVertex[vertexId]] = idx_bone; + vertexJointData[vertexId][jointsPerVertex[vertexId]] = jointNamesIndex; vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; jointsPerVertex[vertexId] += 1; @@ -727,7 +730,6 @@ void glTFExporter::ExportMeshes() CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]); } - Ref invBindMatrixAccessor = ExportData(*mAsset, skinName, b, inverseBindMatricesData.size(), invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT); if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor; From fcf2d6357e7793226a3bd49972be30d402c1f959 Mon Sep 17 00:00:00 2001 From: Angelo Scandaliato Date: Sat, 15 Oct 2016 17:21:55 -0700 Subject: [PATCH 5/5] check if bones exist before creating skin --- code/glTFExporter.cpp | 66 +++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/code/glTFExporter.cpp b/code/glTFExporter.cpp index 3c13db894..074d3654a 100644 --- a/code/glTFExporter.cpp +++ b/code/glTFExporter.cpp @@ -517,17 +517,23 @@ void glTFExporter::ExportMeshes() } //---------------------------------------- - // For the skin - std::string skinName = mAsset->FindUniqueID("skin", "skin"); - Ref skinRef = mAsset->skins.Create(skinName); - skinRef->name = skinName; - // std::vector inverseBindMatricesData; - std::vector inverseBindMatricesData; + // 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; + } + } - // Identity Matrix =====> skinRef->bindShapeMatrix - // Temporary. Hard-coded identity matrix here - skinRef->bindShapeMatrix.isPresent = true; - IdentityMatrix4(skinRef->bindShapeMatrix.value); + Ref skinRef; + std::string skinName = mAsset->FindUniqueID("skin", "skin"); + std::vector inverseBindMatricesData; + if(createSkin) { + skinRef = mAsset->skins.Create(skinName); + skinRef->name = skinName; + } //---------------------------------------- for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) { @@ -725,24 +731,30 @@ void glTFExporter::ExportMeshes() //---------------------------------------- // Finish the skin // Create the Accessor for skinRef->inverseBindMatrices - mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()]; - for (int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) { - CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]); + 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 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 rootNode = mAsset->nodes.Get(unsigned(0)); + Ref meshNode; + std::string meshID = mAsset->meshes.Get(unsigned(0))->id; + FindMeshNode(rootNode, meshNode, meshID); + + Ref rootJoint = FindSkeletonRootJoint(skinRef); + meshNode->skeletons.push_back(rootJoint); + meshNode->skin = skinRef; } - - Ref invBindMatrixAccessor = ExportData(*mAsset, skinName, b, inverseBindMatricesData.size(), invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT); - if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor; - - // Find node that contains this mesh and add "skeletons" and "skin" attributes to that node. - Ref rootNode = mAsset->nodes.Get(unsigned(0)); - Ref meshNode; - std::string meshID = mAsset->meshes.Get(unsigned(0))->id; - FindMeshNode(rootNode, meshNode, meshID); - - Ref rootJoint = FindSkeletonRootJoint(skinRef); - meshNode->skeletons.push_back(rootJoint); - meshNode->skin = skinRef; - //---------------------------------------- } /*