@@ -219,6 +219,77 @@ public IEnumerator TestSurfaceMagnetism()
219219 Assert . IsTrue ( Mathf . Approximately ( 1.0f , Vector3 . Dot ( targetTransform . forward . normalized , Vector3 . forward ) ) ) ;
220220 }
221221
222+ /// <summary>
223+ /// Test Surface Magnetism against vertical normal and ensure no temporal instability (gimbal lock)
224+ /// </summary>
225+ [ UnityTest ]
226+ public IEnumerator TestSurfaceMagnetismGimbalLock ( )
227+ {
228+ Vector3 floorCenter = Vector3 . forward * 10.0f + Vector3 . up * - 2.0f ;
229+
230+ // Reset view to origin
231+ MixedRealityPlayspace . PerformTransformation ( p =>
232+ {
233+ p . position = Vector3 . zero ;
234+ p . LookAt ( floorCenter ) ;
235+ } ) ;
236+
237+ // Build floor to collide against
238+ var floor = GameObject . CreatePrimitive ( PrimitiveType . Cube ) ;
239+ floor . transform . localScale = new Vector3 ( 25.0f , 0.2f , 25.0f ) ;
240+ floor . transform . position = floorCenter ;
241+
242+ // Give the floor a very small tilt
243+ floor . transform . rotation = Quaternion . Euler ( 10 , 0 , 0 ) ;
244+
245+ yield return WaitForFrames ( 2 ) ;
246+
247+ // Instantiate our test GameObject with solver.
248+ // Set layer to ignore raycast so solver doesn't raycast itself (i.e BoxCollider)
249+ var testObjects = InstantiateTestSolver < SurfaceMagnetism > ( ) ;
250+ testObjects . target . layer = LayerMask . NameToLayer ( "Ignore Raycast" ) ;
251+ SurfaceMagnetism surfaceMag = testObjects . solver as SurfaceMagnetism ;
252+
253+ var targetTransform = testObjects . target . transform ;
254+ var cameraTransform = CameraCache . Main . transform ;
255+
256+ yield return WaitForFrames ( 2 ) ;
257+
258+ // Change default orientation mode to surface normal
259+ surfaceMag . CurrentOrientationMode = SurfaceMagnetism . OrientationMode . SurfaceNormal ;
260+ surfaceMag . KeepOrientationVertical = false ;
261+
262+ yield return WaitForFrames ( 2 ) ;
263+
264+ // Test object should now be facing the floor
265+ Assert . IsTrue ( Mathf . Approximately ( 1.0f , Vector3 . Dot ( - targetTransform . forward . normalized , floor . transform . up ) ) ) ;
266+
267+ // Cache test object's current right vector
268+ Vector3 rightVector = testObjects . target . transform . right ;
269+
270+ floor . transform . Rotate ( new Vector3 ( - 20 , 0 , 0 ) ) ;
271+ yield return WaitForFrames ( 2 ) ;
272+
273+ // Make sure that the target's right vector has not flip-flopped
274+ Assert . IsTrue ( Mathf . Sign ( targetTransform . transform . right . x ) == Mathf . Sign ( rightVector . x ) ) ;
275+
276+ // Do the same, but with KeepOrientationVertical = true
277+ surfaceMag . KeepOrientationVertical = true ;
278+
279+ // Cache test object's current right vector
280+ rightVector = testObjects . target . transform . right ;
281+
282+ yield return WaitForFrames ( 2 ) ;
283+
284+ // Test object should now have proper upright orientation
285+ Assert . IsTrue ( Mathf . Approximately ( 1.0f , Vector3 . Dot ( targetTransform . up . normalized , Vector3 . up ) ) ) ;
286+ floor . transform . Rotate ( new Vector3 ( 20 , 0 , 0 ) ) ;
287+ yield return WaitForFrames ( 2 ) ;
288+
289+ // Make sure that the target's right vector has not flip-flopped
290+ Assert . IsTrue ( Mathf . Sign ( targetTransform . transform . right . x ) == Mathf . Sign ( rightVector . x ) ) ;
291+ }
292+
222293 /// <summary>
223294 /// Test solver system's ability to change target types at runtime
224295 /// </summary>
0 commit comments