/// <summary> /// Compares the properties and curves of two animation clips. /// </summary> /// <param name="animClipOriginal">Animation clip original.</param> /// <param name="animClipImported">Animation clip imported.</param> public static void ClipTest(AnimationClip animClipOriginal, AnimationClip animClipImported) { // check clip properties match AnimTester.ClipPropertyTest(animClipOriginal, animClipImported); foreach (EditorCurveBinding curveBinding in AnimationUtility.GetCurveBindings(animClipOriginal)) { foreach (EditorCurveBinding impCurveBinding in AnimationUtility.GetCurveBindings(animClipImported)) { // only compare if the path and property names match if (curveBinding.path != impCurveBinding.path || curveBinding.propertyName != impCurveBinding.propertyName) { continue; } bool isRotation = AnimationTestDataClass.m_rotationEulerNames.Contains(curveBinding.propertyName) || AnimationTestDataClass.m_rotationQuaternionNames.Contains(curveBinding.propertyName); AnimationCurve animCurveOrig = AnimationUtility.GetEditorCurve(animClipOriginal, curveBinding); Assert.That(animCurveOrig, Is.Not.Null); AnimationCurve animCurveImported = AnimationUtility.GetEditorCurve(animClipImported, impCurveBinding); Assert.That(animCurveImported, Is.Not.Null); AnimTester.KeyValuesTest( animCurveImported, animCurveOrig, string.Format("path: {0}, property: {1}", curveBinding.path, curveBinding.propertyName), isRotation); } } }
public int ContinuousRotationAnimTest(RotationCurveType rotCurveType, float [] keyTimesInSeconds, Vector3 [] keyEulerValues) { System.Type componentType = typeof(Transform); string[] propertyNames = null; string testName = componentType.ToString() + "_ContinuousRotations"; bool compareOriginalKeys = false; switch (rotCurveType) { case RotationCurveType.kEuler: testName += "_Euler"; propertyNames = AnimationTestDataClass.m_rotationEulerNames.ToArray(); break; case RotationCurveType.kQuaternion: compareOriginalKeys = true; testName += "_Quaternion"; propertyNames = AnimationTestDataClass.m_rotationQuaternionNames.ToArray(); break; } KeyData keyData = new TransformKeyData { importSettings = new { resampleCurves = false, animationType = ModelImporterAnimationType.Legacy, animationCompression = ModelImporterAnimationCompression.Off, importConstraints = true }, compareOriginalKeys = compareOriginalKeys, RotationType = rotCurveType, propertyNames = propertyNames, componentType = componentType, keyTimes = keyTimesInSeconds, keyEulerValues = keyEulerValues }; var tester = new AnimTester { keyData = keyData, testName = testName, path = GetRandomFbxFilePath() }; return(tester.DoIt()); }
public void LegacySkinnedMeshAnimTest(string fbxPath) { fbxPath = FindPathInUnitTests(fbxPath); Assert.That(fbxPath, Is.Not.Null); // add fbx to scene GameObject originalGO = AddAssetToScene(fbxPath); // get clip AnimationClip animClipOriginal = originalGO.GetComponentInChildren <Animation>().clip; Assert.That(animClipOriginal, Is.Not.Null); // export fbx // get GameObject string filename = AssetDatabase.GetAssetPath(ExportToFbx(originalGO)); // TODO: Uni-34492 change importer settings of (newly exported model) // so that it's not resampled and it is legacy animation AnimTester.ConfigureImportSettings(filename); var animClipImported = AnimTester.GetClipFromFbx(filename); AnimTester.ClipTest(animClipOriginal, animClipImported); }
public int BlendshapeAnimTest(float [] keyTimesInSeconds, float [] keyValues, System.Type componentType, string componentName) { var prefabPath = FindPathInUnitTests("Models/blendshape.fbx"); Assert.That(prefabPath, Is.Not.Null); // add prefab to scene GameObject originalGO = AddAssetToScene(prefabPath); KeyData keyData = new PropertyKeyData { targetObject = originalGO, propertyName = componentName, componentType = componentType, keyTimes = keyTimesInSeconds, keyFloatValues = keyValues }; var tester = new AnimTester { keyData = keyData, testName = componentName, path = GetRandomFbxFilePath() }; var exportOptions = new ExportModelSettingsSerialize(); exportOptions.SetAnimatedSkinnedMesh(true); tester.exportOptions = exportOptions; return(tester.DoIt()); }
/// <summary> /// Compares the properties and curves of multiple animation clips /// </summary> /// <param name="animClipsOriginal">Animation clips original.</param> /// <param name="animClipsImported">Animation clips imported.</param> public static void MultiClipTest(AnimationClip[] animClipsOriginal, Dictionary <string, AnimationClip> animClipsImported) { Assert.That(animClipsImported.Count, Is.EqualTo(animClipsOriginal.Length)); foreach (var clip in animClipsOriginal) { Assert.That(animClipsImported.ContainsKey(clip.name)); var fbxClip = animClipsImported [clip.name]; AnimTester.ClipTest(clip, fbxClip); } }
public int GimbalConditionsAnimTest(float [] keyTimesInSeconds, Vector3 [] keyValues, System.Type componentType, string [] componentNames) { KeyData keyData = new QuaternionKeyData { propertyNames = componentNames, componentType = componentType, keyTimes = keyTimesInSeconds, keyEulerValues = keyValues }; var tester = new AnimTester { keyData = keyData, testName = componentType.ToString() + "_Gimbal", path = GetRandomFbxFilePath() }; return(tester.DoIt()); }
public int QuaternionPropertyAnimTest(float [] keyTimesInSeconds, Vector3 [] keyValues, System.Type componentType, string[] componentNames) { KeyData keyData = new QuaternionKeyData { compareOriginalKeys = true, propertyNames = componentNames, componentType = componentType, keyTimes = keyTimesInSeconds, keyEulerValues = keyValues }; var tester = new AnimTester { keyData = keyData, testName = (componentType.ToString() + "_Quaternion"), path = GetRandomFbxFilePath() }; return(tester.DoIt()); }
public int SimplePropertyAnimTest(float [] keyTimesInSeconds, float [] keyValues, System.Type componentType, string componentName) { KeyData keyData = new PropertyKeyData { propertyName = componentName, componentType = componentType, keyTimes = keyTimesInSeconds, keyFloatValues = keyValues }; var tester = new AnimTester { keyData = keyData, testName = componentName, path = GetRandomFbxFilePath() }; return(tester.DoIt()); }
public void AnimOnlyExportTest(string prefabPath) { prefabPath = FindPathInUnitTests(prefabPath); Assert.That(prefabPath, Is.Not.Null); // add prefab to scene GameObject originalGO = AddAssetToScene(prefabPath); // get clips var animator = originalGO.GetComponentInChildren <Animator> (); var animClips = GetClipsFromAnimator(animator); // get the set of GameObject transforms to be exported with the clip var animatedObjects = GetAnimatedGameObjects(animClips, animator.gameObject); // export fbx // get GameObject GameObject fbxObj = ExportToFbx(originalGO, true); Assert.IsTrue(fbxObj); // compare hierarchy matches animated objects var s = new Stack <Transform> (); // don't check the root since it probably won't have the same name anyway foreach (Transform child in fbxObj.transform) { s.Push(child); } while (s.Count > 0) { var transform = s.Pop(); Assert.That(animatedObjects.Contains(transform.name)); animatedObjects.Remove(transform.name); foreach (Transform child in transform) { s.Push(child); } } // compare clips var fbxAnimClips = AnimTester.GetClipsFromFbx(AssetDatabase.GetAssetPath(fbxObj)); AnimTester.MultiClipTest(animClips, fbxAnimClips); }
static void Init(bool setMaxSize) { // Get existing open window or if none, make a new one: AnimTester wnd = EditorWindow.GetWindow <AnimTester> (false, "Anim Tester"); //restrict size Object[] activeGOs = Selection.GetFiltered(typeof(Animation), SelectionMode.Editable | SelectionMode.TopLevel); wnd.minSize = new Vector2(500, 75 + activeGOs.Length * 20); if (setMaxSize) { wnd.maxSize = new Vector2(500, GetMaxHeight(activeGOs.Length)); //75.1, because when the values are exactly the same, the window doesn't have a border which is ugly // if (wnd.docked == false) //wnd.docked is unfortunately inaccessible wnd.maxSize = new Vector2(4000, 4000); //setting back to defaults (after formatting the window size) to allow window resizing which is good for docked windows (otherwise will resize the parent) } }
public int ComponentAnimTest(System.Type componentType) { #if DEBUG_UNITTEST Debug.Log(string.Format("ComponentAnimTest {0}", componentType.ToString())); #endif if (!ModelExporter.MapsToFbxObject.ContainsKey(componentType)) { #if DEBUG_UNITTEST Debug.Log(string.Format("skipping {0}; fbx export not supported", componentType.ToString())); #endif return(1); } string testName = "ComponentAnimTest_" + componentType.ToString(); GameObject targetObject = AnimTester.CreateTargetObject(testName, componentType); string [] propertyNames = (from b in AnimationUtility.GetAnimatableBindings(targetObject, targetObject) where b.type == componentType select b.propertyName).ToArray(); if (propertyNames.Length == 0) { #if DEBUG_UNITTEST Debug.Log(string.Format("skipping {0}; no animatable Single properties found", componentType.ToString())); #endif return(1); } float [] keyTimesInSeconds = new float [3] { 1f, 2f, 3f }; var ran = new System.Random(); float [] keyValues = Enumerable.Range(1, keyTimesInSeconds.Length).Select(x => (float)ran.NextDouble()).ToArray(); KeyData keyData = new MultiPropertyKeyData { propertyNames = propertyNames, componentType = componentType, keyTimes = keyTimesInSeconds, keyValues = keyValues, targetObject = targetObject }; var tester = new AnimTester { keyData = keyData, testName = testName, path = GetRandomFbxFilePath() }; return(tester.DoIt() <= propertyNames.Length ? 1 : 0); }
// private void RefreshContent() { //we have to do this bullsh*t because the wnd.docked is inaccessible and we need to know whether we can set the max size or not (i.e. guess if it's docked or custom sized) bool setMaxSize = true; AnimTester wnd = EditorWindow.GetWindow <AnimTester> (false, "Anim Tester"); if (m_EditableObjects == null || m_EditableObjects.Length <= 0) { setMaxSize = false; } else if (wnd.position.height > GetMaxHeight(m_EditableObjects.Length)) { setMaxSize = false; } //reinit the window and its content Init(setMaxSize); InitObjects(); this.Repaint(); }
public int Main(KeyData keyData, string testName, string path) { if (!keyData.targetObject) { keyData.targetObject = CreateTargetObject(testName, keyData.componentType); } Animation animOrig = keyData.targetObject.AddComponent(typeof(Animation)) as Animation; AnimationClip animClipOriginal = new AnimationClip(); var animCurvesOriginal = new AnimationCurve[keyData.NumProperties]; animClipOriginal.legacy = true; animClipOriginal.name = "anim_" + testName; for (int id = 0; id < keyData.NumProperties; id++) { // initialize keys Keyframe [] keys = new Keyframe [keyData.NumKeys]; for (int idx = 0; idx < keyData.NumKeys; idx++) { keys [idx].time = keyData.keyTimes [idx]; keys [idx].value = keyData.GetKeyValues(id) [idx]; } animCurvesOriginal[id] = new AnimationCurve(keys); animClipOriginal.SetCurve("", keyData.componentType, keyData.GetPropertyName(id), animCurvesOriginal[id]); } animOrig.AddClip(animClipOriginal, animClipOriginal.name); animOrig.clip = animClipOriginal; // NOTE: when we first cached the curves the tangents wheren't set. foreach (EditorCurveBinding curveBinding in AnimationUtility.GetCurveBindings(animOrig.clip)) { int id = keyData.GetIndexOf(curveBinding.propertyName); if (id == -1) { continue; } animCurvesOriginal[id] = AnimationUtility.GetEditorCurve(animOrig.clip, curveBinding); } // TODO: add extra parent so that we can test export/import of transforms var goRoot = new GameObject(); goRoot.name = "Root_" + testName; keyData.targetObject.transform.parent = goRoot.transform; //export the object var exportedFilePath = ModelExporter.ExportObject(path, goRoot); Assert.That(exportedFilePath, Is.EqualTo(path)); // TODO: Uni-34492 change importer settings of (newly exported model) // so that it's not resampled and it is legacy animation AnimTester.ConfigureImportSettings(path, keyData.importSettings); // create a scene GO so we can compare. #if DEBUG_UNITTEST GameObject prefabGO = AssetDatabase.LoadMainAssetAtPath(path) as GameObject; GameObject sceneGO = Object.Instantiate(prefabGO, keyData.targetObject.transform.localPosition, keyData.targetObject.transform.localRotation); sceneGO.name = "Imported_" + testName; #endif //acquire imported object from exported file AnimationClip animClipImported = GetClipFromFbx(path); ClipPropertyTest(animClipOriginal, animClipImported); int result = 0; foreach (EditorCurveBinding curveBinding in AnimationUtility.GetCurveBindings(animClipImported)) { AnimationCurve animCurveImported = AnimationUtility.GetEditorCurve(animClipImported, curveBinding); Assert.That(animCurveImported, Is.Not.Null); string propertyBinding = curveBinding.propertyName; int id = keyData.GetIndexOf(propertyBinding); bool hasQuatBinding = MapEulerToQuaternionPropertyName.TryGetValue(propertyBinding, out propertyBinding); bool isRotation = AnimationTestDataClass.m_rotationEulerNames.Contains(curveBinding.propertyName) || AnimationTestDataClass.m_rotationQuaternionNames.Contains(curveBinding.propertyName); if (id == -1) { id = keyData.GetIndexOf(propertyBinding); } #if DEBUG_UNITTEST Debug.Log(string.Format("propertyBinding={0} mappedBinding={1} id={2}", curveBinding.propertyName, propertyBinding, id)); #endif if (id != -1) { if (keyData.compareOriginalKeys) { // NOTE: we cannot compare the keys that exported quaternion but are imported as euler. if (!hasQuatBinding) { // compare against original keydata KeysTest(keyData.keyTimes, keyData.GetKeyValues(id), animCurveImported, curveBinding.propertyName); // compare against original animCurve KeysTest(animCurvesOriginal[id], animCurveImported, curveBinding.propertyName, keyComparer); } else { // compare by sampled keyvalues against original keydata KeyValuesTest(keyData.keyTimes, keyData.GetAltKeyValues(id), animCurveImported, curveBinding.propertyName, isRotation); } } else { // compare by sampled keyvalues against original animCurve KeyValuesTest(animCurvesOriginal[id], animCurveImported, curveBinding.propertyName, isRotation); } result++; } } return(result); }