diff --git a/projects/FBX/FBXSDK.cpp b/projects/FBX/FBXSDK.cpp index 013118ca88..dbc62d6e32 100644 --- a/projects/FBX/FBXSDK.cpp +++ b/projects/FBX/FBXSDK.cpp @@ -10,6 +10,7 @@ #include #include "zeno/utils/log.h" +#include "zeno/utils/bit_operations.h" #include #include "zeno/types/PrimitiveObject.h" #include "zeno/utils/scope_exit.h" @@ -343,6 +344,7 @@ struct ReadFBXFile: INode { // Import the contents of the file into the scene. lImporter->Import(fbx_object->lScene); + FbxRootNodeUtility::RemoveAllFbxRoots(fbx_object->lScene); // The file is imported; so get rid of the importer. lImporter->Destroy(); @@ -463,7 +465,7 @@ void getAttr(T* arr, std::string name, std::shared_ptr prim) { } } -std::shared_ptr GetMesh(FbxNode* pNode) { +static std::shared_ptr GetMesh(FbxNode* pNode) { FbxMesh* pMesh = pNode->GetMesh(); if (!pMesh) return nullptr; std::string nodeName = pNode->GetName(); @@ -595,7 +597,86 @@ std::shared_ptr GetMesh(FbxNode* pNode) { return prim; } -void TraverseNodesToGetNames(FbxNode* pNode, std::vector &names) { +static std::shared_ptr GetSkeleton(FbxNode* pNode) { + FbxMesh* pMesh = pNode->GetMesh(); + if (!pMesh) return nullptr; + std::vector bone_names; + std::vector poss; + std::vector transform_r0; + std::vector transform_r1; + std::vector transform_r2; + std::map parent_mapping; + if (pMesh->GetDeformerCount(FbxDeformer::eSkin)) { + FbxSkin* pSkin = (FbxSkin*)pMesh->GetDeformer(0, FbxDeformer::eSkin); + // Iterate over each cluster (bone) + for (int j = 0; j < pSkin->GetClusterCount(); ++j) { + FbxCluster* pCluster = pSkin->GetCluster(j); + + FbxNode* pBoneNode = pCluster->GetLink(); + if (!pBoneNode) continue; + FbxAMatrix transformLinkMatrix; + pCluster->GetTransformLinkMatrix(transformLinkMatrix); + + // The transformation of the mesh at binding time + FbxAMatrix transformMatrix; + pCluster->GetTransformMatrix(transformMatrix); + + // Inverse bind matrix. + FbxAMatrix bindMatrix_ = transformMatrix.Inverse() * transformLinkMatrix; + auto bindMatrix = bit_cast(bindMatrix_); + auto t = bindMatrix.GetRow(3); + poss.emplace_back(t[0], t[1], t[2]); + + auto r0 = bindMatrix.GetRow(0); + auto r1 = bindMatrix.GetRow(1); + auto r2 = bindMatrix.GetRow(2); + transform_r0.emplace_back(r0[0], r0[1], r0[2]); + transform_r1.emplace_back(r1[0], r1[1], r1[2]); + transform_r2.emplace_back(r2[0], r2[1], r2[2]); + std::string boneName = pBoneNode->GetName(); + bone_names.emplace_back(boneName); + auto pParentNode = pBoneNode->GetParent(); + if (pParentNode) { + std::string parentName = pParentNode->GetName(); + parent_mapping[boneName] = parentName; + } + } + } + std::string nodeName = pNode->GetName(); + auto prim = std::make_shared(); + prim->userData().set2("RootName", nodeName); + prim->verts.resize(bone_names.size()); + prim->verts.values = poss; + prim->verts.add_attr("transform_r0") = transform_r0; + prim->verts.add_attr("transform_r1") = transform_r1; + prim->verts.add_attr("transform_r2") = transform_r2; + std::vector bone_connects; + for (auto bone_name: bone_names) { + if (parent_mapping.count(bone_name)) { + auto parent_name = parent_mapping[bone_name]; + if (std::count(bone_names.begin(), bone_names.end(), parent_name)) { + auto self_index = std::find(bone_names.begin(), bone_names.end(), bone_name) - bone_names.begin(); + auto parent_index = std::find(bone_names.begin(), bone_names.end(), parent_name) - bone_names.begin(); + bone_connects.push_back(parent_index); + bone_connects.push_back(self_index); + } + } + } + prim->loops.values = bone_connects; + prim->polys.resize(bone_connects.size() / 2); + for (auto j = 0; j < bone_connects.size() / 2; j++) { + prim->polys[j] = {j * 2, 2}; + } + auto &boneNames = prim->verts.add_attr("boneName"); + std::iota(boneNames.begin(), boneNames.end(), 0); + prim->userData().set2("boneName_count", int(bone_names.size())); + for (auto i = 0; i < bone_names.size(); i++) { + prim->userData().set2(zeno::format("boneName_{}", i), bone_names[i]); + } + return prim; +} + +static void TraverseNodesToGetNames(FbxNode* pNode, std::vector &names) { if (!pNode) return; FbxMesh* mesh = pNode->GetMesh(); @@ -609,7 +690,7 @@ void TraverseNodesToGetNames(FbxNode* pNode, std::vector &names) { } } -void TraverseNodesToGetPrim(FbxNode* pNode, std::string target_name, std::shared_ptr &prim) { +static void TraverseNodesToGetPrim(FbxNode* pNode, std::string target_name, std::shared_ptr &prim) { if (!pNode) return; FbxMesh* mesh = pNode->GetMesh(); @@ -628,7 +709,7 @@ void TraverseNodesToGetPrim(FbxNode* pNode, std::string target_name, std::shared TraverseNodesToGetPrim(pNode->GetChild(i), target_name, prim); } } -void TraverseNodesToGetPrims(FbxNode* pNode, std::vector> &prims) { +static void TraverseNodesToGetPrims(FbxNode* pNode, std::vector> &prims) { if (!pNode) return; FbxMesh* mesh = pNode->GetMesh(); @@ -758,101 +839,206 @@ ZENDEFNODE(NewFBXImportSkin, { {"primitive"}, }); -struct NewFBXImportSkeleton : INode { - virtual void apply() override { - auto fbx_object = get_input2("fbx_object"); - auto lSdkManager = fbx_object->lSdkManager; - auto lScene = fbx_object->lScene; +static int GetSkeletonFromBindPose(FbxManager* lSdkManager, FbxScene* lScene, std::shared_ptr& prim) { + auto pose_count = lScene->GetPoseCount(); + bool found_bind_pose = false; + for (auto i = 0; i < pose_count; i++) { + auto pose = lScene->GetPose(i); + if (pose == nullptr || !pose->IsBindPose()) { + continue; + } + found_bind_pose = true; + } + if (found_bind_pose == false) { + lSdkManager->CreateMissingBindPoses(lScene); + } + pose_count = lScene->GetPoseCount(); + + std::vector bone_names; + std::map parent_mapping; + std::vector poss; + std::vector transform_r0; + std::vector transform_r1; + std::vector transform_r2; + for (auto i = 0; i < pose_count; i++) { + auto pose = lScene->GetPose(i); + if (pose == nullptr || !pose->IsBindPose()) { + continue; + } + for (int j = 1; j < pose->GetCount(); ++j) { + std::string bone_name = pose->GetNode(j)->GetName(); + if (std::count(bone_names.begin(), bone_names.end(), bone_name)) { + continue; + } - // Print the nodes of the scene and their attributes recursively. - // Note that we are not printing the root node because it should - // not contain any attributes. - auto prim = std::make_shared(); + FbxMatrix transformMatrix = pose->GetMatrix(j); + auto t = transformMatrix.GetRow(3); + poss.emplace_back(t[0], t[1], t[2]); + + auto r0 = transformMatrix.GetRow(0); + auto r1 = transformMatrix.GetRow(1); + auto r2 = transformMatrix.GetRow(2); + transform_r0.emplace_back(r0[0], r0[1], r0[2]); + transform_r1.emplace_back(r1[0], r1[1], r1[2]); + transform_r2.emplace_back(r2[0], r2[1], r2[2]); + + bone_names.emplace_back(pose->GetNode(j)->GetName()); + } + for (int j = 1; j < pose->GetCount(); ++j) { + auto self_name = pose->GetNode(j)->GetName(); + auto parent = pose->GetNode(j)->GetParent(); + if (parent) { + auto parent_name = parent->GetName(); + parent_mapping[self_name] = parent_name; + } + } + } + { + prim->verts.resize(bone_names.size()); + prim->verts.values = poss; + prim->verts.add_attr("transform_r0") = transform_r0; + prim->verts.add_attr("transform_r1") = transform_r1; + prim->verts.add_attr("transform_r2") = transform_r2; + auto &boneNames = prim->verts.add_attr("boneName"); + std::iota(boneNames.begin(), boneNames.end(), 0); - auto pose_count = lScene->GetPoseCount(); - bool found_bind_pose = false; - for (auto i = 0; i < pose_count; i++) { - auto pose = lScene->GetPose(i); - if (pose == nullptr || !pose->IsBindPose()) { - continue; + std::vector bone_connects; + for (auto bone_name: bone_names) { + if (parent_mapping.count(bone_name)) { + auto parent_name = parent_mapping[bone_name]; + if (std::count(bone_names.begin(), bone_names.end(), parent_name)) { + auto self_index = std::find(bone_names.begin(), bone_names.end(), bone_name) - bone_names.begin(); + auto parent_index = std::find(bone_names.begin(), bone_names.end(), parent_name) - bone_names.begin(); + bone_connects.push_back(parent_index); + bone_connects.push_back(self_index); + } } - found_bind_pose = true; } - if (found_bind_pose == false) { - lSdkManager->CreateMissingBindPoses(lScene); + prim->loops.values = bone_connects; + prim->polys.resize(bone_connects.size() / 2); + for (auto j = 0; j < bone_connects.size() / 2; j++) { + prim->polys[j] = {j * 2, 2}; } - pose_count = lScene->GetPoseCount(); + prim->userData().set2("boneName_count", int(bone_names.size())); + for (auto i = 0; i < bone_names.size(); i++) { + prim->userData().set2(zeno::format("boneName_{}", i), bone_names[i]); + } + } +} + +static void TraverseNodesToGetSkeleton(FbxNode* pNode, std::vector &bone_names, std::vector &transforms, std::map &parent_mapping) { + if (!pNode) return; + + FbxMesh* pMesh = pNode->GetMesh(); + if (pMesh && pMesh->GetDeformerCount(FbxDeformer::eSkin)) { + FbxSkin* pSkin = (FbxSkin*)pMesh->GetDeformer(0, FbxDeformer::eSkin); + // Iterate over each cluster (bone) + for (int j = 0; j < pSkin->GetClusterCount(); ++j) { + FbxCluster* pCluster = pSkin->GetCluster(j); + + FbxNode* pBoneNode = pCluster->GetLink(); + if (!pBoneNode) continue; + std::string boneName = pBoneNode->GetName(); + if (std::count(bone_names.begin(), bone_names.end(), boneName)) { + continue; + } + bone_names.emplace_back(boneName); + FbxAMatrix transformLinkMatrix; + pCluster->GetTransformLinkMatrix(transformLinkMatrix); + + // The transformation of the mesh at binding time + FbxAMatrix transformMatrix; + pCluster->GetTransformMatrix(transformMatrix); + + // Inverse bind matrix. + FbxAMatrix bindMatrix_ = transformMatrix.Inverse() * transformLinkMatrix; + auto bindMatrix = bit_cast(bindMatrix_); + transforms.emplace_back(bindMatrix); + + auto pParentNode = pBoneNode->GetParent(); + if (pParentNode) { + std::string parentName = pParentNode->GetName(); + parent_mapping[boneName] = parentName; + } + } + } + + for (int i = 0; i < pNode->GetChildCount(); i++) { + TraverseNodesToGetSkeleton(pNode->GetChild(i), bone_names, transforms, parent_mapping); + } +} +std::shared_ptr GetSkeletonFromMesh(FbxScene* lScene) { + auto prim = std::make_shared(); + + FbxNode* lRootNode = lScene->GetRootNode(); + if (lRootNode) { std::vector bone_names; + std::vector transforms; std::map parent_mapping; + TraverseNodesToGetSkeleton(lRootNode, bone_names, transforms, parent_mapping); std::vector poss; std::vector transform_r0; std::vector transform_r1; std::vector transform_r2; - for (auto i = 0; i < pose_count; i++) { - auto pose = lScene->GetPose(i); - if (pose == nullptr || !pose->IsBindPose()) { - continue; - } - for (int j = 1; j < pose->GetCount(); ++j) { - std::string bone_name = pose->GetNode(j)->GetName(); - if (std::count(bone_names.begin(), bone_names.end(), bone_name)) { - continue; - } - - FbxMatrix transformMatrix = pose->GetMatrix(j); - auto t = transformMatrix.GetRow(3); - poss.emplace_back(t[0], t[1], t[2]); - - auto r0 = transformMatrix.GetRow(0); - auto r1 = transformMatrix.GetRow(1); - auto r2 = transformMatrix.GetRow(2); - transform_r0.emplace_back(r0[0], r0[1], r0[2]); - transform_r1.emplace_back(r1[0], r1[1], r1[2]); - transform_r2.emplace_back(r2[0], r2[1], r2[2]); - - bone_names.emplace_back(pose->GetNode(j)->GetName()); - } - for (int j = 1; j < pose->GetCount(); ++j) { - auto self_name = pose->GetNode(j)->GetName(); - auto parent = pose->GetNode(j)->GetParent(); - if (parent) { - auto parent_name = parent->GetName(); - parent_mapping[self_name] = parent_name; + for (auto i = 0; i < bone_names.size(); i++) { + auto bone_name = bone_names[i]; + auto bindMatrix = transforms[i]; + auto t = bindMatrix.GetRow(3); + poss.emplace_back(t[0], t[1], t[2]); + + auto r0 = bindMatrix.GetRow(0); + auto r1 = bindMatrix.GetRow(1); + auto r2 = bindMatrix.GetRow(2); + transform_r0.emplace_back(r0[0], r0[1], r0[2]); + transform_r1.emplace_back(r1[0], r1[1], r1[2]); + transform_r2.emplace_back(r2[0], r2[1], r2[2]); + } + prim->verts.resize(bone_names.size()); + prim->verts.values = poss; + prim->verts.add_attr("transform_r0") = transform_r0; + prim->verts.add_attr("transform_r1") = transform_r1; + prim->verts.add_attr("transform_r2") = transform_r2; + std::vector bone_connects; + for (auto bone_name: bone_names) { + if (parent_mapping.count(bone_name)) { + auto parent_name = parent_mapping[bone_name]; + if (std::count(bone_names.begin(), bone_names.end(), parent_name)) { + auto self_index = std::find(bone_names.begin(), bone_names.end(), bone_name) - bone_names.begin(); + auto parent_index = std::find(bone_names.begin(), bone_names.end(), parent_name) - bone_names.begin(); + bone_connects.push_back(parent_index); + bone_connects.push_back(self_index); } } } - { - prim->verts.resize(bone_names.size()); - prim->verts.values = poss; - prim->verts.add_attr("transform_r0") = transform_r0; - prim->verts.add_attr("transform_r1") = transform_r1; - prim->verts.add_attr("transform_r2") = transform_r2; - auto &boneNames = prim->verts.add_attr("boneName"); - std::iota(boneNames.begin(), boneNames.end(), 0); + prim->loops.values = bone_connects; + prim->polys.resize(bone_connects.size() / 2); + for (auto j = 0; j < bone_connects.size() / 2; j++) { + prim->polys[j] = {j * 2, 2}; + } + auto &boneNames = prim->verts.add_attr("boneName"); + std::iota(boneNames.begin(), boneNames.end(), 0); + prim->userData().set2("boneName_count", int(bone_names.size())); + for (auto i = 0; i < bone_names.size(); i++) { + prim->userData().set2(zeno::format("boneName_{}", i), bone_names[i]); + } + } + return prim; +} +struct NewFBXImportSkeleton : INode { + virtual void apply() override { + auto fbx_object = get_input2("fbx_object"); + auto lSdkManager = fbx_object->lSdkManager; + auto lScene = fbx_object->lScene; - std::vector bone_connects; - for (auto bone_name: bone_names) { - if (parent_mapping.count(bone_name)) { - auto parent_name = parent_mapping[bone_name]; - if (std::count(bone_names.begin(), bone_names.end(), parent_name)) { - auto self_index = std::find(bone_names.begin(), bone_names.end(), bone_name) - bone_names.begin(); - auto parent_index = std::find(bone_names.begin(), bone_names.end(), parent_name) - bone_names.begin(); - bone_connects.push_back(parent_index); - bone_connects.push_back(self_index); - } - } - } - prim->loops.values = bone_connects; - prim->polys.resize(bone_connects.size() / 2); - for (auto j = 0; j < bone_connects.size() / 2; j++) { - prim->polys[j] = {j * 2, 2}; - } + // Print the nodes of the scene and their attributes recursively. + // Note that we are not printing the root node because it should + // not contain any attributes. + auto prim = std::make_shared(); - prim->userData().set2("boneName_count", int(bone_names.size())); - for (auto i = 0; i < bone_names.size(); i++) { - prim->userData().set2(zeno::format("boneName_{}", i), bone_names[i]); - } + auto pose_count = GetSkeletonFromBindPose(lSdkManager, lScene, prim); + if (pose_count == 0 || get_input2("ForceFromMesh")) { + prim = GetSkeletonFromMesh(lScene); } if (get_input2("ConvertUnits")) { @@ -876,6 +1062,7 @@ ZENDEFNODE(NewFBXImportSkeleton, { { "fbx_object", {"bool", "ConvertUnits", "0"}, + {"bool", "ForceFromMesh", "0"}, }, { "prim",