Skip to content

Commit c3479b2

Browse files
authored
Merge pull request #296 from Unity-Technologies/UNI-32844-export-animation-only
Uni 32844 export animation only
2 parents 853eb64 + e21e708 commit c3479b2

10 files changed

+3468
-170
lines changed

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 567 additions & 144 deletions
Large diffs are not rendered by default.

Assets/FbxExporters/Editor/UnitTests/FbxAnimationTest.cs

Lines changed: 150 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ public static IEnumerable BlendShapeTestCases {
9393
yield return "Models/blendshape_with_skinning.fbx";
9494
}
9595
}
96+
97+
public static IEnumerable AnimOnlyTestCaases {
98+
get {
99+
yield return new TestCaseData ("Models/DefaultMale/DefaultMale.prefab");
100+
}
101+
}
96102
}
97103

98104
[TestFixture]
@@ -477,14 +483,42 @@ public int Main (KeyData keyData, string testName, string path)
477483

478484
public static void ClipPropertyTest (AnimationClip animClipExpected, AnimationClip animClipActual)
479485
{
480-
// TODO: figure out why we get __preview__ on Windows
481-
Assert.That (animClipActual.name, Is.EqualTo (animClipExpected.name).Or.EqualTo("__preview__" + animClipExpected.name));
486+
Assert.That (animClipActual.name, Is.EqualTo (animClipExpected.name).Or.EqualTo(animClipExpected.name));
482487
Assert.That (animClipActual.legacy, Is.EqualTo (animClipExpected.legacy));
483488
Assert.That (animClipActual.isLooping, Is.EqualTo (animClipExpected.isLooping));
484489
Assert.That (animClipActual.wrapMode, Is.EqualTo (animClipExpected.wrapMode));
485490

486491
// TODO: Uni-34489
487-
Assert.That (animClipActual.length, Is.EqualTo (animClipExpected.length).Within (Mathf.Epsilon), "animClip length doesn't match");
492+
Assert.That (animClipActual.length, Is.EqualTo (animClipExpected.length).Within (0.0001f), "animClip length doesn't match");
493+
}
494+
495+
/// <summary>
496+
/// Compares the properties and curves of two animation clips.
497+
/// </summary>
498+
/// <param name="animClipOriginal">Animation clip original.</param>
499+
/// <param name="animClipImported">Animation clip imported.</param>
500+
public static void ClipTest(AnimationClip animClipOriginal, AnimationClip animClipImported){
501+
// check clip properties match
502+
AnimTester.ClipPropertyTest (animClipOriginal, animClipImported);
503+
504+
foreach (EditorCurveBinding curveBinding in AnimationUtility.GetCurveBindings (animClipOriginal)) {
505+
foreach(EditorCurveBinding impCurveBinding in AnimationUtility.GetCurveBindings (animClipImported)) {
506+
507+
// only compare if the path and property names match
508+
if (curveBinding.path != impCurveBinding.path || curveBinding.propertyName != impCurveBinding.propertyName) {
509+
continue;
510+
}
511+
512+
AnimationCurve animCurveOrig = AnimationUtility.GetEditorCurve (animClipOriginal, curveBinding);
513+
Assert.That (animCurveOrig, Is.Not.Null);
514+
515+
AnimationCurve animCurveImported = AnimationUtility.GetEditorCurve (animClipImported, impCurveBinding);
516+
Assert.That (animCurveImported, Is.Not.Null);
517+
518+
AnimTester.KeyValuesTest(animCurveImported, animCurveOrig,
519+
string.Format("path: {0}, property: {1}", curveBinding.path, curveBinding.propertyName));
520+
}
521+
}
488522
}
489523

490524
public static void KeysTest (AnimationCurve expectedAnimCurve, AnimationCurve actualAnimCurve, string message, IComparer<Keyframe> keyComparer = null)
@@ -568,6 +602,28 @@ public static void CurveTest(AnimationCurve animCurveImported, AnimationCurve an
568602
Assert.That(actualValueKeys, Is.EqualTo(importedValueKeys), string.Format("{0} key value doesn't match", message));
569603
}
570604

