@@ -10,6 +10,12 @@ namespace FbxExporters.UnitTests
10
10
{
11
11
using CustomExtensions ;
12
12
13
+ public enum RotationInterpolation {
14
+ kEuler = 3 ,
15
+ kMixed = ( 3 + 4 ) ,
16
+ kQuaternion = 4
17
+ } ;
18
+
13
19
public class AnimationTestDataClass
14
20
{
15
21
// TODO: remove items that become supported by exporter
@@ -26,12 +32,20 @@ public class AnimationTestDataClass
26
32
Where ( t => typeof ( Component ) . IsAssignableFrom ( t ) &&
27
33
ModelExporter . MapsToFbxObject . ContainsKey ( t ) ) . Except ( m_exceptionTypes ) ;
28
34
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
+
29
39
public static float [ ] m_keytimes1 = new float [ 3 ] { 1f , 2f , 3f } ;
30
40
public static float [ ] m_keyFloatValues1 = new float [ 3 ] { 0f , 100f , 0f } ;
31
41
public static float [ ] m_keyvalues2 = new float [ 3 ] { 1f , 100f , 1f } ;
32
42
public static Vector3 [ ] m_keyEulerValues3 = new Vector3 [ 3 ] { new Vector3 ( 0f , 80f , 0f ) , new Vector3 ( 80f , 0f , 0f ) , new Vector3 ( 0f , 0f , 80f ) } ;
33
43
public static Vector3 [ ] m_keyEulerValues4 = new Vector3 [ 3 ] { new Vector3 ( 0f , 270f , 0f ) , new Vector3 ( 270f , 0f , 0f ) , new Vector3 ( 0f , 0f , 270f ) } ;
34
44
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
+
35
49
public static IEnumerable TestCases1 {
36
50
get {
37
51
yield return new TestCaseData ( m_keytimes1 , m_keyvalues2 , typeof ( Transform ) , "m_LocalScale.x" ) . Returns ( 1 ) ;
@@ -44,13 +58,13 @@ public static IEnumerable TestCases1 {
44
58
}
45
59
public static IEnumerable TestCases2 {
46
60
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 ) ;
48
62
}
49
63
}
50
64
// specify gimbal conditions for rotation
51
65
public static IEnumerable TestCases3 {
52
66
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 ) ;
54
68
}
55
69
}
56
70
// specify one of each component type
@@ -60,6 +74,16 @@ public static IEnumerable TestCases4 {
60
74
yield return new TestCaseData ( cType ) . Returns ( 1 ) ;
61
75
}
62
76
}
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
+ }
63
87
}
64
88
65
89
[ TestFixture ]
@@ -103,8 +127,10 @@ protected void AnimCurveTest (float [] keyTimesExpected, float [] keyValuesExpec
103
127
Assert . That ( animCurveActual . length , Is . EqualTo ( numKeysExpected ) , "animcurve number of keys doesn't match" ) ;
104
128
105
129
//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.
106
132
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 ) ) ;
108
134
109
135
return ;
110
136
}
@@ -116,11 +142,11 @@ public class KeyData
116
142
public GameObject targetObject ;
117
143
118
144
public virtual int NumKeys { get { return 0 ; } }
119
- public virtual int NumComponents { get { return 0 ; } }
145
+ public virtual int NumProperties { get { return 0 ; } }
120
146
public virtual float [ ] GetKeyValues ( int id ) { return null ; }
121
147
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 ; }
124
150
125
151
}
126
152
@@ -130,10 +156,10 @@ public class ComponentKeyData : KeyData
130
156
public float [ ] keyFloatValues ;
131
157
132
158
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 ; } }
134
160
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 ; }
137
163
}
138
164
139
165
public class SingleKeyData : KeyData
@@ -142,10 +168,10 @@ public class SingleKeyData : KeyData
142
168
public System . Single [ ] keyFloatValues ;
143
169
144
170
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 ; } }
146
172
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 ) ; }
149
175
}
150
176
151
177
public class QuaternionKeyData : KeyData
@@ -154,7 +180,7 @@ public class QuaternionKeyData : KeyData
154
180
public Vector3 [ ] keyEulerValues ;
155
181
156
182
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 ; } }
158
184
public override float [ ] GetKeyValues ( int id )
159
185
{
160
186
return ( from e in keyEulerValues select Quaternion . Euler ( e ) [ id ] ) . ToArray ( ) ;
@@ -164,8 +190,105 @@ public override float [] GetAltKeyValues (int id)
164
190
return ( from e in keyEulerValues select e [ id ] ) . ToArray ( ) ;
165
191
}
166
192
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 )
169
292
{
170
293
return System . Array . IndexOf ( propertyNames , name ) ;
171
294
}
@@ -198,7 +321,7 @@ public int AnimTest (KeyData keyData, string testName)
198
321
animClipOriginal . legacy = true ;
199
322
animClipOriginal . name = "anim_" + testName ;
200
323
201
- for ( int id = 0 ; id < keyData . NumComponents ; id ++ ) {
324
+ for ( int id = 0 ; id < keyData . NumProperties ; id ++ ) {
202
325
// initialize keys
203
326
Keyframe [ ] keys = new Keyframe [ keyData . NumKeys ] ;
204
327
@@ -208,7 +331,7 @@ public int AnimTest (KeyData keyData, string testName)
208
331
}
209
332
AnimationCurve animCurveOriginal = new AnimationCurve ( keys ) ;
210
333
211
- animClipOriginal . SetCurve ( "" , keyData . componentType , keyData . GetComponentName ( id ) , animCurveOriginal ) ;
334
+ animClipOriginal . SetCurve ( "" , keyData . componentType , keyData . GetPropertyName ( id ) , animCurveOriginal ) ;
212
335
}
213
336
214
337
animOrig . AddClip ( animClipOriginal , animClipOriginal . name ) ;
@@ -268,7 +391,7 @@ public int AnimTest (KeyData keyData, string testName)
268
391
if ( ! hasAltPropertyName )
269
392
altPropertyName = curveBinding . propertyName ;
270
393
271
- int id = keyData . FindComponent ( altPropertyName ) ;
394
+ int id = keyData . GetIndexOf ( altPropertyName ) ;
272
395
273
396
if ( id != - 1 ) {
274
397
AnimCurveTest ( keyData . keyTimesInSeconds , hasAltPropertyName ? keyData . GetAltKeyValues ( id ) : keyData . GetKeyValues ( id ) , animCurveImported , curveBinding . propertyName ) ;
@@ -301,7 +424,33 @@ public int GimbalConditionsAnimTest (float [] keyTimesInSeconds, Vector3 [] keyV
301
424
{
302
425
KeyData keyData = new QuaternionKeyData { propertyNames = componentNames , componentType = componentType , keyTimesInSeconds = keyTimesInSeconds , keyEulerValues = keyValues } ;
303
426
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" ) ;
305
454
}
306
455
307
456
[ Test , TestCaseSource ( typeof ( AnimationTestDataClass ) , "TestCases4" ) ]
0 commit comments