public IEnumerator TestFollowStuckBehind() { // Instantiate our test GameObject with solver. var testObjects = InstantiateTestSolver <Follow>(); var followSolver = (Follow)testObjects.solver; followSolver.MoveToDefaultDistanceLerpTime = 0; testObjects.handler.TrackedTargetType = TrackedObjectType.Head; var targetTransform = testObjects.target.transform; // variables and lambdas to test direction remains within bounds Vector3 toTarget() => targetTransform.position - CameraCache.Main.transform.position; // Test without rotation TestUtilities.PlayspaceToOriginLookingForward(); yield return(new WaitForFixedUpdate()); yield return(null); Assert.Greater(Vector3.Dot(CameraCache.Main.transform.forward, toTarget()), 0, "Follow behind the player"); // Test y axis rotation MixedRealityPlayspace.PerformTransformation(p => p.Rotate(Vector3.up, 180)); yield return(new WaitForFixedUpdate()); yield return(null); Assert.Greater(Vector3.Dot(CameraCache.Main.transform.forward, toTarget()), 0, "Follow behind the player"); }
/// <summary> /// Instantiates a bounding box at 0, 0, -1.5f /// box is at scale .5, .5, .5 /// Target is set to its child if targetIsChild is true /// </summary> private BoundingBox InstantiateSceneAndDefaultBbox(GameObject target = null) { GameObject bboxGameObject; if (target != null) { bboxGameObject = new GameObject(); } else { bboxGameObject = GameObject.CreatePrimitive(PrimitiveType.Cube); } bboxGameObject.transform.position = boundingBoxStartCenter; bboxGameObject.transform.localScale = boundingBoxStartScale; BoundingBox bbox = bboxGameObject.AddComponent <BoundingBox>(); if (target != null) { target.transform.parent = bboxGameObject.transform; target.transform.localScale = Vector3.one; target.transform.localPosition = Vector3.zero; bbox.Target = target; } MixedRealityPlayspace.PerformTransformation( p => { p.position = Vector3.zero; p.LookAt(bboxGameObject.transform.position); }); bbox.Active = true; return(bbox); }
public IEnumerator TestTapToPlaceOnClickHead() { TestUtilities.PlayspaceToOriginLookingForward(); // Create a cube with Tap to Place attached and Head (default) as the TrackedTargetType var tapToPlaceObj = InstantiateTestSolver <TapToPlace>(); tapToPlaceObj.target.transform.position = Vector3.forward; TapToPlace tapToPlace = tapToPlaceObj.solver as TapToPlace; // Set hand position Vector3 handStartPosition = new Vector3(0, -0.1f, 0.6f); var leftHand = new TestHand(Handedness.Left); yield return(leftHand.Show(handStartPosition)); // Select Tap to Place Obj yield return(leftHand.Click()); // Make sure the object is being placed Assert.True(tapToPlace.IsBeingPlaced); // Move the playspace to simulate head movement MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.left * 1.5f; }); yield return(new WaitForFixedUpdate()); yield return(null); // Make sure the target obj has followed the head Assert.AreEqual(CameraCache.Main.transform.position.x, tapToPlaceObj.target.transform.position.x, "The tap to place object position.x does not match the camera position.x"); // Tap to place has a 0.5 sec timer between clicks to make sure a double click does not get registered // We need to wait at least 0.5 secs until another click is called or tap to place will ignore the action yield return(new WaitForSeconds(0.5f)); // Click object to stop placement yield return(leftHand.Click()); // Make sure the object is not being placed after the click Assert.False(tapToPlace.IsBeingPlaced); // Move the playspace to simulate head movement again MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.right; }); yield return(new WaitForFixedUpdate()); yield return(null); // Make sure the target obj is NOT following the head Assert.AreNotEqual(CameraCache.Main.transform.position.x, tapToPlaceObj.target.transform.position.x, "The tap to place object position.x matches camera position.x, when it should not"); }
/// <summary> /// Creates a playspace and moves it into a default position. /// </summary> public static void InitializePlayspace() { MixedRealityPlayspace.PerformTransformation( p => { p.position = new Vector3(1.0f, 1.5f, -2.0f); p.LookAt(Vector3.zero); }); }
/// <summary> /// Force the playspace camera into the specified position and orientation. /// </summary> /// <param name="position">World space position for the playspace.</param> /// <param name="rotation">World space orientation for the playspace.</param> /// <remarks> /// Note that this has no effect on the camera's local space transform, but /// will change the camera's world space position. If and only if the camera's /// local transform is identity with the camera's world transform equal the playspace's. /// </remarks> public static void PlayspaceToPositionAndRotation(Vector3 position, Quaternion rotation) { MixedRealityPlayspace.PerformTransformation( p => { p.position = position; p.rotation = rotation; }); }
/// <summary> /// Forces the playspace camera to face forward. /// </summary> public static void PlayspaceToOriginLookingForward() { // Move the camera to origin looking at +z to more easily see the a target at 0,0,0 MixedRealityPlayspace.PerformTransformation( p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); }
public IEnumerator SetTarget() { // create cube without control var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = boundingBoxStartCenter; MixedRealityPlayspace.PerformTransformation( p => { p.position = Vector3.zero; p.LookAt(cube.transform.position); }); cube.transform.localScale = boundingBoxStartScale; // create another gameobject with boundscontrol attached var emptyGameObject = new GameObject("empty"); BoundingBox bbox = emptyGameObject.AddComponent <BoundingBox>(); // set target to cube bbox.Target = cube; bbox.Active = true; // front right corner is corner 3 var frontRightCornerPos = cube.transform.Find("rigRoot/corner_3").position; // grab corner and scale object Vector3 initialHandPosition = new Vector3(0, 0, 0.5f); int numSteps = 30; var delta = new Vector3(0.1f, 0.1f, 0f); TestHand hand = new TestHand(Handedness.Right); yield return(hand.Show(initialHandPosition)); yield return(hand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint)); yield return(hand.MoveTo(frontRightCornerPos, numSteps)); yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); yield return(hand.MoveTo(frontRightCornerPos + delta, numSteps)); var endBounds = cube.GetComponent <BoxCollider>().bounds; Vector3 expectedCenter = new Vector3(0.033f, 0.033f, 1.467f); Vector3 expectedSize = Vector3.one * .567f; TestUtilities.AssertAboutEqual(endBounds.center, expectedCenter, "endBounds incorrect center"); TestUtilities.AssertAboutEqual(endBounds.size, expectedSize, "endBounds incorrect size"); Object.Destroy(emptyGameObject); Object.Destroy(cube); // Wait for a frame to give Unity a change to actually destroy the object yield return(null); }
public IEnumerator TestFollowOrientation() { // Instantiate our test GameObject with solver. var testObjects = InstantiateTestSolver <Follow>(); var followSolver = (Follow)testObjects.solver; followSolver.MoveToDefaultDistanceLerpTime = 0; testObjects.handler.TrackedTargetType = TrackedObjectType.Head; var targetTransform = testObjects.target.transform; // Test orientation deadzone followSolver.OrientToControllerDeadzoneDegrees = 70; MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.back; p.LookAt(Vector3.forward); }); yield return(new WaitForFixedUpdate()); yield return(null); Assert.AreEqual(targetTransform.rotation, Quaternion.identity, "Target rotated before we moved beyond the deadzone"); MixedRealityPlayspace.PerformTransformation(p => p.RotateAround(Vector3.zero, Vector3.up, 45)); yield return(new WaitForFixedUpdate()); yield return(null); Assert.AreEqual(targetTransform.rotation, Quaternion.identity, "Target rotated before we moved beyond the deadzone"); MixedRealityPlayspace.PerformTransformation(p => p.RotateAround(Vector3.zero, Vector3.up, 45)); yield return(new WaitForFixedUpdate()); yield return(null); Assert.AreNotEqual(targetTransform.rotation, Quaternion.identity, "Target did not rotate after we moved beyond the deadzone"); // Test FaceUserDefinedTargetTransform var hand = new TestHand(Handedness.Right); yield return(hand.Show(Vector3.forward + Vector3.right)); testObjects.handler.TrackedTargetType = TrackedObjectType.HandJoint; followSolver.FaceUserDefinedTargetTransform = true; followSolver.TargetToFace = CameraCache.Main.transform; Assert.AreEqual(Quaternion.LookRotation(targetTransform.position - CameraCache.Main.transform.position), targetTransform.rotation); yield return(hand.MoveTo(Vector3.forward + Vector3.left, 1)); yield return(null); Assert.AreEqual(Quaternion.LookRotation(targetTransform.position - CameraCache.Main.transform.position), targetTransform.rotation); }
public IEnumerator TestFollowDirection() { // Instantiate our test GameObject with solver. var testObjects = InstantiateTestSolver <Follow>(); var followSolver = (Follow)testObjects.solver; followSolver.MoveToDefaultDistanceLerpTime = 0; testObjects.handler.TrackedTargetType = TrackedObjectType.Head; var targetTransform = testObjects.target.transform; // variables and lambdas to test direction remains within bounds var maxXAngle = followSolver.MaxViewHorizontalDegrees / 2; var maxYAngle = followSolver.MaxViewVerticalDegrees / 2; Vector3 directionToHead() => CameraCache.Main.transform.position - targetTransform.position; float xAngle() => (Mathf.Acos(Vector3.Dot(directionToHead(), targetTransform.right)) * Mathf.Rad2Deg) - 90; float yAngle() => 90 - (Mathf.Acos(Vector3.Dot(directionToHead(), targetTransform.up)) * Mathf.Rad2Deg); // Test without rotation TestUtilities.PlayspaceToOriginLookingForward(); yield return(new WaitForFixedUpdate()); yield return(null); Assert.LessOrEqual(Mathf.Abs(xAngle()), maxXAngle, "Follow exceeded the max horizontal angular bounds"); Assert.LessOrEqual(Mathf.Abs(yAngle()), maxYAngle, "Follow exceeded the max vertical angular bounds"); // Test y axis rotation MixedRealityPlayspace.PerformTransformation(p => p.Rotate(Vector3.up, 45)); yield return(new WaitForFixedUpdate()); yield return(null); Assert.LessOrEqual(Mathf.Abs(xAngle()), maxXAngle, "Follow exceeded the max horizontal angular bounds"); Assert.LessOrEqual(Mathf.Abs(yAngle()), maxYAngle, "Follow exceeded the max vertical angular bounds"); // Test x axis rotation MixedRealityPlayspace.PerformTransformation(p => p.Rotate(Vector3.right, 45)); yield return(new WaitForFixedUpdate()); yield return(null); Assert.LessOrEqual(Mathf.Abs(xAngle()), maxXAngle, "Follow exceeded the max horizontal angular bounds"); Assert.LessOrEqual(Mathf.Abs(yAngle()), maxYAngle, "Follow exceeded the max vertical angular bounds"); // Test translation MixedRealityPlayspace.PerformTransformation(p => p.Translate(Vector3.back, Space.World)); yield return(new WaitForFixedUpdate()); yield return(null); Assert.LessOrEqual(Mathf.Abs(xAngle()), maxXAngle, "Follow exceeded the max horizontal angular bounds"); Assert.LessOrEqual(Mathf.Abs(yAngle()), maxYAngle, "Follow exceeded the max vertical angular bounds"); }
public IEnumerator ZoomRotatedSlate() { // Configuring camera and hands to interact with rotated slate InstantiateFromPrefab(Vector3.right, Quaternion.LookRotation(Vector3.right)); MixedRealityPlayspace.PerformTransformation(p => p.Rotate(Vector3.up, 90)); yield return(null); // Right hand pinches slate TestHand handRight = new TestHand(Handedness.Right); yield return(handRight.Show(panZoom.transform.position + Vector3.forward * -0.1f + Vector3.right * -0.3f)); yield return(handRight.SetGesture(ArticulatedHandPose.GestureId.Pinch)); // Left hand pinches slate TestHand handLeft = new TestHand(Handedness.Left); yield return(handLeft.Show(panZoom.transform.position + Vector3.forward * 0.1f + Vector3.right * -0.3f)); yield return(handLeft.SetGesture(ArticulatedHandPose.GestureId.Pinch)); // Use both hands to zoom in yield return(handRight.Move(new Vector3(0f, 0f, -0.1f), 5)); yield return(handLeft.Move(new Vector3(0f, 0f, 0.1f), 5)); Assert.AreEqual(0.6, panZoom.CurrentScale, 0.1, "Rotated slate did not zoom in using near interaction"); // Reset slate and hands configuration panZoom.Reset(); yield return(handRight.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(handLeft.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(null); // Both hands touch slate yield return(handRight.MoveTo(panZoom.transform.position + Vector3.forward * -0.1f)); yield return(handLeft.MoveTo(panZoom.transform.position + Vector3.forward * 0.1f)); yield return(null); // Use both hands to zoom in yield return(handRight.Move(new Vector3(0f, 0f, -0.1f), 5)); yield return(handLeft.Move(new Vector3(0f, 0f, 0.1f), 5)); Assert.AreEqual(0.6, panZoom.CurrentScale, 0.1, "Rotated slate did not zoom in using far interaction"); yield return(handRight.Hide()); yield return(handLeft.Hide()); }
public IEnumerator TestTapToPlaceCodeConfigurability() { TestUtilities.PlayspaceToOriginLookingForward(); // Create a cube with Tap to Place attached var tapToPlaceObj = InstantiateTestSolver <TapToPlace>(); tapToPlaceObj.target.transform.position = Vector3.forward; TapToPlace tapToPlace = tapToPlaceObj.solver as TapToPlace; // Start Placing the object immediately tapToPlace.AutoStart = true; Vector3 handStartPosition = new Vector3(0, -0.1f, 0.6f); var leftHand = new TestHand(Handedness.Left); yield return(leftHand.Show(handStartPosition)); // Make sure the object is being placed after setting AutoStart Assert.True(tapToPlace.IsBeingPlaced); // Move the playspace to simulate head movement MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.left * 1.5f; }); yield return(new WaitForFixedUpdate()); yield return(null); // Make sure the target obj is following the head Assert.AreEqual(CameraCache.Main.transform.position.x, tapToPlaceObj.target.transform.position.x, "The tap to place object position.x does not match the camera position.x"); // Stop the placement via code instead of click from the hand tapToPlace.StopPlacement(); Assert.False(tapToPlace.IsBeingPlaced); // Move the playspace to simulate head movement again MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.right; }); yield return(new WaitForFixedUpdate()); yield return(null); // Make sure the target obj is NOT following the head Assert.AreNotEqual(CameraCache.Main.transform.position.x, tapToPlaceObj.target.transform.position.x, "The tap to place object position.x matches the camera position.x, when it should not"); }
public IEnumerator TestSolverSwap() { // Reset view to origin MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); // Instantiate and setup RadialView to place object in the view center. var testObjects = InstantiateTestSolver <RadialView>(); RadialView radialViewSolver = (RadialView)testObjects.solver; radialViewSolver.MinDistance = 2.0f; radialViewSolver.MaxDistance = 2.0f; radialViewSolver.MinViewDegrees = 0.0f; radialViewSolver.MaxViewDegrees = 0.0f; // Let RadialView update the target object yield return(WaitForFrames(2)); // Make sure Radial View is placing object in center of View, so we can later check that a solver swap actually moved the target object. TestUtilities.AssertAboutEqual(testObjects.target.transform.position, Vector3.forward * 2.0f, "RadialView does not place object in center of view"); // Disable the old solver radialViewSolver.enabled = false; // Add a another solver during runtime, give him a specific location to check whether the new solver updates the target object. Orbital orbitalSolver = AddSolverComponent <Orbital>(testObjects.target); orbitalSolver.WorldOffset = Vector3.zero; orbitalSolver.LocalOffset = Vector3.down * 2.0f; // Let Orbital update the target object yield return(WaitForFrames(2)); // Make sure Orbital is now updating the target object TestUtilities.AssertAboutEqual(testObjects.target.transform.position, Vector3.down * 2.0f, "Orbital solver did not place object below origin"); // Swap solvers once again during runtime radialViewSolver.enabled = true; orbitalSolver.enabled = false; // Let RadialView update the target object yield return(WaitForFrames(2)); // Make sure Radial View is now updating the target object once again. TestUtilities.AssertAboutEqual(testObjects.target.transform.position, Vector3.forward * 2.0f, "RadialView solver did not place object in center of view"); }
public IEnumerator TestEyeTrackingTargetColliderOnParent() { // Eye tracking configuration profile should set eye based gaze var profile = AssetDatabase.LoadAssetAtPath(eyeTrackingConfigurationProfilePath, typeof(MixedRealityToolkitConfigurationProfile)) as MixedRealityToolkitConfigurationProfile; MixedRealityToolkit.Instance.ResetConfiguration(profile); // Reset view to origin MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); // Build a target to collide against var parentOfTargetObject = GameObject.CreatePrimitive(PrimitiveType.Cube); parentOfTargetObject.name = "ParentOfTargetObject"; parentOfTargetObject.AddComponent <BoxCollider>(); parentOfTargetObject.transform.localScale = new Vector3(25.0f, 25.0f, 0.2f); parentOfTargetObject.transform.Rotate(Vector3.up, 180.0f); // Rotate parentOfTargetObject so forward faces camera parentOfTargetObject.transform.position = Vector3.forward * 10.0f; var childOfTargetObject = GameObject.CreatePrimitive(PrimitiveType.Cube); childOfTargetObject.name = "childOfTargetObject"; var eyeTrackingTargetComponent = childOfTargetObject.AddComponent <EyeTrackingTarget>(); childOfTargetObject.transform.localScale = new Vector3(20.0f, 20.0f, 0.1f); childOfTargetObject.transform.SetParent(parentOfTargetObject.transform); //We need to simulate an eye in the direction of the parent object var inputSimulationService = PlayModeTestUtilities.GetInputSimulationService(); //We don't need any other input just eye gaze inputSimulationService.UserInputEnabled = false; inputSimulationService.EyeGazeSimulationMode = EyeGazeSimulationMode.CameraForwardAxis; yield return(null); var isEyeGazeActive = InputRayUtils.TryGetEyeGazeRay(out var eyegazeRay); Assert.True(isEyeGazeActive); yield return(null); Assert.True(EyeTrackingTarget.LookedAtTarget != null); Assert.True(EyeTrackingTarget.LookedAtEyeTarget.name == "childOfTargetObject"); }
/// <summary> /// Creates a playspace and moves it into a default position. /// </summary> public static void InitializePlayspace() { if (MixedRealityPlayspace.Transform.parent == null) { GameObject gameObject = new GameObject("MRTKPlayspaceTestParent"); MixedRealityPlayspace.Transform.parent = gameObject.transform; gameObject.transform.position = ArbitraryParentPose.position; gameObject.transform.rotation = ArbitraryParentPose.rotation; } MixedRealityPlayspace.PerformTransformation( p => { p.position = new Vector3(1.0f, 1.5f, -2.0f); p.LookAt(Vector3.zero); }); }
public IEnumerator Test01_WhizzySphere() { TestUtilities.InitializeMixedRealityToolkitAndCreateScenes(true); MixedRealityPlayspace.PerformTransformation( p => { p.position = new Vector3(1.0f, 1.5f, -2.0f); p.LookAt(Vector3.zero); }); var testLight = new GameObject("TestLight"); var light = testLight.AddComponent <Light>(); light.type = LightType.Directional; testLight.transform.position = new Vector3(-2.5f, 3.0f, -1.2f); testLight.transform.LookAt(Vector3.zero); var testObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); testObject.transform.localScale = Vector3.one * 0.1f; var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); while (stopwatch.Elapsed.TotalSeconds < 10.0f) { float t = (float)stopwatch.Elapsed.TotalSeconds; var pi2 = 2.0f * Mathf.PI; var a = 0.5f * t; var b = 0.75f * t; var c = 0.9f * t; testObject.transform.position = new Vector3( Mathf.Sin((a + 0.0f) * pi2), Mathf.Sin((b + 0.333f) * pi2), Mathf.Sin((c + 0.666f) * pi2) ); yield return(new WaitForFixedUpdate()); } stopwatch.Stop(); GameObject.Destroy(testLight); GameObject.Destroy(testObject); // Wait for a frame to give Unity a change to actually destroy the object yield return(null); }
public IEnumerator TestDirectionalIndicator() { // Reset view to origin MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); const float ANGLE_THRESHOLD = 30.0f; var directionTarget = GameObject.CreatePrimitive(PrimitiveType.Cylinder); directionTarget.transform.position = 10.0f * Vector3.right; // Instantiate our test gameobject with solver. var testObjects = InstantiateTestSolver <DirectionalIndicator>(); var indicatorSolver = testObjects.solver as DirectionalIndicator; indicatorSolver.DirectionalTarget = directionTarget.transform; var indicatorMesh = indicatorSolver.GetComponent <Renderer>(); // Test that solver points to the right and is visible yield return(WaitForFrames(2)); Assert.LessOrEqual(Vector3.Angle(indicatorSolver.transform.up, directionTarget.transform.position.normalized), ANGLE_THRESHOLD); Assert.IsTrue(indicatorMesh.enabled); directionTarget.transform.position = -10.0f * Vector3.right; // Test that solver points to the left now and is visible yield return(WaitForFrames(2)); Assert.LessOrEqual(Vector3.Angle(indicatorSolver.transform.up, directionTarget.transform.position.normalized), ANGLE_THRESHOLD); Assert.IsTrue(indicatorMesh.enabled); // Test that the solver is invisible directionTarget.transform.position = 5.0f * Vector3.forward; yield return(WaitForFrames(2)); Assert.IsFalse(indicatorMesh.enabled); }
/// <summary> /// Instantiates a bounds control at boundsControlStartCenter /// box is at scale boundsControlStartScale /// </summary> private BoundsControl InstantiateSceneAndDefaultBbox() { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = boundsControlStartCenter; BoundsControl bbox = cube.AddComponent <BoundsControl>(); MixedRealityPlayspace.PerformTransformation( p => { p.position = Vector3.zero; p.LookAt(cube.transform.position); }); bbox.transform.localScale = boundsControlStartScale; bbox.Active = true; return(bbox); }
/// <summary> /// Instantiates a bounding box at 0, 0, -1.5f /// box is at scale .5, .5, .5 /// </summary> private BoundingBox InstantiateSceneAndDefaultBbox() { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = Vector3.forward * 1.5f; BoundingBox bbox = cube.AddComponent <BoundingBox>(); MixedRealityPlayspace.PerformTransformation( p => { p.position = Vector3.zero; p.LookAt(cube.transform.position); }); bbox.transform.localScale *= 0.5f; bbox.Active = true; return(bbox); }
public IEnumerator TestEyeTrackingTarget() { // Eye tracking configuration profile should set eye based gaze var profile = AssetDatabase.LoadAssetAtPath(eyeTrackingConfigurationProfilePath, typeof(MixedRealityToolkitConfigurationProfile)) as MixedRealityToolkitConfigurationProfile; MixedRealityToolkit.Instance.ResetConfiguration(profile); // Reset view to origin MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); string targetName = "eyetrackingTargetObject"; var eyetrackingTargetObject = GameObject.CreatePrimitive(PrimitiveType.Cube); eyetrackingTargetObject.name = targetName; var eyeTrackingTargetComponent = eyetrackingTargetObject.AddComponent <EyeTrackingTarget>(); eyetrackingTargetObject.transform.position = Vector3.forward; // We need to simulate an eye in the direction of the parent object var inputSimulationService = PlayModeTestUtilities.GetInputSimulationService(); // We don't need any other input just eye gaze inputSimulationService.UserInputEnabled = false; inputSimulationService.EyeGazeSimulationMode = EyeGazeSimulationMode.CameraForwardAxis; yield return(null); var isEyeGazeActive = InputRayUtils.TryGetEyeGazeRay(out var eyegazeRay); Assert.True(isEyeGazeActive); yield return(null); Assert.True(EyeTrackingTarget.LookedAtTarget != null); Assert.True(EyeTrackingTarget.LookedAtEyeTarget.name == targetName); }
public IEnumerator ManipulationHandlerOneHandMoveNear() { // set up cube with manipulation handler var testObject = GameObject.CreatePrimitive(PrimitiveType.Cube); testObject.transform.localScale = Vector3.one * 0.2f; Vector3 initialObjectPosition = new Vector3(0f, 0f, 1f); testObject.transform.position = initialObjectPosition; var manipHandler = testObject.AddComponent <ManipulationHandler>(); manipHandler.HostTransform = testObject.transform; manipHandler.SmoothingActive = false; manipHandler.ManipulationType = ManipulationHandler.HandMovementType.OneHandedOnly; // add near interaction grabbable to be able to grab the cube with the simulated articulated hand testObject.AddComponent <NearInteractionGrabbable>(); yield return(new WaitForFixedUpdate()); yield return(null); const int numCircleSteps = 10; const int numHandSteps = 3; Vector3 initialHandPosition = new Vector3(0, 0, 0.5f); Vector3 initialGrabPosition = new Vector3(-0.1f, -0.1f, 1f); // grab the left bottom corner of the cube TestHand hand = new TestHand(Handedness.Right); // do this test for every one hand rotation mode foreach (ManipulationHandler.RotateInOneHandType type in Enum.GetValues(typeof(ManipulationHandler.RotateInOneHandType))) { manipHandler.OneHandRotationModeNear = type; TestUtilities.PlayspaceToOriginLookingForward(); yield return(hand.Show(initialHandPosition)); var pointer = hand.GetPointer <SpherePointer>(); Assert.IsNotNull(pointer); yield return(hand.MoveTo(initialGrabPosition, numHandSteps)); yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); // save relative pos grab point to object Vector3 initialOffsetGrabToObjPivot = pointer.Position - testObject.transform.position; Vector3 initialGrabPointInObject = testObject.transform.InverseTransformPoint(manipHandler.GetPointerGrabPoint(pointer.PointerId)); // full circle const int degreeStep = 360 / numCircleSteps; // rotating the pointer in a circle around "the user" for (int i = 1; i <= numCircleSteps; ++i) { // rotate main camera (user) MixedRealityPlayspace.PerformTransformation( p => { p.position = MixedRealityPlayspace.Position; Vector3 rotatedFwd = Quaternion.AngleAxis(degreeStep * i, Vector3.up) * Vector3.forward; p.LookAt(rotatedFwd); }); yield return(null); // move hand with the camera Vector3 newHandPosition = Quaternion.AngleAxis(degreeStep * i, Vector3.up) * initialGrabPosition; yield return(hand.MoveTo(newHandPosition, numHandSteps)); if (type == ManipulationHandler.RotateInOneHandType.RotateAboutObjectCenter) { // make sure that the offset between hand and object centre hasn't changed while rotating Vector3 offsetRotated = pointer.Position - testObject.transform.position; TestUtilities.AssertAboutEqual(offsetRotated, initialOffsetGrabToObjPivot, $"Object offset changed during rotation using {type}"); } else { // make sure that the offset between grab point and object pivot hasn't changed while rotating Vector3 grabPoint = manipHandler.GetPointerGrabPoint(pointer.PointerId); Vector3 cornerRotated = testObject.transform.TransformPoint(initialGrabPointInObject); TestUtilities.AssertAboutEqual(cornerRotated, grabPoint, $"Grab point on object changed during rotation using {type}"); } } yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(hand.Hide()); } }
public IEnumerator ManipulationHandlerOneHandMoveFar() { // set up cube with manipulation handler var testObject = GameObject.CreatePrimitive(PrimitiveType.Cube); testObject.transform.localScale = Vector3.one * 0.2f; Vector3 initialObjectPosition = new Vector3(0f, 0f, 1f); testObject.transform.position = initialObjectPosition; var manipHandler = testObject.AddComponent <ManipulationHandler>(); manipHandler.HostTransform = testObject.transform; manipHandler.SmoothingActive = false; manipHandler.ManipulationType = ManipulationHandler.HandMovementType.OneHandedOnly; // add near interaction grabbable to be able to grab the cube with the simulated articulated hand testObject.AddComponent <NearInteractionGrabbable>(); yield return(new WaitForFixedUpdate()); yield return(null); const int numCircleSteps = 10; const int numHandSteps = 3; Vector3 initialHandPosition = new Vector3(0.04f, -0.18f, 0.3f); // grab point on the lower center part of the cube TestHand hand = new TestHand(Handedness.Right); // do this test for every one hand rotation mode foreach (ManipulationHandler.RotateInOneHandType type in Enum.GetValues(typeof(ManipulationHandler.RotateInOneHandType))) { // TODO: grab point is moving in this test and has to be covered by a different test if (type == ManipulationHandler.RotateInOneHandType.MaintainOriginalRotation) { continue; } manipHandler.OneHandRotationModeFar = type; TestUtilities.PlayspaceToOriginLookingForward(); yield return(hand.Show(initialHandPosition)); yield return(null); // pinch and let go of the object again to make sure that any rotation adjustment we're doing is applied // at the beginning of our test and doesn't interfere with our grab position on the cubes surface while we're moving around yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); yield return(new WaitForFixedUpdate()); yield return(null); yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); // save relative pos grab point to object - for far interaction we need to check the grab point where the pointer ray hits the manipulated object InputSimulationService simulationService = PlayModeTestUtilities.GetInputSimulationService(); IMixedRealityController[] inputControllers = simulationService.GetActiveControllers(); // assume hand is first controller and pointer for this test IMixedRealityController handController = inputControllers[0]; IMixedRealityPointer handPointer = handController.InputSource.Pointers[0]; Vector3 initialGrabPosition = handPointer.Result.Details.Point; Vector3 initialOffsetGrabToObjPivot = MixedRealityPlayspace.InverseTransformPoint(initialGrabPosition) - MixedRealityPlayspace.InverseTransformPoint(testObject.transform.position); // full circle const int degreeStep = 360 / numCircleSteps; // rotating the pointer in a circle around "the user" for (int i = 1; i <= numCircleSteps; ++i) { // rotate main camera (user) MixedRealityPlayspace.PerformTransformation( p => { p.position = MixedRealityPlayspace.Position; Vector3 rotatedFwd = Quaternion.AngleAxis(degreeStep * i, Vector3.up) * Vector3.forward; p.LookAt(rotatedFwd); }); yield return(null); // move hand with the camera Vector3 newHandPosition = Quaternion.AngleAxis(degreeStep * i, Vector3.up) * initialHandPosition; yield return(hand.MoveTo(newHandPosition, numHandSteps)); yield return(new WaitForFixedUpdate()); yield return(null); // make sure that the offset between grab point and object pivot hasn't changed while rotating Vector3 newGrabPosition = handPointer.Result.Details.Point; Vector3 offsetRotated = MixedRealityPlayspace.InverseTransformPoint(newGrabPosition) - MixedRealityPlayspace.InverseTransformPoint(testObject.transform.position); TestUtilities.AssertAboutEqual(offsetRotated, initialOffsetGrabToObjPivot, "Grab point on object changed during rotation"); } yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(hand.Hide()); } }
public IEnumerator TestFollowDistance() { // Reset view to origin TestUtilities.PlayspaceToOriginLookingForward(); // Instantiate our test GameObject with solver. var testObjects = InstantiateTestSolver <Follow>(); var followSolver = (Follow)testObjects.solver; followSolver.MoveToDefaultDistanceLerpTime = 0; testObjects.handler.TrackedTargetType = TrackedObjectType.Head; var targetTransform = testObjects.target.transform; yield return(new WaitForFixedUpdate()); yield return(null); // Test distance remains within min/max bounds float distanceToHead = Vector3.Distance(targetTransform.position, CameraCache.Main.transform.position); Assert.LessOrEqual(distanceToHead, followSolver.MaxDistance, "Follow exceeded max distance"); Assert.GreaterOrEqual(distanceToHead, followSolver.MinDistance, "Follow subceeded min distance"); MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.back * 2; }); yield return(new WaitForFixedUpdate()); yield return(null); distanceToHead = Vector3.Distance(targetTransform.position, CameraCache.Main.transform.position); Assert.LessOrEqual(distanceToHead, followSolver.MaxDistance, "Follow exceeded max distance"); Assert.GreaterOrEqual(distanceToHead, followSolver.MinDistance, "Follow subceeded min distance"); MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.forward * 4; }); yield return(new WaitForFixedUpdate()); yield return(null); distanceToHead = Vector3.Distance(targetTransform.position, CameraCache.Main.transform.position); Assert.LessOrEqual(distanceToHead, followSolver.MaxDistance, "Follow exceeded max distance"); Assert.GreaterOrEqual(distanceToHead, followSolver.MinDistance, "Follow subceeded min distance"); // Test VerticalMaxDistance followSolver.VerticalMaxDistance = 0.1f; targetTransform.position = Vector3.forward; targetTransform.rotation = Quaternion.identity; MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); yield return(new WaitForFixedUpdate()); yield return(null); MixedRealityPlayspace.PerformTransformation(p => { p.LookAt(Vector3.forward + Vector3.up); }); yield return(new WaitForFixedUpdate()); yield return(null); float yDistance = targetTransform.position.y - CameraCache.Main.transform.position.y; Assert.AreEqual(followSolver.VerticalMaxDistance, yDistance); followSolver.VerticalMaxDistance = 0f; }
/// <summary> /// Manipulates the given testObject in a number of ways and records the output here /// </summary> /// <param name="testObject">An unrotated primitive cube at (0, 0, 1) with scale (0.2, 0.2, 0,2)</param> public IEnumerator RecordTransformValues(GameObject testObject) { TestUtilities.PlayspaceToOriginLookingForward(); float testRotation = 45; Quaternion testQuaternion = Quaternion.Euler(testRotation, testRotation, testRotation); Vector3 leftHandNearPos = new Vector3(-0.1f, 0, 1); Vector3 rightHandNearPos = new Vector3(0.1f, 0, 1); Vector3 leftHandFarPos = new Vector3(-0.06f, -0.1f, 0.5f); Vector3 rightHandFarPos = new Vector3(0.06f, -0.1f, 0.5f); TestHand leftHand = new TestHand(Handedness.Left); TestHand rightHand = new TestHand(Handedness.Right); // One hand rotate near yield return(rightHand.MoveTo(rightHandNearPos, numSteps)); yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); yield return(rightHand.SetRotation(testQuaternion, numSteps)); RecordTransform(testObject.transform, "one hand rotate near"); // Two hand rotate/scale near yield return(rightHand.SetRotation(Quaternion.identity, numSteps)); yield return(leftHand.MoveTo(leftHandNearPos, numSteps)); yield return(leftHand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); yield return(rightHand.Move(new Vector3(0.2f, 0.2f, 0), numSteps)); yield return(leftHand.Move(new Vector3(-0.2f, -0.2f, 0), numSteps)); RecordTransform(testObject.transform, "two hand rotate/scale near"); // Two hand rotate/scale far yield return(rightHand.MoveTo(rightHandNearPos, numSteps)); yield return(leftHand.MoveTo(leftHandNearPos, numSteps)); yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(leftHand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(rightHand.MoveTo(rightHandFarPos, numSteps)); yield return(leftHand.MoveTo(leftHandFarPos, numSteps)); yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); yield return(leftHand.SetGesture(ArticulatedHandPose.GestureId.Pinch)); yield return(rightHand.Move(new Vector3(0.2f, 0.2f, 0), numSteps)); yield return(leftHand.Move(new Vector3(-0.2f, -0.2f, 0), numSteps)); RecordTransform(testObject.transform, "two hand rotate/scale far"); // One hand rotate near yield return(rightHand.MoveTo(rightHandFarPos, numSteps)); yield return(leftHand.MoveTo(leftHandFarPos, numSteps)); yield return(leftHand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(leftHand.Hide()); MixedRealityPlayspace.PerformTransformation( p => { p.position = MixedRealityPlayspace.Position; Vector3 rotatedFwd = Quaternion.AngleAxis(testRotation, Vector3.up) * Vector3.forward; p.LookAt(rotatedFwd); }); yield return(null); Vector3 newHandPosition = Quaternion.AngleAxis(testRotation, Vector3.up) * rightHandFarPos; yield return(rightHand.MoveTo(newHandPosition, numSteps)); RecordTransform(testObject.transform, "one hand rotate far"); yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open)); yield return(rightHand.Hide()); }
public IEnumerator TestSurfaceMagnetism() { // Reset view to origin MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(Vector3.forward); }); // Build wall to collide against var wall = GameObject.CreatePrimitive(PrimitiveType.Cube); wall.transform.localScale = new Vector3(25.0f, 25.0f, 0.2f); wall.transform.Rotate(Vector3.up, 180.0f); // Rotate wall so forward faces camera wall.transform.position = Vector3.forward * 10.0f; yield return(WaitForFrames(2)); // Instantiate our test GameObject with solver. // Set layer to ignore raycast so solver doesn't raycast itself (i.e BoxCollider) var testObjects = InstantiateTestSolver <SurfaceMagnetism>(); testObjects.target.layer = LayerMask.NameToLayer("Ignore Raycast"); SurfaceMagnetism surfaceMag = testObjects.solver as SurfaceMagnetism; var targetTransform = testObjects.target.transform; var cameraTransform = CameraCache.Main.transform; yield return(WaitForFrames(2)); // Confirm that the surfacemagnetic cube is about on the wall straight ahead Assert.LessOrEqual(Vector3.Distance(targetTransform.position, wall.transform.position), DistanceThreshold); // Rotate the camera Vector3 cameraDir = Vector3.forward + Vector3.right; MixedRealityPlayspace.PerformTransformation(p => { p.position = Vector3.zero; p.LookAt(cameraDir); }); // Calculate where our camera hits the wall RaycastHit hitInfo; Assert.IsTrue(UnityEngine.Physics.Raycast(Vector3.zero, cameraDir, out hitInfo), "Raycast from camera did not hit wall"); // Let SurfaceMagnetism update yield return(WaitForFrames(2)); // Confirm that the surfacemagnetic cube is on the wall with camera rotated Assert.LessOrEqual(Vector3.Distance(targetTransform.position, hitInfo.point), DistanceThreshold); // Default orientation mode is TrackedTarget, test object should be facing camera Assert.IsTrue(Mathf.Approximately(-1.0f, Vector3.Dot(targetTransform.forward.normalized, cameraTransform.forward.normalized))); // Change default orientation mode to surface normal surfaceMag.CurrentOrientationMode = SurfaceMagnetism.OrientationMode.SurfaceNormal; yield return(WaitForFrames(2)); // Test object should now be facing into the wall (i.e Z axis) Assert.IsTrue(Mathf.Approximately(1.0f, Vector3.Dot(targetTransform.forward.normalized, Vector3.forward))); }
public IEnumerator TestSimulatedGlobalSelectInputOnPrefab() { // Face the camera in the opposite direction so we don't focus on button MixedRealityPlayspace.PerformTransformation( p => { p.position = Vector3.zero; p.LookAt(Vector3.back); }); // Load interactable prefab GameObject interactableObject; Interactable interactable; Transform translateTargetObject; // Place out of the way of any pointers InstantiateDefaultInteractablePrefab( new Vector3(10f, 0.0f, 0.5f), new Vector3(-90f, 0f, 0f), out interactableObject, out interactable, out translateTargetObject); // Subscribe to interactable's on click so we know the click went through bool wasClicked = false; interactable.OnClick.AddListener(() => { wasClicked = true; }); // Set interactable to global interactable.IsGlobal = true; Vector3 targetStartPosition = translateTargetObject.localPosition; yield return(null); // Find an input source to associate with the input event (doesn't matter which one) IMixedRealityInputSource defaultInputSource = MixedRealityToolkit.InputSystem.DetectedInputSources.FirstOrDefault(); Assert.NotNull(defaultInputSource, "At least one input source must be present for this test to work."); // Add interactable as a global listener // This is only necessary if IsGlobal is being set manually. If it's set in the inspector, interactable will register itself in OnEnable automatically. MixedRealityToolkit.InputSystem.PushModalInputHandler(interactableObject); // Raise a select down input event, then wait for transition to take place MixedRealityToolkit.InputSystem.RaiseOnInputDown(defaultInputSource, Handedness.None, interactable.InputAction); // Wait for at least one frame explicitly to ensure the input goes through yield return(new WaitForFixedUpdate()); float pressStartTime = Time.time; bool wasTranslated = false; while (Time.time < pressStartTime + buttonPressAnimationDelay) { // If the transform is moved at any point during this interval, we were successful yield return(new WaitForFixedUpdate()); wasTranslated |= targetStartPosition != translateTargetObject.localPosition; } // Raise a select up input event, then wait for transition to take place MixedRealityToolkit.InputSystem.RaiseOnInputUp(defaultInputSource, Handedness.Right, interactable.InputAction); // Wait for at least one frame explicitly to ensure the input goes through yield return(new WaitForFixedUpdate()); yield return(new WaitForSeconds(buttonReleaseAnimationDelay)); Assert.True(wasClicked, "Interactable was not clicked."); Assert.True(wasTranslated, "Transform target object was not translated by action."); Assert.False(interactable.HasFocus, "Interactable had focus"); // Remove as global listener MixedRealityToolkit.InputSystem.PopModalInputHandler(); }