SkinnedMeshExportTest.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // ***********************************************************************
  2. // Copyright (c) 2017 Unity Technologies. All rights reserved.
  3. //
  4. // Licensed under the ##LICENSENAME##.
  5. // See LICENSE.md file in the project root for full license information.
  6. // ***********************************************************************
  7. using NUnit.Framework;
  8. using System.Collections.Generic;
  9. using Autodesk.Fbx;
  10. namespace Autodesk.Fbx.UseCaseTests
  11. {
  12. public class SkinnedMeshExportTest : StaticMeshExportTest
  13. {
  14. [SetUp]
  15. public override void Init ()
  16. {
  17. fileNamePrefix = "_safe_to_delete__skinned_mesh_export_test";
  18. base.Init ();
  19. }
  20. protected override FbxScene CreateScene (FbxManager manager)
  21. {
  22. // Add a skeleton to the cube that we created in StaticMeshExportTest
  23. FbxScene scene = base.CreateScene (manager);
  24. FbxNode meshNode = scene.GetRootNode().GetChild(0);
  25. FbxNode skeletonRootNode = CreateSkeleton(scene);
  26. FbxNode rootNode = scene.GetRootNode ();
  27. rootNode.AddChild (skeletonRootNode);
  28. LinkMeshToSkeleton (scene, meshNode, skeletonRootNode);
  29. FbxNode limb1 = skeletonRootNode.GetChild (0);
  30. FbxNode limb2 = limb1.GetChild (0);
  31. ExportBindPose (meshNode, scene, new List<FbxNode> (){ skeletonRootNode, limb1, limb2 });
  32. return scene;
  33. }
  34. protected FbxNode CreateSkeleton(FbxScene scene)
  35. {
  36. FbxSkeleton skelRoot = FbxSkeleton.Create (scene, "SkelRoot");
  37. skelRoot.SetSkeletonType (FbxSkeleton.EType.eRoot);
  38. FbxNode skelRootNode = FbxNode.Create (scene, "SkelRootNode");
  39. skelRootNode.SetNodeAttribute (skelRoot);
  40. skelRootNode.LclTranslation.Set(new FbxDouble3(0.0, -40.0, 0.0));
  41. // create skeleton limb nodes
  42. FbxSkeleton skelLimb1 = FbxSkeleton.Create(scene, "SkelLimb1");
  43. skelLimb1.SetSkeletonType (FbxSkeleton.EType.eLimbNode);
  44. skelLimb1.Size.Set (1.5);
  45. FbxNode skelLimbNode1 = FbxNode.Create (scene, "SkelLimbNode1");
  46. skelLimbNode1.SetNodeAttribute (skelLimb1);
  47. skelLimbNode1.LclTranslation.Set (new FbxDouble3 (0.0, 40.0, 0.0));
  48. FbxSkeleton skelLimb2 = FbxSkeleton.Create(scene, "SkelLimb2");
  49. skelLimb2.SetSkeletonType (FbxSkeleton.EType.eLimbNode);
  50. skelLimb2.Size.Set (1.5);
  51. FbxNode skelLimbNode2 = FbxNode.Create (scene, "SkelLimbNode2");
  52. skelLimbNode2.SetNodeAttribute (skelLimb2);
  53. skelLimbNode2.LclTranslation.Set (new FbxDouble3 (0.0, 40.0, 0.0));
  54. // build skeleton hierarchy
  55. skelRootNode.AddChild (skelLimbNode1);
  56. skelLimbNode1.AddChild (skelLimbNode2);
  57. return skelRootNode;
  58. }
  59. protected void LinkMeshToSkeleton(FbxScene scene, FbxNode meshNode, FbxNode skelRootNode)
  60. {
  61. FbxNode limb1 = skelRootNode.GetChild (0);
  62. FbxNode limb2 = limb1.GetChild (0);
  63. FbxCluster rootCluster = FbxCluster.Create (scene, "RootCluster");
  64. rootCluster.SetLink (skelRootNode);
  65. rootCluster.SetLinkMode (FbxCluster.ELinkMode.eTotalOne);
  66. for (int i = 0; i < 4; i++) {
  67. for (int j = 0; j < 4; j++) {
  68. rootCluster.AddControlPointIndex (4 * i + j, 1.0 - 0.25 * i);
  69. }
  70. }
  71. FbxCluster limb1Cluster = FbxCluster.Create (scene, "Limb1Cluster");
  72. limb1Cluster.SetLink (limb1);
  73. limb1Cluster.SetLinkMode (FbxCluster.ELinkMode.eTotalOne);
  74. for (int i = 1; i < 6; i++) {
  75. for (int j = 0; j < 4; j++) {
  76. limb1Cluster.AddControlPointIndex (4 * i + j, (i == 1 || i == 5 ? 0.25 : 0.5));
  77. }
  78. }
  79. FbxCluster limb2Cluster = FbxCluster.Create (scene, "Limb2Cluster");
  80. limb2Cluster.SetLink (limb2);
  81. limb2Cluster.SetLinkMode (FbxCluster.ELinkMode.eTotalOne);
  82. for (int i = 3; i < 7; i++) {
  83. for (int j = 0; j < 4; j++) {
  84. limb2Cluster.AddControlPointIndex (4 * i + j, 0.25 * (i - 2));
  85. }
  86. }
  87. FbxAMatrix globalTransform = meshNode.EvaluateGlobalTransform ();
  88. rootCluster.SetTransformMatrix (globalTransform);
  89. limb1Cluster.SetTransformMatrix (globalTransform);
  90. limb2Cluster.SetTransformMatrix (globalTransform);
  91. rootCluster.SetTransformLinkMatrix (skelRootNode.EvaluateGlobalTransform ());
  92. limb1Cluster.SetTransformLinkMatrix (limb1.EvaluateGlobalTransform ());
  93. limb2Cluster.SetTransformLinkMatrix (limb2.EvaluateGlobalTransform ());
  94. FbxSkin skin = FbxSkin.Create (scene, "Skin");
  95. skin.AddCluster (rootCluster);
  96. skin.AddCluster (limb1Cluster);
  97. skin.AddCluster (limb2Cluster);
  98. meshNode.GetMesh ().AddDeformer (skin);
  99. }
  100. protected void ExportBindPose (FbxNode meshNode, FbxScene fbxScene, List<FbxNode> boneNodes)
  101. {
  102. FbxPose fbxPose = FbxPose.Create (fbxScene, "Pose");
  103. // set as bind pose
  104. fbxPose.SetIsBindPose (true);
  105. // assume each bone node has one weighted vertex cluster
  106. foreach (FbxNode fbxNode in boneNodes)
  107. {
  108. // EvaluateGlobalTransform returns an FbxAMatrix (affine matrix)
  109. // which has to be converted to an FbxMatrix so that it can be passed to fbxPose.Add().
  110. // The hierarchy for FbxMatrix and FbxAMatrix is as follows:
  111. //
  112. // FbxDouble4x4
  113. // / \
  114. // FbxMatrix FbxAMatrix
  115. //
  116. // Therefore we can't convert directly from FbxAMatrix to FbxMatrix,
  117. // however FbxMatrix has a constructor that takes an FbxAMatrix.
  118. FbxMatrix fbxBindMatrix = new FbxMatrix(fbxNode.EvaluateGlobalTransform ());
  119. fbxPose.Add (fbxNode, fbxBindMatrix);
  120. }
  121. FbxMatrix bindMatrix = new FbxMatrix(meshNode.EvaluateGlobalTransform ());
  122. fbxPose.Add (meshNode, bindMatrix);
  123. // add the pose to the scene
  124. fbxScene.AddPose (fbxPose);
  125. }
  126. protected override void CheckScene (FbxScene scene)
  127. {
  128. base.CheckScene (scene);
  129. FbxScene origScene = CreateScene (FbxManager);
  130. Assert.AreEqual (origScene.GetRootNode ().GetChildCount (), origScene.GetRootNode ().GetChildCount ());
  131. FbxNode origMeshNode = origScene.GetRootNode ().GetChild (0);
  132. FbxNode importMeshNode = scene.GetRootNode ().GetChild (0);
  133. FbxNode origSkelRootNode = origScene.GetRootNode ().GetChild (1);
  134. FbxNode importSkelRootNode = scene.GetRootNode ().GetChild (1);
  135. // Check that the skeletons match
  136. CheckSkeleton(origSkelRootNode, importSkelRootNode);
  137. // TODO: Fix so that calling GetDeformer either allows us to downcast
  138. // to FbxSkin, or so that it just returns the correct type.
  139. // Check that the mesh is correctly linked to the skeleton
  140. //CheckMeshLinkedToSkeleton(origMeshNode.GetMesh(), importMeshNode.GetMesh());
  141. origMeshNode.GetMesh();
  142. importMeshNode.GetMesh ();
  143. // Check that bind pose is set correctly
  144. CheckExportBindPose(origScene, scene);
  145. }
  146. struct SkelNodePair {
  147. public FbxNode origNode;
  148. public FbxNode importNode;
  149. public bool isRoot;
  150. public SkelNodePair(FbxNode origNode, FbxNode importNode, bool isRoot = false){
  151. this.origNode = origNode;
  152. this.importNode = importNode;
  153. this.isRoot = isRoot;
  154. }
  155. }
  156. protected void CheckSkeleton(FbxNode origRootNode, FbxNode importRootNode)
  157. {
  158. FbxNode origLimb1Node = origRootNode.GetChild (0);
  159. FbxNode importLimb1Node = importRootNode.GetChild (0);
  160. FbxNode origLimb2Node = origLimb1Node.GetChild (0);
  161. FbxNode importLimb2Node = importLimb1Node.GetChild (0);
  162. foreach (var skelNodePair in new SkelNodePair[]{ new SkelNodePair(origRootNode, importRootNode, true),
  163. new SkelNodePair(origLimb1Node, importLimb1Node), new SkelNodePair(origLimb2Node, importLimb2Node) }) {
  164. FbxSkeleton origSkel = skelNodePair.origNode.GetSkeleton ();
  165. FbxSkeleton importSkel = skelNodePair.importNode.GetSkeleton ();
  166. Assert.IsNotNull (origSkel);
  167. Assert.IsNotNull (importSkel);
  168. Assert.AreEqual (origSkel.GetName (), importSkel.GetName ());
  169. Assert.AreEqual (origSkel.GetSkeletonType (), importSkel.GetSkeletonType ());
  170. Assert.AreEqual (skelNodePair.origNode.LclTranslation.Get (), skelNodePair.importNode.LclTranslation.Get ());
  171. if (!skelNodePair.isRoot) {
  172. Assert.AreEqual (origSkel.Size.Get (), importSkel.Size.Get ());
  173. }
  174. }
  175. }
  176. struct ClusterPair {
  177. public FbxCluster orig;
  178. public FbxCluster import;
  179. public ClusterPair(FbxCluster orig, FbxCluster import){
  180. this.orig = orig;
  181. this.import = import;
  182. }
  183. }
  184. protected void CheckMeshLinkedToSkeleton(FbxMesh origMesh, FbxMesh importMesh)
  185. {
  186. FbxSkin origSkin = origMesh.GetDeformer (0, new FbxStatus()) as FbxSkin;
  187. Assert.IsNotNull (origSkin);
  188. FbxSkin importSkin = origMesh.GetDeformer (0, new FbxStatus()) as FbxSkin;
  189. Assert.IsNotNull (importSkin);
  190. ClusterPair[] clusters = new ClusterPair[3];
  191. for (int i = 0; i < 3; i++) {
  192. FbxCluster origCluster = origSkin.GetCluster (i);
  193. Assert.IsNotNull (origCluster);
  194. FbxCluster importCluster = importSkin.GetCluster (i);
  195. Assert.IsNotNull (importCluster);
  196. clusters [i] = new ClusterPair (origCluster, importCluster);
  197. }
  198. foreach (var c in clusters) {
  199. FbxAMatrix origTransformMatrix = null;
  200. FbxAMatrix importTransformMatrix = null;
  201. Assert.AreEqual (c.orig.GetTransformMatrix (origTransformMatrix), c.import.GetTransformMatrix (importTransformMatrix));
  202. Assert.AreEqual (c.orig.GetTransformLinkMatrix (origTransformMatrix), c.import.GetTransformLinkMatrix (importTransformMatrix));
  203. Assert.AreEqual (c.orig.GetLink (), c.import.GetLink ());
  204. Assert.AreEqual (c.orig.GetLinkMode (), c.import.GetLinkMode ());
  205. Assert.AreEqual (c.orig.GetControlPointIndicesCount (), c.import.GetControlPointIndicesCount ());
  206. for (int i = 0; i < c.orig.GetControlPointIndicesCount (); i++) {
  207. Assert.AreEqual (c.orig.GetControlPointIndexAt (i), c.import.GetControlPointIndexAt (i));
  208. Assert.AreEqual (c.orig.GetControlPointWeightAt (i), c.import.GetControlPointWeightAt (i));
  209. }
  210. }
  211. }
  212. protected void CheckExportBindPose(FbxScene origScene, FbxScene importScene)
  213. {
  214. FbxPose origPose = origScene.GetPose (0);
  215. FbxPose importPose = importScene.GetPose (0);
  216. Assert.IsNotNull (origPose);
  217. Assert.IsNotNull (importPose);
  218. Assert.AreEqual (origPose.IsBindPose (), importPose.IsBindPose ());
  219. Assert.AreEqual (origPose.GetCount (), importPose.GetCount ());
  220. for (int i = 0; i < origPose.GetCount (); i++) {
  221. Assert.AreEqual (origPose.GetNode (i).GetName (), importPose.GetNode (i).GetName ());
  222. Assert.AreEqual (origPose.GetMatrix (i), importPose.GetMatrix (i));
  223. }
  224. }
  225. }
  226. }