VR4Medical/ICI/Library/PackageCache/com.unity.xr.interaction.toolkit@42ef3600567b/Tests/Runtime/XRBodyTransformerTests.cs
2025-07-29 13:45:50 +03:00

346 lines
17 KiB
C#

using System.Collections;
using NUnit.Framework;
using UnityEngine.TestTools;
using UnityEngine.TestTools.Utils;
using UnityEngine.XR.Interaction.Toolkit.Locomotion;
namespace UnityEngine.XR.Interaction.Toolkit.Tests
{
[TestFixture]
class XRBodyTransformerTests
{
[TearDown]
public void TearDown()
{
TestUtilities.DestroyAllSceneObjects();
}
[UnityTest]
public IEnumerator TransformationsAreAppliedInQueuePriorityOrder()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
// Order of transformations:
// 1. (priority -1) Move origin forward 1 meter
// 2. (priority 0) Rotate origin yaw 90 degrees
// 3. (priority 0) Move origin forward 1 meter
// 4. (priority int.MaxValue) Rotate origin pitch 90 degrees
var transformation1 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Translate(Vector3.forward); });
var transformation2 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Rotate(Vector3.up, 90f); });
var transformation3 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Translate(Vector3.forward); });
var transformation4 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Rotate(Vector3.right, 90f); });
var expectedPosition = new Vector3(1f, 0f, 1f);
var expectedRotation = Quaternion.AngleAxis(90f, Vector3.up) * Quaternion.AngleAxis(90f, Vector3.right);
// Queue in decreasing order to test that priority value is respected
xrBodyTransformer.QueueTransformation(transformation4, int.MaxValue);
xrBodyTransformer.QueueTransformation(transformation2);
xrBodyTransformer.QueueTransformation(transformation3);
xrBodyTransformer.QueueTransformation(transformation1, -1);
yield return new WaitForFixedUpdate();
yield return null;
var originTransform = xrOrigin.transform;
Assert.That(originTransform.position, Is.EqualTo(expectedPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
Assert.That(originTransform.rotation, Is.EqualTo(expectedRotation).Using(QuaternionEqualityComparer.Instance));
}
[UnityTest]
public IEnumerator TransformationsWithSamePriorityAreAppliedInQueueOrder()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
// Order of transformations:
// 1. (priority -1) Move origin forward 1 meter
// 2. (priority 0) Rotate origin yaw 90 degrees
// 3. (priority 0) Move origin forward 1 meter
// 4. (priority 0) Rotate origin pitch 90 degrees
var transformation1 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Translate(Vector3.forward); });
var transformation2 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Rotate(Vector3.up, 90f); });
var transformation3 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Translate(Vector3.forward); });
var transformation4 = new DelegateXRBodyTransformation(body => { body.xrOrigin.transform.Rotate(Vector3.right, 90f); });
var expectedPosition = new Vector3(1f, 0f, 1f);
var expectedRotation = Quaternion.AngleAxis(90f, Vector3.up) * Quaternion.AngleAxis(90f, Vector3.right);
xrBodyTransformer.QueueTransformation(transformation2);
xrBodyTransformer.QueueTransformation(transformation3);
xrBodyTransformer.QueueTransformation(transformation1, -1);
xrBodyTransformer.QueueTransformation(transformation4);
yield return new WaitForFixedUpdate();
yield return null;
var originTransform = xrOrigin.transform;
Assert.That(originTransform.position, Is.EqualTo(expectedPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
Assert.That(originTransform.rotation, Is.EqualTo(expectedRotation).Using(QuaternionEqualityComparer.Instance));
}
[UnityTest]
public IEnumerator QueuedTransformationsAreOnlyAppliedForOneFrame()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var m_Transformation1Applied = false;
var m_Transformation2Applied = false;
var m_Transformation3Applied = false;
var transformation1 = new DelegateXRBodyTransformation(body => { m_Transformation1Applied = true; });
var transformation2 = new DelegateXRBodyTransformation(body => { m_Transformation2Applied = true; });
var transformation3 = new DelegateXRBodyTransformation(body => { m_Transformation3Applied = true; });
xrBodyTransformer.QueueTransformation(transformation1);
xrBodyTransformer.QueueTransformation(transformation2);
xrBodyTransformer.QueueTransformation(transformation3);
yield return new WaitForFixedUpdate();
yield return null;
Assert.IsTrue(m_Transformation1Applied);
Assert.IsTrue(m_Transformation2Applied);
Assert.IsTrue(m_Transformation3Applied);
m_Transformation1Applied = false;
m_Transformation2Applied = false;
m_Transformation3Applied = false;
yield return new WaitForFixedUpdate();
yield return null;
Assert.IsFalse(m_Transformation1Applied);
Assert.IsFalse(m_Transformation2Applied);
Assert.IsFalse(m_Transformation3Applied);
}
[UnityTest]
public IEnumerator ConstrainedBodyManipulatorLinksToBody()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var characterController = xrOrigin.Origin.AddComponent<CharacterController>();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
// Manipulator should link to body when it is assigned to body transformer
var characterControllerManipulator = ScriptableObject.CreateInstance<CharacterControllerBodyManipulator>();
xrBodyTransformer.constrainedBodyManipulator = characterControllerManipulator;
Assert.That(characterControllerManipulator.linkedBody.xrOrigin, Is.EqualTo(xrOrigin));
Assert.That(characterControllerManipulator.linkedBody.bodyPositionEvaluator, Is.EqualTo(xrBodyTransformer.bodyPositionEvaluator));
Assert.That(characterControllerManipulator.characterController, Is.EqualTo(characterController));
yield return new WaitForFixedUpdate();
yield return null;
// Should unlink when body transformer is disabled
xrBodyTransformer.enabled = false;
Assert.That(characterControllerManipulator.linkedBody, Is.Null);
Object.Destroy(characterControllerManipulator);
}
[UnityTest]
public IEnumerator ApplyTransformation_DelegateXRBodyTransformation()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var transformationApplied = false;
var transformation = new DelegateXRBodyTransformation(body => { transformationApplied = true; });
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.IsTrue(transformationApplied);
}
[UnityTest]
public IEnumerator ApplyTransformation_OriginMovement()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var originTransform = xrOrigin.Origin.transform;
var translation = new Vector3(1.5f, 2.5f, 3.5f);
var transformation = new XROriginMovement { motion = translation, forceUnconstrained = true, };
var expectedPosition = originTransform.position + translation;
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(originTransform.position, Is.EqualTo(expectedPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
}
[UnityTest]
public IEnumerator ApplyTransformation_ConstrainedOriginMovement()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
// Test without constrained manipulator
var motion = Vector3.forward;
var transformation = new XROriginMovement { motion = motion, forceUnconstrained = false, };
xrBodyTransformer.QueueTransformation(transformation);
var origin = xrOrigin.Origin.transform;
var expectedPosition = origin.position + motion;
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(origin.position, Is.EqualTo(expectedPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
// Test with character controller without collision
var characterController = xrOrigin.Origin.AddComponent<CharacterController>();
characterController.center = new Vector3(0f, 1f, 0f);
characterController.height = 2f;
characterController.radius = 0.5f;
var characterControllerManipulator = ScriptableObject.CreateInstance<CharacterControllerBodyManipulator>();
xrBodyTransformer.constrainedBodyManipulator = characterControllerManipulator;
xrBodyTransformer.QueueTransformation(transformation);
expectedPosition = origin.position + motion;
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(origin.position, Is.EqualTo(expectedPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
// Test with character controller with collision
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube).transform;
cube.gameObject.AddComponent<Rigidbody>();
cube.position = origin.position + motion + Vector3.up;
cube.rotation = origin.rotation;
cube.localScale = new Vector3(0.5f, 2f, 0.5f);
// Wait for physics to update after adding rigidbody
yield return new WaitForFixedUpdate();
yield return null;
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(characterController.collisionFlags.HasFlag(CollisionFlags.Sides));
Assert.That(characterControllerManipulator.lastCollisionFlags.HasFlag(CollisionFlags.Sides));
Object.Destroy(characterControllerManipulator);
}
[UnityTest]
public IEnumerator ApplyTransformation_BodyGroundPositioning()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var bodyPositionEvaluator = xrBodyTransformer.bodyPositionEvaluator;
// Make sure body starts out offset from origin
xrOrigin.Camera.transform.localPosition = Vector3.forward;
Assert.That(bodyPositionEvaluator.GetBodyGroundLocalPosition(xrOrigin), Is.Not.EqualTo(Vector3.zero).Using(Vector3ComparerWithEqualsOperator.Instance));
var targetPosition = new Vector3(1.5f, 2.5f, 3.5f);
var transformation = new XRBodyGroundPosition { targetPosition = targetPosition };
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(bodyPositionEvaluator.GetBodyGroundWorldPosition(xrOrigin), Is.EqualTo(targetPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
}
[UnityTest]
public IEnumerator ApplyTransformation_OriginUpAlignment()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var targetUp = new Vector3(1.5f, 2.5f, 3.5f);
var transformation = new XROriginUpAlignment { targetUp = targetUp };
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(xrOrigin.Origin.transform.up, Is.EqualTo(targetUp.normalized).Using(Vector3ComparerWithEqualsOperator.Instance));
}
[UnityTest]
public IEnumerator ApplyTransformation_BodyYawRotation()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var bodyPositionEvaluator = xrBodyTransformer.bodyPositionEvaluator;
// Make sure body starts out with some non-identity rotation and is offset from the origin
xrOrigin.Camera.transform.localPosition = new Vector3(1.5f, 2.5f, 3.5f);
Assert.That(bodyPositionEvaluator.GetBodyGroundLocalPosition(xrOrigin), Is.Not.EqualTo(Vector3.zero).Using(Vector3ComparerWithEqualsOperator.Instance));
var initialRotation = Quaternion.Euler(30f, 60f, 90f);
var origin = xrOrigin.Origin.transform;
origin.localRotation = initialRotation;
var initialBodyGroundPosition = bodyPositionEvaluator.GetBodyGroundWorldPosition(xrOrigin);
var angleDelta = 123f;
var expectedRotation = initialRotation * Quaternion.AngleAxis(angleDelta, Vector3.up);
var transformation = new XRBodyYawRotation { angleDelta = angleDelta };
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(origin.localRotation, Is.EqualTo(expectedRotation).Using(QuaternionEqualityComparer.Instance));
Assert.That(bodyPositionEvaluator.GetBodyGroundWorldPosition(xrOrigin), Is.EqualTo(initialBodyGroundPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
}
[UnityTest]
public IEnumerator ApplyTransformation_CameraForwardXZAlignment()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
// Make sure origin and camera start out with non-identity rotations and camera is offset from the origin
var cameraTransform = xrOrigin.Camera.transform;
cameraTransform.localPosition = new Vector3(1.5f, 2.5f, 3.5f);
cameraTransform.localRotation = Quaternion.Euler(30f, 60f, 90f);
var origin = xrOrigin.Origin.transform;
origin.localRotation = Quaternion.Euler(30f, 30f, 0f);
var initialCameraPosition = cameraTransform.position;
var targetDirection = Vector3.forward;
var transformation = new XRCameraForwardXZAlignment { targetDirection = targetDirection };
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
var projectedCamForward = Vector3.ProjectOnPlane(cameraTransform.forward, origin.up).normalized;
var projectedTargetForward = Vector3.ProjectOnPlane(targetDirection, origin.up).normalized;
Assert.That(projectedCamForward, Is.EqualTo(projectedTargetForward).Using(Vector3ComparerWithEqualsOperator.Instance));
Assert.That(cameraTransform.position, Is.EqualTo(initialCameraPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
}
[UnityTest]
public IEnumerator ApplyTransformation_BodyScaling()
{
var xrOrigin = TestUtilities.CreateXROrigin();
var xrBodyTransformer = TestUtilities.CreateXRBodyTransformer(xrOrigin);
var bodyPositionEvaluator = xrBodyTransformer.bodyPositionEvaluator;
// Make sure body starts out offset from origin
xrOrigin.Camera.transform.localPosition = new Vector3(1.5f, 2.5f, 3.5f);
Assert.That(bodyPositionEvaluator.GetBodyGroundLocalPosition(xrOrigin), Is.Not.EqualTo(Vector3.zero).Using(Vector3ComparerWithEqualsOperator.Instance));
var initialBodyGroundPosition = bodyPositionEvaluator.GetBodyGroundWorldPosition(xrOrigin);
var scale = 123f;
var transformation = new XRBodyScale { uniformScale = scale };
xrBodyTransformer.QueueTransformation(transformation);
yield return new WaitForFixedUpdate();
yield return null;
Assert.That(xrOrigin.Origin.transform.localScale, Is.EqualTo(Vector3.one * scale).Using(Vector3ComparerWithEqualsOperator.Instance));
var pos = bodyPositionEvaluator.GetBodyGroundWorldPosition(xrOrigin);
Assert.That(bodyPositionEvaluator.GetBodyGroundWorldPosition(xrOrigin), Is.EqualTo(initialBodyGroundPosition).Using(Vector3ComparerWithEqualsOperator.Instance));
}
}
}