605+
606+
public static Dictionary<string, AnimationClip> GetClipsFromFbx(string path, bool setLegacy = false){
607+
//acquire imported object from exported file
608+
Object [] goAssetImported = AssetDatabase.LoadAllAssetsAtPath (path);
609+
Assert.That (goAssetImported, Is.Not.Null);
610+
611+
// TODO : configure object so that it imports w Legacy Animation
612+
613+
var animClips = new Dictionary<string, AnimationClip> ();
614+
foreach (Object o in goAssetImported) {
615+
var animClipImported = o as AnimationClip;
616+
if (animClipImported && !animClipImported.name.StartsWith("__preview__")) {
617+
// TODO : configure import settings so we don't need to force legacy
618+
animClipImported.legacy = setLegacy;
619+
animClips.Add (animClipImported.name, animClipImported);
620+
}
621+
}
622+
Assert.That (animClips, Is.Not.Empty, "expected imported clips");
623+
624+
return animClips;
625+
}
626+
571627
public static AnimationClip GetClipFromFbx(string path){
572628
//acquire imported object from exported file
573629
Object [] goAssetImported = AssetDatabase.LoadAllAssetsAtPath (path);
@@ -578,7 +634,7 @@ public static AnimationClip GetClipFromFbx(string path){
578634
AnimationClip animClipImported = null;
579635
foreach (Object o in goAssetImported) {
580636
animClipImported = o as AnimationClip;
581-
if (animClipImported) break;
637+
if (animClipImported && !animClipImported.name.StartsWith("__preview__")) break;
582638
}
583639
Assert.That (animClipImported, Is.Not.Null, "expected imported clip");
584640

@@ -618,28 +674,7 @@ public void LegacySkinnedMeshAnimTest (string fbxPath)
618674

619675
var animClipImported = AnimTester.GetClipFromFbx (filename);
620676

621-
// check clip properties match
622-
AnimTester.ClipPropertyTest (animClipOriginal, animClipImported);
623-
624-
foreach (EditorCurveBinding curveBinding in AnimationUtility.GetCurveBindings (animClipOriginal)) {
625-
foreach(EditorCurveBinding impCurveBinding in AnimationUtility.GetCurveBindings (animClipImported)) {
626-
627-
// only compare if the path and property names match
628-
if (curveBinding.path != impCurveBinding.path || curveBinding.propertyName != impCurveBinding.propertyName) {
629-
continue;
630-
}
631-
632-
AnimationCurve animCurveOrig = AnimationUtility.GetEditorCurve (animClipOriginal, curveBinding);
633-
Assert.That (animCurveOrig, Is.Not.Null);
634-
635-
AnimationCurve animCurveImported = AnimationUtility.GetEditorCurve (animClipImported, impCurveBinding);
636-
Assert.That (animCurveImported, Is.Not.Null);
637-
638-
AnimTester.CurveTest(animCurveImported, animCurveOrig, curveBinding.propertyName);
639-
}
640-
}
641-
642-
677+
AnimTester.ClipTest (animClipOriginal, animClipImported);
643678
}
644679

645680
[Test, TestCaseSource (typeof (AnimationTestDataClass), "TransformIndependantComponentTestCases")]
@@ -764,5 +799,94 @@ public int ComponentAnimTest (System.Type componentType)
764799
var tester = new AnimTester {keyData=keyData, testName=testName, path=GetRandomFbxFilePath ()};
765800
return tester.DoIt() <= propertyNames.Length ? 1 : 0;
766801
}
802+
803+
[Test, TestCaseSource (typeof (AnimationTestDataClass), "AnimOnlyTestCaases")]
804+
public void AnimOnlyExportTest(string prefabPath)
805+
{
806+
prefabPath = FindPathInUnitTests (prefabPath);
807+
Assert.That (prefabPath, Is.Not.Null);
808+
809+
// add prefab to scene
810+
GameObject originalObj = AssetDatabase.LoadMainAssetAtPath ("Assets/" + prefabPath) as GameObject;
811+
Assert.IsNotNull (originalObj);
812+
GameObject originalGO = GameObject.Instantiate (originalObj);
813+
Assert.IsTrue (originalGO);
814+
815+
// get clips
816+
var animator = originalGO.GetComponentInChildren<Animator> ();
817+
Assert.That (animator, Is.Not.Null);
818+
819+
var controller = animator.runtimeAnimatorController;
820+
Assert.That (controller, Is.Not.Null);
821+
822+
var animClips = controller.animationClips;
823+
Assert.That (animClips, Is.Not.Null);
824+
825+
// get the set of GameObject transforms to be exported with the clip
826+
var animatedObjects = GetAnimatedGameObjects (animClips, animator.gameObject);
827+
828+
// export fbx
829+
// get GameObject
830+
string filename = GetRandomFbxFilePath ();
831+
var exportedFilePath = ModelExporter.ExportObject (filename, originalGO, animOnly: true);
832+
Assert.That (exportedFilePath, Is.EqualTo (filename));
833+
834+
GameObject fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject;
835+
Assert.IsTrue (fbxObj);
836+
837+
// compare hierarchy matches animated objects
838+
var s = new Stack<Transform> ();
839+
840+
// don't check the root since it probably won't have the same name anyway
841+
foreach (Transform child in fbxObj.transform) {
842+
s.Push (child);
843+
}
844+
while (s.Count > 0) {
845+
var transform = s.Pop ();
846+
847+
Assert.That (animatedObjects.Contains(transform.name));
848+
animatedObjects.Remove (transform.name);
849+
850+
foreach (Transform child in transform) {
851+
s.Push (child);
852+
}
853+
}
854+
855+
// compare clips
856+
var fbxAnimClips = AnimTester.GetClipsFromFbx (filename);
857+
Assert.That (fbxAnimClips.Count, Is.EqualTo (animClips.Count()));
858+
859+
foreach (var clip in animClips) {
860+
Assert.That (fbxAnimClips.ContainsKey (clip.name));
861+
var fbxClip = fbxAnimClips [clip.name];
862+
AnimTester.ClipTest (clip, fbxClip);
863+
}
864+
}
865+
866+
private HashSet<string> GetAnimatedGameObjects(AnimationClip[] animClips, GameObject animatorObject){
867+
var animatedObjects = new HashSet<string>();
868+
foreach (var clip in animClips) {
869+
foreach (EditorCurveBinding uniCurveBinding in AnimationUtility.GetCurveBindings (clip)) {
870+
Object uniObj = AnimationUtility.GetAnimatedObject (animatorObject, uniCurveBinding);
871+
if (!uniObj) {
872+
continue;
873+
}
874+
875+
GameObject unityGo = ModelExporter.GetGameObject (uniObj);
876+
if (!unityGo) {
877+
continue;
878+
}
879+
880+
// also it's parents up until but excluding the root (the root will have a different name)
881+
var parent = unityGo.transform;
882+
while (parent != null && parent.parent != null) {
883+
animatedObjects.Add (parent.name);
884+
parent = parent.parent;
885+
}
886+
887+
}
888+
}
889+
return animatedObjects;
890+
}
767891
}
768892
}
Binary file not shown.

0 commit comments

Comments
 (0)