284 lines
12 KiB
C#
284 lines
12 KiB
C#
// ***********************************************************************
|
|
// Copyright (c) 2017 Unity Technologies. All rights reserved.
|
|
//
|
|
// Licensed under the ##LICENSENAME##.
|
|
// See LICENSE.md file in the project root for full license information.
|
|
// ***********************************************************************
|
|
using NUnit.Framework;
|
|
using System.Collections.Generic;
|
|
using Autodesk.Fbx;
|
|
|
|
namespace Autodesk.Fbx.UseCaseTests
|
|
{
|
|
internal class SkinnedMeshExportTest : StaticMeshExportTest
|
|
{
|
|
[SetUp]
|
|
public override void Init ()
|
|
{
|
|
fileNamePrefix = "_safe_to_delete__skinned_mesh_export_test";
|
|
base.Init ();
|
|
}
|
|
|
|
protected override FbxScene CreateScene (FbxManager manager)
|
|
{
|
|
// Add a skeleton to the cube that we created in StaticMeshExportTest
|
|
FbxScene scene = base.CreateScene (manager);
|
|
|
|
FbxNode meshNode = scene.GetRootNode().GetChild(0);
|
|
FbxNode skeletonRootNode = CreateSkeleton(scene);
|
|
|
|
FbxNode rootNode = scene.GetRootNode ();
|
|
rootNode.AddChild (skeletonRootNode);
|
|
|
|
LinkMeshToSkeleton (scene, meshNode, skeletonRootNode);
|
|
|
|
FbxNode limb1 = skeletonRootNode.GetChild (0);
|
|
FbxNode limb2 = limb1.GetChild (0);
|
|
|
|
ExportBindPose (meshNode, scene, new List<FbxNode> (){ skeletonRootNode, limb1, limb2 });
|
|
|
|
return scene;
|
|
}
|
|
|
|
protected FbxNode CreateSkeleton(FbxScene scene)
|
|
{
|
|
FbxSkeleton skelRoot = FbxSkeleton.Create (scene, "SkelRoot");
|
|
skelRoot.SetSkeletonType (FbxSkeleton.EType.eRoot);
|
|
FbxNode skelRootNode = FbxNode.Create (scene, "SkelRootNode");
|
|
skelRootNode.SetNodeAttribute (skelRoot);
|
|
skelRootNode.LclTranslation.Set(new FbxDouble3(0.0, -40.0, 0.0));
|
|
|
|
// create skeleton limb nodes
|
|
FbxSkeleton skelLimb1 = FbxSkeleton.Create(scene, "SkelLimb1");
|
|
skelLimb1.SetSkeletonType (FbxSkeleton.EType.eLimbNode);
|
|
skelLimb1.Size.Set (1.5);
|
|
FbxNode skelLimbNode1 = FbxNode.Create (scene, "SkelLimbNode1");
|
|
skelLimbNode1.SetNodeAttribute (skelLimb1);
|
|
skelLimbNode1.LclTranslation.Set (new FbxDouble3 (0.0, 40.0, 0.0));
|
|
|
|
FbxSkeleton skelLimb2 = FbxSkeleton.Create(scene, "SkelLimb2");
|
|
skelLimb2.SetSkeletonType (FbxSkeleton.EType.eLimbNode);
|
|
skelLimb2.Size.Set (1.5);
|
|
FbxNode skelLimbNode2 = FbxNode.Create (scene, "SkelLimbNode2");
|
|
skelLimbNode2.SetNodeAttribute (skelLimb2);
|
|
skelLimbNode2.LclTranslation.Set (new FbxDouble3 (0.0, 40.0, 0.0));
|
|
|
|
// build skeleton hierarchy
|
|
skelRootNode.AddChild (skelLimbNode1);
|
|
skelLimbNode1.AddChild (skelLimbNode2);
|
|
|
|
return skelRootNode;
|
|
}
|
|
|
|
protected void LinkMeshToSkeleton(FbxScene scene, FbxNode meshNode, FbxNode skelRootNode)
|
|
{
|
|
FbxNode limb1 = skelRootNode.GetChild (0);
|
|
FbxNode limb2 = limb1.GetChild (0);
|
|
|
|
FbxCluster rootCluster = FbxCluster.Create (scene, "RootCluster");
|
|
rootCluster.SetLink (skelRootNode);
|
|
rootCluster.SetLinkMode (FbxCluster.ELinkMode.eTotalOne);
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
rootCluster.AddControlPointIndex (4 * i + j, 1.0 - 0.25 * i);
|
|
}
|
|
}
|
|
|
|
FbxCluster limb1Cluster = FbxCluster.Create (scene, "Limb1Cluster");
|
|
limb1Cluster.SetLink (limb1);
|
|
limb1Cluster.SetLinkMode (FbxCluster.ELinkMode.eTotalOne);
|
|
for (int i = 1; i < 6; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
limb1Cluster.AddControlPointIndex (4 * i + j, (i == 1 || i == 5 ? 0.25 : 0.5));
|
|
}
|
|
}
|
|
|
|
FbxCluster limb2Cluster = FbxCluster.Create (scene, "Limb2Cluster");
|
|
limb2Cluster.SetLink (limb2);
|
|
limb2Cluster.SetLinkMode (FbxCluster.ELinkMode.eTotalOne);
|
|
for (int i = 3; i < 7; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
limb2Cluster.AddControlPointIndex (4 * i + j, 0.25 * (i - 2));
|
|
}
|
|
}
|
|
|
|
FbxAMatrix globalTransform = meshNode.EvaluateGlobalTransform ();
|
|
|
|
rootCluster.SetTransformMatrix (globalTransform);
|
|
limb1Cluster.SetTransformMatrix (globalTransform);
|
|
limb2Cluster.SetTransformMatrix (globalTransform);
|
|
|
|
rootCluster.SetTransformLinkMatrix (skelRootNode.EvaluateGlobalTransform ());
|
|
limb1Cluster.SetTransformLinkMatrix (limb1.EvaluateGlobalTransform ());
|
|
limb2Cluster.SetTransformLinkMatrix (limb2.EvaluateGlobalTransform ());
|
|
|
|
FbxSkin skin = FbxSkin.Create (scene, "Skin");
|
|
skin.AddCluster (rootCluster);
|
|
skin.AddCluster (limb1Cluster);
|
|
skin.AddCluster (limb2Cluster);
|
|
meshNode.GetMesh ().AddDeformer (skin);
|
|
}
|
|
|
|
protected void ExportBindPose (FbxNode meshNode, FbxScene fbxScene, List<FbxNode> boneNodes)
|
|
{
|
|
FbxPose fbxPose = FbxPose.Create (fbxScene, "Pose");
|
|
|
|
// set as bind pose
|
|
fbxPose.SetIsBindPose (true);
|
|
|
|
// assume each bone node has one weighted vertex cluster
|
|
foreach (FbxNode fbxNode in boneNodes)
|
|
{
|
|
// EvaluateGlobalTransform returns an FbxAMatrix (affine matrix)
|
|
// which has to be converted to an FbxMatrix so that it can be passed to fbxPose.Add().
|
|
// The hierarchy for FbxMatrix and FbxAMatrix is as follows:
|
|
//
|
|
// FbxDouble4x4
|
|
// / \
|
|
// FbxMatrix FbxAMatrix
|
|
//
|
|
// Therefore we can't convert directly from FbxAMatrix to FbxMatrix,
|
|
// however FbxMatrix has a constructor that takes an FbxAMatrix.
|
|
FbxMatrix fbxBindMatrix = new FbxMatrix(fbxNode.EvaluateGlobalTransform ());
|
|
|
|
fbxPose.Add (fbxNode, fbxBindMatrix);
|
|
}
|
|
|
|
FbxMatrix bindMatrix = new FbxMatrix(meshNode.EvaluateGlobalTransform ());
|
|
|
|
fbxPose.Add (meshNode, bindMatrix);
|
|
|
|
// add the pose to the scene
|
|
fbxScene.AddPose (fbxPose);
|
|
}
|
|
|
|
protected override void CheckScene (FbxScene scene)
|
|
{
|
|
base.CheckScene (scene);
|
|
|
|
FbxScene origScene = CreateScene (FbxManager);
|
|
|
|
Assert.AreEqual (origScene.GetRootNode ().GetChildCount (), origScene.GetRootNode ().GetChildCount ());
|
|
|
|
FbxNode origMeshNode = origScene.GetRootNode ().GetChild (0);
|
|
FbxNode importMeshNode = scene.GetRootNode ().GetChild (0);
|
|
|
|
FbxNode origSkelRootNode = origScene.GetRootNode ().GetChild (1);
|
|
FbxNode importSkelRootNode = scene.GetRootNode ().GetChild (1);
|
|
|
|
// Check that the skeletons match
|
|
CheckSkeleton(origSkelRootNode, importSkelRootNode);
|
|
|
|
// TODO: Fix so that calling GetDeformer either allows us to downcast
|
|
// to FbxSkin, or so that it just returns the correct type.
|
|
// Check that the mesh is correctly linked to the skeleton
|
|
//CheckMeshLinkedToSkeleton(origMeshNode.GetMesh(), importMeshNode.GetMesh());
|
|
origMeshNode.GetMesh();
|
|
importMeshNode.GetMesh ();
|
|
|
|
// Check that bind pose is set correctly
|
|
CheckExportBindPose(origScene, scene);
|
|
}
|
|
|
|
struct SkelNodePair {
|
|
public FbxNode origNode;
|
|
public FbxNode importNode;
|
|
public bool isRoot;
|
|
|
|
public SkelNodePair(FbxNode origNode, FbxNode importNode, bool isRoot = false){
|
|
this.origNode = origNode;
|
|
this.importNode = importNode;
|
|
this.isRoot = isRoot;
|
|
}
|
|
}
|
|
|
|
protected void CheckSkeleton(FbxNode origRootNode, FbxNode importRootNode)
|
|
{
|
|
FbxNode origLimb1Node = origRootNode.GetChild (0);
|
|
FbxNode importLimb1Node = importRootNode.GetChild (0);
|
|
|
|
FbxNode origLimb2Node = origLimb1Node.GetChild (0);
|
|
FbxNode importLimb2Node = importLimb1Node.GetChild (0);
|
|
|
|
foreach (var skelNodePair in new SkelNodePair[]{ new SkelNodePair(origRootNode, importRootNode, true),
|
|
new SkelNodePair(origLimb1Node, importLimb1Node), new SkelNodePair(origLimb2Node, importLimb2Node) }) {
|
|
FbxSkeleton origSkel = skelNodePair.origNode.GetSkeleton ();
|
|
FbxSkeleton importSkel = skelNodePair.importNode.GetSkeleton ();
|
|
|
|
Assert.IsNotNull (origSkel);
|
|
Assert.IsNotNull (importSkel);
|
|
|
|
Assert.AreEqual (origSkel.GetName (), importSkel.GetName ());
|
|
Assert.AreEqual (origSkel.GetSkeletonType (), importSkel.GetSkeletonType ());
|
|
Assert.AreEqual (skelNodePair.origNode.LclTranslation.Get (), skelNodePair.importNode.LclTranslation.Get ());
|
|
|
|
if (!skelNodePair.isRoot) {
|
|
Assert.AreEqual (origSkel.Size.Get (), importSkel.Size.Get ());
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ClusterPair {
|
|
public FbxCluster orig;
|
|
public FbxCluster import;
|
|
|
|
public ClusterPair(FbxCluster orig, FbxCluster import){
|
|
this.orig = orig;
|
|
this.import = import;
|
|
}
|
|
}
|
|
|
|
protected void CheckMeshLinkedToSkeleton(FbxMesh origMesh, FbxMesh importMesh)
|
|
{
|
|
FbxSkin origSkin = origMesh.GetDeformer (0, new FbxStatus()) as FbxSkin;
|
|
Assert.IsNotNull (origSkin);
|
|
|
|
FbxSkin importSkin = origMesh.GetDeformer (0, new FbxStatus()) as FbxSkin;
|
|
Assert.IsNotNull (importSkin);
|
|
|
|
ClusterPair[] clusters = new ClusterPair[3];
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
FbxCluster origCluster = origSkin.GetCluster (i);
|
|
Assert.IsNotNull (origCluster);
|
|
|
|
FbxCluster importCluster = importSkin.GetCluster (i);
|
|
Assert.IsNotNull (importCluster);
|
|
|
|
clusters [i] = new ClusterPair (origCluster, importCluster);
|
|
}
|
|
|
|
foreach (var c in clusters) {
|
|
FbxAMatrix origTransformMatrix = null;
|
|
FbxAMatrix importTransformMatrix = null;
|
|
Assert.AreEqual (c.orig.GetTransformMatrix (origTransformMatrix), c.import.GetTransformMatrix (importTransformMatrix));
|
|
Assert.AreEqual (c.orig.GetTransformLinkMatrix (origTransformMatrix), c.import.GetTransformLinkMatrix (importTransformMatrix));
|
|
|
|
Assert.AreEqual (c.orig.GetLink (), c.import.GetLink ());
|
|
Assert.AreEqual (c.orig.GetLinkMode (), c.import.GetLinkMode ());
|
|
Assert.AreEqual (c.orig.GetControlPointIndicesCount (), c.import.GetControlPointIndicesCount ());
|
|
|
|
for (int i = 0; i < c.orig.GetControlPointIndicesCount (); i++) {
|
|
Assert.AreEqual (c.orig.GetControlPointIndexAt (i), c.import.GetControlPointIndexAt (i));
|
|
Assert.AreEqual (c.orig.GetControlPointWeightAt (i), c.import.GetControlPointWeightAt (i));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void CheckExportBindPose(FbxScene origScene, FbxScene importScene)
|
|
{
|
|
FbxPose origPose = origScene.GetPose (0);
|
|
FbxPose importPose = importScene.GetPose (0);
|
|
|
|
Assert.IsNotNull (origPose);
|
|
Assert.IsNotNull (importPose);
|
|
Assert.AreEqual (origPose.IsBindPose (), importPose.IsBindPose ());
|
|
Assert.AreEqual (origPose.GetCount (), importPose.GetCount ());
|
|
|
|
for (int i = 0; i < origPose.GetCount (); i++) {
|
|
Assert.AreEqual (origPose.GetNode (i).GetName (), importPose.GetNode (i).GetName ());
|
|
Assert.AreEqual (origPose.GetMatrix (i), importPose.GetMatrix (i));
|
|
}
|
|
}
|
|
}
|
|
} |