From 3fd865b4bf1a35cfca578c286107e97ecb1cae79 Mon Sep 17 00:00:00 2001 From: Gordon Chapman Date: Wed, 24 Mar 2021 18:10:03 -0700 Subject: [PATCH] Added Blendshape Support to FBX Export --- code/AssetLib/FBX/FBXExporter.cpp | 109 +++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index ae93cba02..322161411 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -812,6 +812,18 @@ void FBXExporter::WriteDefinitions () // Geometry / FbxMesh // <~~ aiMesh count = mScene->mNumMeshes; + + // Blendshapes are considered Geometry + int32_t bsDeformerCount=0; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + aiMesh* m = mScene->mMeshes[mi]; + if (m->mNumAnimMeshes > 0) { + count+=m->mNumAnimMeshes; + bsDeformerCount+=m->mNumAnimMeshes; // One deformer per blendshape + bsDeformerCount++; // Plus one master blendshape deformer + } + } + if (count) { n = FBX::Node("ObjectType", "Geometry"); n.AddChild("Count", count); @@ -978,7 +990,7 @@ void FBXExporter::WriteDefinitions () } // Deformer - count = int32_t(count_deformers(mScene)); + count = int32_t(count_deformers(mScene))+bsDeformerCount; if (count) { n = FBX::Node("ObjectType", "Deformer"); n.AddChild("Count", count); @@ -1363,6 +1375,7 @@ void FBXExporter::WriteObjects () n.End(outstream, binary, indent, true); } + // aiMaterial material_uids.clear(); for (size_t i = 0; i < mScene->mNumMaterials; ++i) { @@ -1697,6 +1710,100 @@ void FBXExporter::WriteObjects () } } + // Blendshapes, if any + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + if (m->mNumAnimMeshes == 0) { + continue; + } + // make a deformer for this mesh + int64_t deformer_uid = generate_uid(); + FBX::Node dnode("Deformer"); + dnode.AddProperties(deformer_uid, m->mName.data + FBX::SEPARATOR + "Blendshapes", "BlendShape"); + dnode.AddChild("Version", int32_t(101)); + dnode.Dump(outstream, binary, indent); + // connect it + connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); + std::vector vertex_indices = vVertexIndice[mi]; + + for (unsigned int am = 0; am < m->mNumAnimMeshes; ++am) { + aiAnimMesh *pAnimMesh = m->mAnimMeshes[am]; + std::string blendshape_name = pAnimMesh->mName.data; + + // start the node record + FBX::Node bsnode("Geometry"); + int64_t blendshape_uid = generate_uid(); + mesh_uids.push_back(blendshape_uid); + bsnode.AddProperty(blendshape_uid); + bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape"); + bsnode.AddProperty("Shape"); + bsnode.AddChild("Version", int32_t(100)); + bsnode.Begin(outstream, binary, indent); + bsnode.DumpProperties(outstream, binary, indent); + bsnode.EndProperties(outstream, binary, indent); + bsnode.BeginChildren(outstream, binary, indent); + indent++; + if (pAnimMesh->HasPositions()) { + std::vectorshape_indices; + std::vectorpPositionDiff; + std::vectorpNormalDiff; + + for (unsigned int vt = 0; vt < vertex_indices.size(); ++vt) { + aiVector3D pDiff = (pAnimMesh->mVertices[vertex_indices[vt]] - m->mVertices[vertex_indices[vt]]); + if(pDiff.Length()>1e-8){ + shape_indices.push_back(vertex_indices[vt]); + pPositionDiff.push_back(pDiff[0]); + pPositionDiff.push_back(pDiff[1]); + pPositionDiff.push_back(pDiff[2]); + + if (pAnimMesh->HasNormals()) { + aiVector3D nDiff = (pAnimMesh->mNormals[vertex_indices[vt]] - m->mNormals[vertex_indices[vt]]); + pNormalDiff.push_back(nDiff[0]); + pNormalDiff.push_back(nDiff[1]); + pNormalDiff.push_back(nDiff[2]); + } + } + } + + FBX::Node::WritePropertyNode( + "Indexes", shape_indices, outstream, binary, indent + ); + + FBX::Node::WritePropertyNode( + "Vertices", pPositionDiff, outstream, binary, indent + ); + + if (pNormalDiff.size()>0) { + FBX::Node::WritePropertyNode( + "Normals", pNormalDiff, outstream, binary, indent + ); + } + } + indent--; + bsnode.End(outstream, binary, indent, true); + + // Add blendshape Channel Deformer + FBX::Node sdnode("Deformer"); + const int64_t blendchannel_uid = generate_uid(); + sdnode.AddProperties( + blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel" + ); + sdnode.AddChild("Version", int32_t(100)); + sdnode.AddChild("DeformPercent", int32_t(100)); + FBX::Node p("Properties70"); + p.AddP70numberA("DeformPercent", 100.); + sdnode.AddChild(p); + // TODO: Normally just one weight per channel, adding stub for later development + std::vectorfFullWeights; + fFullWeights.push_back(0.); + sdnode.AddChild("FullWeights", fFullWeights); + sdnode.Dump(outstream, binary, indent); + + connections.emplace_back("C", "OO", blendchannel_uid, deformer_uid); + connections.emplace_back("C", "OO", blendshape_uid, blendchannel_uid); + } + } + // bones. // // output structure: