Skip to content

Commit 4ef5b41

Browse files
authored
Merge pull request #275 from Unity-Technologies/Uni-35616-continuous-rotations
Uni 35616 continuous rotations
2 parents 52a5771 + 0b7c8b4 commit 4ef5b41

File tree

2 files changed

+181
-19
lines changed

2 files changed

+181
-19
lines changed

Assets/FbxExporters/Editor/UnitTests/FbxAnimationTest.cs

Lines changed: 168 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ namespace FbxExporters.UnitTests
1010
{
1111
using CustomExtensions;
1212

13+
public enum RotationInterpolation {
14+
kEuler=3,
15+
kMixed=(3+4),
16+
kQuaternion=4
17+
};
18+
1319
public class AnimationTestDataClass
1420
{
1521
// TODO: remove items that become supported by exporter
@@ -26,12 +32,20 @@ public class AnimationTestDataClass
2632
Where (t => typeof (Component).IsAssignableFrom (t) &&
2733
ModelExporter.MapsToFbxObject.ContainsKey(t)).Except(m_exceptionTypes);
2834

35+
public static string [] m_quaternionRotationNames = new string [4] { "m_LocalRotation.x", "m_LocalRotation.y", "m_LocalRotation.z", "m_LocalRotation.w" };
36+
public static string [] m_eulerRotationNames = new string [3] { "localEulerAnglesRaw.x", "localEulerAnglesRaw.y", "localEulerAnglesRaw.z" };
37+
public static string [] m_translationNames = new string [3] { "m_LocalPosition.x", "m_LocalPosition.y", "m_LocalPosition.z"};
38+
2939
public static float [] m_keytimes1 = new float [3] { 1f, 2f, 3f };
3040
public static float [] m_keyFloatValues1 = new float [3] { 0f, 100f, 0f };
3141
public static float [] m_keyvalues2 = new float [3] { 1f, 100f, 1f };
3242
public static Vector3 [] m_keyEulerValues3 = new Vector3 [3] { new Vector3 (0f, 80f, 0f), new Vector3 (80f, 0f, 0f), new Vector3 (0f, 0f, 80f) };
3343
public static Vector3 [] m_keyEulerValues4 = new Vector3 [3] { new Vector3 (0f, 270f, 0f), new Vector3 (270f, 0f, 0f), new Vector3 (0f, 0f, 270f) };
3444

45+
public static float [] m_keytimes5 = new float [5] { 0f, 30f, 60f, 90f, 120f };
46+
public static Vector3 [] m_keyPosValues5 = new Vector3 [5] { new Vector3 (5.078195f, 0.000915527f, 4.29761f), new Vector3 (0.81f, 0.000915527f, 10.59f), new Vector3 (-3.65f, 0.000915527f, 4.29761f), new Vector3 (0.81f, 0.000915527f, -3.37f), new Vector3 (5.078195f, 0.000915527f, 4.29761f) };
47+
public static Vector3 [] m_keyRotValues5 = new Vector3 [5] { new Vector3 (0f, 0f, 0f), new Vector3 (0f, -90f, 0f), new Vector3 (0f, -180f, 0f), new Vector3 (0f, -270f, 0f), new Vector3 (0f, -360f, 0f) };
48+
3549
public static IEnumerable TestCases1 {
3650
get {
3751
yield return new TestCaseData (m_keytimes1, m_keyvalues2, typeof (Transform), "m_LocalScale.x").Returns (1);
@@ -44,13 +58,13 @@ public static IEnumerable TestCases1 {
4458
}
4559
public static IEnumerable TestCases2 {
4660
get {
47-
yield return new TestCaseData (m_keytimes1, m_keyEulerValues3, typeof (Transform), new string [4] { "m_LocalRotation.x", "m_LocalRotation.y", "m_LocalRotation.z", "m_LocalRotation.w" } ).Returns (3);
61+
yield return new TestCaseData (m_keytimes1, m_keyEulerValues3, typeof (Transform), m_quaternionRotationNames ).Returns (3);
4862
}
4963
}
5064
// specify gimbal conditions for rotation
5165
public static IEnumerable TestCases3 {
5266
get {
53-
yield return new TestCaseData (m_keytimes1, m_keyEulerValues4, typeof (Transform), new string [4] { "m_LocalRotation.x", "m_LocalRotation.y", "m_LocalRotation.z", "m_LocalRotation.w" } ).Returns (3);
67+
yield return new TestCaseData (m_keytimes1, m_keyEulerValues4, typeof (Transform), m_quaternionRotationNames ).Returns (3);
5468
}
5569
}
5670
// specify one of each component type
@@ -60,6 +74,16 @@ public static IEnumerable TestCases4 {
6074
yield return new TestCaseData (cType).Returns(1);
6175
}
6276
}
77+
// specify continuous rotations
78+
public static IEnumerable TestCases5 {
79+
get {
80+
yield return new TestCaseData (RotationInterpolation.kEuler /*use euler values*/, m_keytimes5, m_keyPosValues5, m_keyRotValues5).Returns (3);
81+
// Uni-35616 can't programmatically define a Euler (Quaternion) mix.
82+
// yield return new TestCaseData (RotationInterpolation.kMixed /*use euler+quaternion values*/, m_keytimes5, m_keyPosValues5, m_keyRotValues5).Returns (3);
83+
// Uni-35616 doesn't work with quaternions; rotations don't exceed 180
84+
// yield return new TestCaseData (RotationInterpolation.kQuaternion /*use quaternion values*/, m_keytimes5, m_keyPosValues5, m_keyRotValues5).Returns (3);
85+
}
86+
}
6387
}
6488

6589
[TestFixture]
@@ -103,8 +127,10 @@ protected void AnimCurveTest (float [] keyTimesExpected, float [] keyValuesExpec
103127
Assert.That (animCurveActual.length, Is.EqualTo(numKeysExpected), "animcurve number of keys doesn't match");
104128

105129
//check imported animation against original
130+
// NOTE: if I check the key values explicitly they match but when I compare using this ListMapper the float values
131+
// are off by 0.000005f; not sure why that happens.
106132
Assert.That(new ListMapper(animCurveActual.keys).Property ("time"), Is.EqualTo(keyTimesExpected), string.Format("{0} key time doesn't match",message));
107-
Assert.That(new ListMapper(animCurveActual.keys).Property ("value"), Is.EqualTo(keyValuesExpected), string.Format("{0} key value doesn't match", message));
133+
Assert.That(new ListMapper(animCurveActual.keys).Property ("value"), Is.EqualTo(keyValuesExpected).Within(0.000005f), string.Format("{0} key value doesn't match", message));
108134

109135
return ;
110136
}
@@ -116,11 +142,11 @@ public class KeyData
116142
public GameObject targetObject;
117143

118144
public virtual int NumKeys { get { return 0; } }
119-
public virtual int NumComponents { get { return 0; } }
145+
public virtual int NumProperties { get { return 0; } }
120146
public virtual float[] GetKeyValues(int id) { return null; }
121147
public virtual float [] GetAltKeyValues (int id) { return GetKeyValues(id); }
122-
public virtual string GetComponentName (int id) { return null; }
123-
public virtual int FindComponent (string name) { return -1; }
148+
public virtual string GetPropertyName (int id) { return null; }
149+
public virtual int GetIndexOf (string name) { return -1; }
124150

125151
}
126152

@@ -130,10 +156,10 @@ public class ComponentKeyData : KeyData
130156
public float [] keyFloatValues;
131157

132158
public override int NumKeys { get { return Mathf.Min (keyTimesInSeconds.Length, keyFloatValues.Length); } }
133-
public override int NumComponents { get { return 1; } }
159+
public override int NumProperties { get { return 1; } }
134160
public override float[] GetKeyValues (int id) { return keyFloatValues; }
135-
public override string GetComponentName (int id) { return propertyName; }
136-
public override int FindComponent (string name) { return (name == propertyName) ? 0 : -1; }
161+
public override string GetPropertyName (int id) { return propertyName; }
162+
public override int GetIndexOf (string name) { return (name == propertyName) ? 0 : -1; }
137163
}
138164

139165
public class SingleKeyData : KeyData
@@ -142,10 +168,10 @@ public class SingleKeyData : KeyData
142168
public System.Single [] keyFloatValues;
143169

144170
public override int NumKeys { get { return Mathf.Min (keyTimesInSeconds.Length, keyFloatValues.Length); } }
145-
public override int NumComponents { get { return propertyNames.Length; } }
171+
public override int NumProperties { get { return propertyNames.Length; } }
146172
public override float [] GetKeyValues (int id) { return keyFloatValues; }
147-
public override string GetComponentName (int id) { return propertyNames[id]; }
148-
public override int FindComponent (string name) { return System.Array.IndexOf (propertyNames, name); }
173+
public override string GetPropertyName (int id) { return propertyNames[id]; }
174+
public override int GetIndexOf (string name) { return System.Array.IndexOf (propertyNames, name); }
149175
}
150176

151177
public class QuaternionKeyData : KeyData
@@ -154,7 +180,7 @@ public class QuaternionKeyData : KeyData
154180
public Vector3 [] keyEulerValues;
155181

156182
public override int NumKeys { get { return Mathf.Min (keyTimesInSeconds.Length, keyEulerValues.Length); } }
157-
public override int NumComponents { get { return propertyNames.Length; } }
183+
public override int NumProperties { get { return propertyNames.Length; } }
158184
public override float [] GetKeyValues (int id)
159185
{
160186
return (from e in keyEulerValues select Quaternion.Euler(e)[id]).ToArray ();
@@ -164,8 +190,105 @@ public override float [] GetAltKeyValues (int id)
164190
return (from e in keyEulerValues select e[id]).ToArray ();
165191
}
166192

167-
public override string GetComponentName (int id) { return propertyNames[id]; }
168-
public override int FindComponent (string name)
193+
public override string GetPropertyName (int id) { return propertyNames[id]; }
194+
public override int GetIndexOf (string name)
195+
{
196+
return System.Array.IndexOf (propertyNames, name);
197+
}
198+
}
199+
200+
public class TransformKeyData : KeyData
201+
{
202+
public RotationInterpolation RotationType = RotationInterpolation.kEuler;
203+
204+
public string[] propertyNames;
205+
public Vector3 [] keyPosValues;
206+
public Vector3 [] keyEulerValues;
207+
private Quaternion [] keyQuatValues;
208+
209+
public bool IsRotation(int id) { return id < (int)RotationType; }
210+
211+
public override int NumKeys { get { return keyTimesInSeconds.Length; } }
212+
public override int NumProperties { get { return propertyNames.Length; } }
213+
public override float [] GetKeyValues (int id)
214+
{
215+
if (RotationType==RotationInterpolation.kEuler)
216+
return GetAltKeyValues(id);
217+
218+
// compute continous rotations
219+
if (keyQuatValues==null)
220+
{
221+
keyQuatValues = new Quaternion[NumKeys];
222+
223+
for (int idx=0; idx < NumKeys; idx++)
224+
{
225+
keyQuatValues[idx] = Quaternion.Euler(keyEulerValues[idx]);
226+
}
227+
}
228+
229+
float[] result = new float[NumKeys];
230+
231+
for (int idx=0; idx < NumKeys; idx++)
232+
{
233+
if (IsRotation(id))
234+
{
235+
switch (RotationType)
236+
{
237+
case (RotationInterpolation.kEuler):
238+
result[idx] = keyEulerValues[idx][id];
239+
break;
240+
case (RotationInterpolation.kMixed):
241+
int NumEulerFields = (int)RotationInterpolation.kEuler;
242+
243+
result[idx] = (id<NumEulerFields)
244+
? keyEulerValues[idx][id] : keyQuatValues[idx][id-NumEulerFields];
245+
246+
break;
247+
case (RotationInterpolation.kQuaternion):
248+
result[idx] = keyQuatValues[idx][id];
249+
break;
250+
}
251+
}
252+
else
253+
{
254+
result[idx] = keyPosValues[idx][id-(int)RotationType];
255+
}
256+
}
257+
258+
return result;
259+
}
260+
public override float [] GetAltKeyValues (int id)
261+
{
262+
float[] result = new float[NumKeys];
263+
264+
for (int idx=0; idx < NumKeys; idx++)
265+
{
266+
// kMixed
267+
// 0..2 euler XYZ
268+
// 3..6 quaternion XYZ
269+
// 7..9 position XYZ
270+
// kQuaternion
271+
// 0..3 quarternion XYZW
272+
// 4..6 position XYZ
273+
// kEuler
274+
// 0..2 euler XYZ
275+
// 3..5 position XYZ
276+
277+
if (IsRotation(id))
278+
{
279+
result[idx] = keyEulerValues[idx][id];
280+
}
281+
else
282+
{
283+
result[idx] = keyPosValues[idx][id-(int)RotationType];
284+
}
285+
}
286+
287+
return result;
288+
}
289+
290+
public override string GetPropertyName (int id) { return propertyNames[id]; }
291+
public override int GetIndexOf (string name)
169292
{
170293
return System.Array.IndexOf (propertyNames, name);
171294
}
@@ -198,7 +321,7 @@ public int AnimTest (KeyData keyData, string testName)
198321
animClipOriginal.legacy = true;
199322
animClipOriginal.name = "anim_" + testName;
200323

201-
for (int id = 0; id < keyData.NumComponents; id++) {
324+
for (int id = 0; id < keyData.NumProperties; id++) {
202325
// initialize keys
203326
Keyframe [] keys = new Keyframe [keyData.NumKeys];
204327

@@ -208,7 +331,7 @@ public int AnimTest (KeyData keyData, string testName)
208331
}
209332
AnimationCurve animCurveOriginal = new AnimationCurve (keys);
210333

211-
animClipOriginal.SetCurve ("", keyData.componentType, keyData.GetComponentName (id), animCurveOriginal);
334+
animClipOriginal.SetCurve ("", keyData.componentType, keyData.GetPropertyName (id), animCurveOriginal);
212335
}
213336

214337
animOrig.AddClip (animClipOriginal, animClipOriginal.name);
@@ -268,7 +391,7 @@ public int AnimTest (KeyData keyData, string testName)
268391
if (!hasAltPropertyName)
269392
altPropertyName = curveBinding.propertyName;
270393

271-
int id = keyData.FindComponent (altPropertyName);
394+
int id = keyData.GetIndexOf (altPropertyName);
272395

273396
if (id != -1) {
274397
AnimCurveTest (keyData.keyTimesInSeconds, hasAltPropertyName ? keyData.GetAltKeyValues (id) : keyData.GetKeyValues (id), animCurveImported, curveBinding.propertyName);
@@ -301,7 +424,33 @@ public int GimbalConditionsAnimTest (float [] keyTimesInSeconds, Vector3 [] keyV
301424
{
302425
KeyData keyData = new QuaternionKeyData { propertyNames = componentNames, componentType = componentType, keyTimesInSeconds = keyTimesInSeconds, keyEulerValues = keyValues };
303426

304-
return AnimTest (keyData, componentType.ToString () + "_Quaternion");
427+
return AnimTest (keyData, componentType.ToString () + "_Gimbal");
428+
}
429+
430+
[Description("Uni-35616 continuous rotations")]
431+
[Test, TestCaseSource (typeof (AnimationTestDataClass), "TestCases5")]
432+
public int ContinuousRotationAnimTest (RotationInterpolation rotInterp, float [] keyTimesInSeconds, Vector3 [] keyPosValues, Vector3 [] keyEulerValues)
433+
{
434+
System.Type componentType = typeof(Transform);
435+
436+
string[] propertyNames = null;
437+
438+
switch (rotInterp)
439+
{
440+
case RotationInterpolation.kEuler:
441+
propertyNames=AnimationTestDataClass.m_eulerRotationNames.Concat(AnimationTestDataClass.m_translationNames).ToArray();
442+
break;
443+
case RotationInterpolation.kQuaternion:
444+
propertyNames=AnimationTestDataClass.m_quaternionRotationNames.Concat(AnimationTestDataClass.m_translationNames).ToArray();
445+
break;
446+
case RotationInterpolation.kMixed:
447+
propertyNames=AnimationTestDataClass.m_eulerRotationNames.Concat(AnimationTestDataClass.m_quaternionRotationNames).Concat(AnimationTestDataClass.m_translationNames).ToArray();
448+
break;
449+
}
450+
451+
KeyData keyData = new TransformKeyData { RotationType = rotInterp, propertyNames = propertyNames, componentType = componentType, keyTimesInSeconds = keyTimesInSeconds, keyPosValues = keyPosValues, keyEulerValues = keyEulerValues };
452+
453+
return AnimTest (keyData, componentType.ToString () + "_ContinuousRotations");
305454
}
306455

307456
[Test, TestCaseSource (typeof (AnimationTestDataClass), "TestCases4")]

Assets/FbxExporters/Editor/UnitTests/FbxAnimationTest.cs.meta

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)