Exemple #1
0
        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);
        }
Exemple #2
0
        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 ScaleMinMax()
        {
            float minScale = 0.5f;
            float maxScale = 2f;

            var bbox         = InstantiateSceneAndDefaultBbox();
            var scaleHandler = bbox.EnsureComponent <MinMaxScaleConstraint>();

            scaleHandler.ScaleMinimum = minScale;
            scaleHandler.ScaleMaximum = maxScale;
            yield return(null);

            Vector3 initialScale = bbox.transform.localScale;

            const int numHandSteps = 1;

            Vector3  initialHandPosition = new Vector3(0, 0, 0.5f);
            var      frontRightCornerPos = bbox.ScaleCorners[3].transform.position; // front right corner is corner 3
            TestHand hand = new TestHand(Handedness.Right);

            // Hands grab object at initial position
            yield return(hand.Show(initialHandPosition));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint));

            yield return(hand.MoveTo(frontRightCornerPos, numHandSteps));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // No change to scale yet
            Assert.AreEqual(initialScale, bbox.transform.localScale);

            // Move hands beyond max scale limit
            yield return(hand.MoveTo(new Vector3(scaleHandler.ScaleMaximum * 2, scaleHandler.ScaleMaximum * 2, 0) + frontRightCornerPos, numHandSteps));

            // Assert scale at max
            Assert.AreEqual(Vector3.one * scaleHandler.ScaleMaximum, bbox.transform.localScale);

            // Move hands beyond min scale limit
            yield return(hand.MoveTo(new Vector3(-scaleHandler.ScaleMinimum * 2, -scaleHandler.ScaleMinimum * 2, 0) + frontRightCornerPos, numHandSteps));

            // Assert scale at min
            Assert.AreEqual(Vector3.one * scaleHandler.ScaleMinimum, bbox.transform.localScale);

            GameObject.Destroy(bbox.gameObject);
            // Wait for a frame to give Unity a change to actually destroy the object
            yield return(null);
        }
Exemple #4
0
        public IEnumerator PrefabGGVZoom()
        {
            InstantiateFromPrefab();

            PlayModeTestUtilities.SetHandSimulationMode(HandSimulationMode.Gestures);

            TestHand handRight = new TestHand(Handedness.Right);

            yield return(handRight.Show(new Vector3(0.0f, 0.0f, 0.6f)));

            TestHand handLeft = new TestHand(Handedness.Left);

            yield return(handLeft.Show(new Vector3(-0.1f, 0.0f, 0.6f)));

            yield return(handRight.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(handLeft.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(handRight.Move(new Vector3(0.005f, 0.0f, 0.0f), 10));

            yield return(handLeft.Move(new Vector3(-0.005f, 0.0f, 0.0f), 10));

            Assert.AreEqual(0.5, panZoom.CurrentScale, 0.1, "slate did not zoom in using two ggv hands");

            yield return(handRight.Hide());

            yield return(handLeft.Hide());
        }
        /// <summary>
        /// Scroll a slate using GGV and ensure that the slate scrolls
        /// expected amount. Assumes panZoom has already been created.
        /// </summary>
        /// <param name="expectedScroll">The amount panZoom is expected to scroll</param>
        private IEnumerator RunGGVScrollTest(float expectedScroll)
        {
            var iss        = PlayModeTestUtilities.GetInputSimulationService();
            var oldSimMode = iss.ControllerSimulationMode;

            iss.ControllerSimulationMode = ControllerSimulationMode.HandGestures;

            Vector2 totalPanDelta = Vector2.zero;

            panZoom.PanUpdated.AddListener((hpd) => totalPanDelta += hpd.PanDelta);

            TestHand handRight = new TestHand(Handedness.Right);

            yield return(handRight.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(handRight.Show(Vector3.zero));

            // Move requires a slower action (i.e. higher numSteps) in order to
            // achieve a reliable pan.
            yield return(handRight.Move(new Vector3(0.0f, -0.1f, 0f), 30));

            Assert.AreEqual(expectedScroll, totalPanDelta.y, 0.1, "pan delta is not correct");

            yield return(handRight.Hide());

            iss.ControllerSimulationMode = oldSimMode;
            yield return(null);
        }
        public IEnumerator TestTouchEvents()
        {
            interactable.gameObject.AddComponent <NearInteractionTouchableVolume>();
            var  touchReceiver = interactable.AddReceiver <InteractableOnTouchReceiver>();
            bool didTouch      = false;
            bool didUntouch    = false;

            touchReceiver.OnTouchStart.AddListener(() => didTouch = true);
            touchReceiver.OnTouchEnd.AddListener(() => didUntouch = true);

            var testHand = new TestHand(Handedness.Right);

            yield return(testHand.Show(Vector3.forward));

            yield return(testHand.SetGesture(ArticulatedHandPose.GestureId.Open));

            yield return(testHand.MoveTo(interactable.transform.position));

            yield return(testHand.MoveTo(Vector3.forward));

            yield return(testHand.Hide());

            Assert.True(didTouch, "Did not receive touch event");
            Assert.True(didUntouch, "Did not receive touch end event");
        }
Exemple #7
0
        public IEnumerator TriggerButtonFarInteraction([ValueSource(nameof(PressableButtonsTestPrefabPaths))] string prefabFilename)
        {
            GameObject testButton = InstantiateDefaultPressableButton(prefabFilename);

            TestUtilities.PlayspaceToOriginLookingForward();

            Interactable interactableComponent = testButton.GetComponent <Interactable>();
            Button       buttonComponent       = testButton.GetComponent <Button>();

            Assert.IsTrue(interactableComponent != null || buttonComponent != null, "Depending on button type, there should be either an Interactable or a UnityUI Button on the control");

            var objectToMoveAndScale = testButton.transform;

            if (buttonComponent != null)
            {
                objectToMoveAndScale = testButton.transform.parent;
            }

            objectToMoveAndScale.position   += new Vector3(0f, 0.3f, 0.8f);
            objectToMoveAndScale.localScale *= 15f; // scale button up so it's easier to hit it with the far interaction pointer
            yield return(new WaitForFixedUpdate());

            yield return(null);

            bool buttonTriggered = false;

            var onClickEvent = (interactableComponent != null) ? interactableComponent.OnClick : buttonComponent.onClick;

            onClickEvent.AddListener(() =>
            {
                buttonTriggered = true;
            });

            TestHand hand = new TestHand(Handedness.Right);
            Vector3  initialHandPosition = new Vector3(0.05f, -0.05f, 0.3f); // orient hand so far interaction ray will hit button

            yield return(hand.Show(initialHandPosition));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Open));

            Assert.IsTrue(buttonTriggered, "Button did not get triggered with far interaction.");

            Object.Destroy(testButton);
            yield return(null);
        }
        public IEnumerator TestAssembleTouchNoSnapSlider()
        {
            GameObject  pinchSliderObject;
            PinchSlider slider;

            // This should not throw exception
            AssembleSlider(Vector3.forward, Vector3.zero, out pinchSliderObject, out slider);

            // Set the slider to use step divisions
            slider.UseSliderStepDivisions = true;
            slider.SliderStepDivisions    = 100;

            // Set slider to not snap with touch
            slider.SnapToPosition = false;
            slider.IsTouchable    = true;

            yield return(PlayModeTestUtilities.WaitForInputSystemUpdate());

            bool interactionStarted = false;

            slider.OnInteractionStarted.AddListener((x) => interactionStarted = true);
            bool interactionUpdated = false;

            slider.OnValueUpdated.AddListener((x) => interactionUpdated = true);

            var     rightHand  = new TestHand(Handedness.Right);
            Vector3 initialPos = new Vector3(0.0f, 0, 1.00f);

            yield return(rightHand.Show(initialPos));

            yield return(rightHand.Move(new Vector3(0.3f, 0, 0), 60));

            Assert.IsTrue(interactionStarted, "Slider did not raise interaction started.");
            Assert.IsTrue(interactionUpdated, "Slider did not raise SliderUpdated event.");

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open));

            yield return(rightHand.Move(new Vector3(-0.6f, 0, 0), 60));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open));

            yield return(rightHand.Hide());

            Assert.AreEqual(0.20f, slider.SliderValue, 0.005f);

            GameObject.Destroy(pinchSliderObject);
        }
        public IEnumerator SpherePointerPullbackDistance()
        {
            // Approximate distance covered by the pullback distance;
            Vector3 pullbackDelta = new Vector3(0, 0, 0.08f);

            var rightHand = new TestHand(Handedness.Right);

            // Show hand far enough from the test collider
            Vector3 idlePos = new Vector3(0.05f, 0, 1.0f);

            yield return(rightHand.Show(idlePos));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint));

            var pointer = rightHand.GetPointer <SpherePointer>();

            pointer.NearObjectSectorAngle = 360.0f;
            pointer.PullbackDistance      = 0.08f;
            pointer.TryGetNearGraspPoint(out Vector3 graspPoint);
            // Orient the right hand so the grab axis is approximately aligned with the z axis

            Vector3 margin           = new Vector3(0, 0, 0.001f);
            Vector3 nearObjectMargin = new Vector3(0, 0, 0.001f + (pointer.NearObjectSmoothingFactor + 1) * pointer.NearObjectRadius);

            Assert.IsNotNull(pointer, "Expected to find SpherePointer in the hand controller");
            Vector3 nearObjectPos         = new Vector3(0.05f, 0, colliderSurfaceZ - (pointer.NearObjectRadius));
            Vector3 interactionEnabledPos = new Vector3(0.05f, 0, colliderSurfaceZ - pointer.SphereCastRadius);

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Move hand closer to the collider to enable IsNearObject
            yield return(rightHand.MoveTo(nearObjectPos - margin, numFramesPerMove));

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            yield return(rightHand.MoveTo(nearObjectPos + margin, numFramesPerMove));

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            yield return(rightHand.MoveTo(nearObjectPos + margin + pullbackDelta, numFramesPerMove));

            Assert.True(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Move hand back out to disable IsNearObject
            yield return(rightHand.MoveTo(nearObjectPos - nearObjectMargin, numFramesPerMove));

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            yield return(rightHand.MoveTo(idlePos, numFramesPerMove));

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);
        }
        public IEnumerator DisableObject()
        {
            float minScale = 0.5f;
            float maxScale = 2f;

            var bbox         = InstantiateSceneAndDefaultBbox();
            var scaleHandler = bbox.EnsureComponent <MinMaxScaleConstraint>();

            scaleHandler.ScaleMinimum = minScale;
            scaleHandler.ScaleMaximum = maxScale;
            yield return(null);

            Vector3 initialScale = bbox.transform.localScale;

            const int numHandSteps = 1;

            Vector3  initialHandPosition = new Vector3(0, 0, 0.5f);
            var      frontRightCornerPos = bbox.ScaleCorners[3].transform.position; // front right corner is corner 3
            TestHand hand = new TestHand(Handedness.Right);

            // Hands grab object at initial position
            yield return(hand.Show(initialHandPosition));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint));

            yield return(hand.MoveTo(frontRightCornerPos, numHandSteps));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // Verify that scale works before deactivating
            yield return(hand.Move(Vector3.right * 0.2f, numHandSteps));

            Vector3 afterTransformScale = bbox.transform.localScale;

            Assert.AreNotEqual(initialScale, afterTransformScale);

            // Deactivate object and ensure that we don't scale
            bbox.gameObject.SetActive(false);
            yield return(null);

            bbox.gameObject.SetActive(true);
            yield return(hand.Move(Vector3.right * 0.2f, numHandSteps));

            Assert.AreEqual(afterTransformScale, bbox.transform.localScale);
        }
        public IEnumerator TestAssembleInteractableAndEventsRaised()
        {
            GameObject  pinchSliderObject;
            PinchSlider slider;

            // This should not throw exception
            AssembleSlider(Vector3.forward, Vector3.zero, out pinchSliderObject, out slider);

            var     rightHand  = new TestHand(Handedness.Right);
            Vector3 initialPos = new Vector3(0.05f, 0, 1.0f);

            yield return(PlayModeTestUtilities.WaitForInputSystemUpdate());

            bool interactionStarted = false;

            slider.OnInteractionStarted.AddListener((x) => interactionStarted = true);
            yield return(rightHand.Show(initialPos));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            Assert.IsTrue(interactionStarted, "Slider did not raise interaction started.");

            bool interactionUpdated = false;

            slider.OnValueUpdated.AddListener((x) => interactionUpdated = true);

            yield return(rightHand.Move(new Vector3(0.1f, 0, 0)));

            Assert.IsTrue(interactionUpdated, "Slider did not raise SliderUpdated event.");

            bool interactionEnded = false;

            slider.OnInteractionEnded.AddListener((x) => interactionEnded = true);

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open));

            yield return(rightHand.Hide());

            Assert.IsTrue(interactionEnded, "Slider did not raise interaction ended.");

            Assert.That(slider.SliderValue, Is.GreaterThan(0.5));

            GameObject.Destroy(pinchSliderObject);
        }
Exemple #12
0
        public IEnumerator ManipulationHandlerRotateHeadGGV()
        {
            TestUtilities.PlayspaceToOriginLookingForward();

            // Switch to Gestures
            var iss    = PlayModeTestUtilities.GetInputSimulationService();
            var oldIsp = iss.InputSimulationProfile;
            var isp    = ScriptableObject.CreateInstance <MixedRealityInputSimulationProfile>();

            isp.HandSimulationMode     = HandSimulationMode.Gestures;
            iss.InputSimulationProfile = isp;

            // 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);
            Quaternion initialObjectRotation = testObject.transform.rotation;

            testObject.transform.position = initialObjectPosition;

            var manipHandler = testObject.AddComponent <ManipulationHandler>();

            manipHandler.HostTransform   = testObject.transform;
            manipHandler.SmoothingActive = false;

            Vector3   originalHandPosition = new Vector3(0, 0, 0.5f);
            TestHand  hand         = new TestHand(Handedness.Right);
            const int numHandSteps = 1;

            // Grab cube
            yield return(hand.Show(originalHandPosition));

            yield return(null);

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // Rotate Head and readjust hand
            int numRotations = 10;

            for (int i = 0; i < numRotations; i++)
            {
                MixedRealityPlayspace.Transform.Rotate(Vector3.up, 180 / numRotations);
                yield return(hand.MoveTo(originalHandPosition, numHandSteps));

                yield return(null);

                // Test Object hasn't moved
                TestUtilities.AssertAboutEqual(initialObjectPosition, testObject.transform.position, "Object moved while rotating head");
                TestUtilities.AssertAboutEqual(initialObjectRotation, testObject.transform.rotation, "Object rotated while rotating head", 0.25f);
            }

            // Restore the input simulation profile
            iss.InputSimulationProfile = oldIsp;
            yield return(null);
        }
Exemple #13
0
        public IEnumerator ScaleViaHoloLens1Interaction()
        {
            var bbox = InstantiateSceneAndDefaultBbox();

            yield return(null);

            yield return(null);

            var bounds      = bbox.GetComponent <BoxCollider>().bounds;
            var startCenter = new Vector3(0, 0, 1.5f);
            var startSize   = new Vector3(.5f, .5f, .5f);

            TestUtilities.AssertAboutEqual(bounds.center, startCenter, "bbox incorrect center at start");
            TestUtilities.AssertAboutEqual(bounds.size, startSize, "bbox incorrect size at start");

            // Switch to hand gestures
            var iss        = PlayModeTestUtilities.GetInputSimulationService();
            var oldSimMode = iss.ControllerSimulationMode;

            iss.ControllerSimulationMode = ControllerSimulationMode.HandGestures;

            CameraCache.Main.transform.LookAt(bbox.ScaleCorners[3].transform);

            var      startHandPos = CameraCache.Main.transform.TransformPoint(new Vector3(0.1f, 0f, 1.5f));
            TestHand rightHand    = new TestHand(Handedness.Right);

            yield return(rightHand.Show(startHandPos));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // After pinching, center should remain the same
            var afterPinchbounds = bbox.GetComponent <BoxCollider>().bounds;

            TestUtilities.AssertAboutEqual(afterPinchbounds.center, startCenter, "bbox incorrect center after pinch");
            TestUtilities.AssertAboutEqual(afterPinchbounds.size, startSize, "bbox incorrect size after pinch");

            var delta = new Vector3(0.1f, 0.1f, 0f);

            yield return(rightHand.Move(delta));

            var endBounds = bbox.GetComponent <BoxCollider>().bounds;

            TestUtilities.AssertAboutEqual(endBounds.center, new Vector3(0.033f, 0.033f, 1.467f), "endBounds incorrect center", 0.02f);
            TestUtilities.AssertAboutEqual(endBounds.size, Vector3.one * .561f, "endBounds incorrect size", 0.02f);

            Object.Destroy(bbox.gameObject);
            // Wait for a frame to give Unity a change to actually destroy the object
            yield return(null);

            // Restore the input simulation profile
            iss.ControllerSimulationMode = oldSimMode;

            yield return(null);
        }
        public IEnumerator ArticulatedCursorState()
        {
            var inputSystem = PlayModeTestUtilities.GetInputSystem();

            // Show hand far enough from the test collider so the cursor is not on it
            var     hand         = new TestHand(Handedness.Right);
            Vector3 offObjectPos = TestUtilities.PositionRelativeToPlayspace(new Vector3(0.05f, 0, 1.0f));

            yield return(hand.Show(offObjectPos));

            VerifyCursorStateFromPointers(inputSystem.FocusProvider.GetPointers <ShellHandRayPointer>(), CursorStateEnum.Interact);

            // Move hand closer to the collider so the cursor is on it
            Vector3 onObjectPos = TestUtilities.PositionRelativeToPlayspace(new Vector3(0.05f, 0, 1.5f));

            yield return(hand.MoveTo(onObjectPos, numFramesPerMove));

            VerifyCursorStateFromPointers(inputSystem.FocusProvider.GetPointers <ShellHandRayPointer>(), CursorStateEnum.InteractHover);

            // Trigger pinch
            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            VerifyCursorStateFromPointers(inputSystem.FocusProvider.GetPointers <ShellHandRayPointer>(), CursorStateEnum.Select);

            // Release pinch
            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Open, false));

            VerifyCursorStateFromPointers(inputSystem.FocusProvider.GetPointers <ShellHandRayPointer>(), CursorStateEnum.Release);

            // Wait to transition back to InteractHover
            yield return(null);

            VerifyCursorStateFromPointers(inputSystem.FocusProvider.GetPointers <ShellHandRayPointer>(), CursorStateEnum.InteractHover);

            // Move back so the cursor is no longer on the object
            yield return(hand.MoveTo(offObjectPos, numFramesPerMove));

            VerifyCursorStateFromPointers(inputSystem.FocusProvider.GetPointers <ShellHandRayPointer>(), CursorStateEnum.Interact);

            yield return(null);
        }
        public IEnumerator ConstrainScaleApparentSize()
        {
            TestUtilities.PlayspaceToArbitraryPose();

            // set up cube with manipulation handler
            var testObject = GameObject.CreatePrimitive(PrimitiveType.Cube);

            testObject.transform.localScale = Vector3.one * 0.2f;
            testObject.transform.position   = TestUtilities.PositionRelativeToPlayspace(new Vector3(0f, 0f, 1f));
            testObject.transform.rotation   = CameraCache.Main.transform.rotation;
            var manipHandler = testObject.AddComponent <ObjectManipulator>();

            manipHandler.HostTransform          = testObject.transform;
            manipHandler.SmoothingFar           = false;
            manipHandler.ManipulationType       = ManipulationHandFlags.OneHanded;
            manipHandler.OneHandRotationModeFar = ObjectManipulator.RotateInOneHandType.RotateAboutObjectCenter;

            // add an xy move constraint so that the object's position does not change on screen
            var moveConstraint = manipHandler.EnsureComponent <MoveAxisConstraint>();

            moveConstraint.UseLocalSpaceForConstraint = true;
            moveConstraint.ConstraintOnMovement       = AxisFlags.XAxis | AxisFlags.YAxis;

            var constraint = manipHandler.EnsureComponent <MaintainApparentSizeConstraint>();

            Vector3 topLeft       = testObject.transform.TransformPoint(new Vector3(-0.5f, 0.5f, -0.5f));
            Vector3 bottomRight   = testObject.transform.TransformPoint(new Vector3(0.5f, -0.5f, -0.5f));
            float   originalAngle = Vector3.Angle(topLeft - CameraCache.Main.transform.position, bottomRight - CameraCache.Main.transform.position);

            yield return(new WaitForFixedUpdate());

            yield return(null);

            const int numHandSteps = 1;

            // Hand pointing at middle of cube
            TestHand hand = new TestHand(Handedness.Right);

            yield return(hand.Show(TestUtilities.PositionRelativeToPlayspace(new Vector3(0.044f, -0.1f, 0.45f))));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // Move and test that still same distance from head
            yield return(hand.Move(TestUtilities.DirectionRelativeToPlayspace(Vector3.forward * 0.5f), numHandSteps));

            yield return(null);

            Vector3 newtopLeft     = testObject.transform.TransformPoint(new Vector3(-0.5f, 0.5f, -0.5f));
            Vector3 newBottomRight = testObject.transform.TransformPoint(new Vector3(0.5f, -0.5f, -0.5f));
            float   newAngle       = Vector3.Angle(newtopLeft - CameraCache.Main.transform.position, newBottomRight - CameraCache.Main.transform.position);

            Assert.AreEqual(originalAngle, newAngle, 0.05f);
        }
        public IEnumerator TestRiggedHand()
        {
            // Initialize hand
            var rightHand = new TestHand(Handedness.Right);

            yield return(rightHand.Show(Vector3.zero));

            RiggedHandVisualizer handVisualizer = GameObject.FindObjectOfType <RiggedHandVisualizer>().GetComponent <RiggedHandVisualizer>();

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open));

            Assert.IsTrue(handVisualizer.HandRenderer.sharedMaterial.GetFloat(handVisualizer.PinchStrengthMaterialProperty) < 0.5f);

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Grab));

            Assert.IsTrue(handVisualizer.HandRenderer.sharedMaterial.GetFloat(handVisualizer.PinchStrengthMaterialProperty) > 0.5f);

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint));

            Assert.IsTrue(handVisualizer.HandRenderer.sharedMaterial.GetFloat(handVisualizer.PinchStrengthMaterialProperty) < 0.5f);
        }
        public IEnumerator GrabLayerMasks()
        {
            // Initialize hand
            var rightHand = new TestHand(Handedness.Right);

            yield return(rightHand.Show(Vector3.zero));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint));

            var pointer = rightHand.GetPointer <SpherePointer>();

            Assert.IsNotNull(pointer, "Expected to find SpherePointer in the hand controller");
            Vector3 interactionEnabledPos = new Vector3(0.05f, 0, colliderSurfaceZ - pointer.SphereCastRadius);

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Move hand to object, IsNearObject, IsInteractionEnabled should be true
            yield return(rightHand.MoveTo(interactionEnabledPos));

            Assert.True(pointer.IsNearObject);
            Assert.True(pointer.IsInteractionEnabled);

            // Set layer to spatial mesh, which sphere pointer should be ignoring
            // assumption: layer 31 is the spatial mesh layer
            cube.SetLayerRecursively(31);
            yield return(null);

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Set layer back to default
            cube.SetLayerRecursively(0);
            yield return(null);

            Assert.True(pointer.IsNearObject);
            Assert.True(pointer.IsInteractionEnabled);

            // Remove the grabbable component, ray should turn on
            GameObject.Destroy(cube.GetComponent <NearInteractionGrabbable>());
            yield return(null);

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Add back the grabbable, ray should turn off
            cube.AddComponent <NearInteractionGrabbable>();
            yield return(null);

            Assert.True(pointer.IsNearObject);
            Assert.True(pointer.IsInteractionEnabled);
        }
        public IEnumerator Prefab_RayScroll()
        {
            InstantiateFromPrefab(Vector3.forward);
            Vector2 totalPanDelta = Vector2.zero;

            panZoom.PanUpdated.AddListener((hpd) => totalPanDelta += hpd.PanDelta);

            TestHand h           = new TestHand(Handedness.Right);
            Vector3  screenPoint = CameraCache.Main.ViewportToScreenPoint(new Vector3(0.5f, 0.25f, 0.5f));

            yield return(h.Show(CameraCache.Main.ScreenToWorldPoint(screenPoint)));

            yield return(h.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(h.Move(new Vector3(0, -0.05f, 0), 10));

            yield return(h.SetGesture(ArticulatedHandPose.GestureId.Open));

            Assert.AreEqual(0.1, totalPanDelta.y, 0.05, "pan delta is not correct");

            yield return(h.Hide());
        }
        /// <summary>
        /// Scroll contents to the limit in the specified direction
        /// </summary>
        /// <param name="direction">Scroll direction</param>
        private IEnumerator ScrollToLimit(Vector3 direction)
        {
            TestHand handRight = new TestHand(Handedness.Right);

            yield return(handRight.Show(Vector3.zero));

            Vector3 screenPoint = CameraCache.Main.ViewportToScreenPoint(new Vector3(0.5f, 0.0f, 0.5f));
            var     moveDelta   = -0.5f * direction;

            for (var i = 0; i < 3; i++)
            {
                yield return(handRight.MoveTo(CameraCache.Main.ScreenToWorldPoint(screenPoint)));

                yield return(handRight.SetGesture(ArticulatedHandPose.GestureId.Pinch));

                yield return(handRight.Move(moveDelta, 10));

                yield return(handRight.SetGesture(ArticulatedHandPose.GestureId.Open));
            }

            yield return(handRight.Hide());
        }
        public IEnumerator TestSelectFarEventConfiguration()
        {
            // Create an interactive cube
            InteractiveElement interactiveElement = CreateInteractiveCube();

            yield return(null);

            // Add the selectFar state
            InteractionState selectFar = interactiveElement.AddNewState(selectFarStateName);

            yield return(null);

            // Get the event configuration for the SelectFar state
            var eventConfiguration = interactiveElement.GetStateEvents <SelectFarEvents>(selectFarStateName);

            // Set global to true, this registers the IMixedRealityPointerHandler
            eventConfiguration.Global = true;

            bool onSelectDown    = false;
            bool onSelectHold    = false;
            bool onSelectClicked = false;
            bool onSelectUp      = false;

            eventConfiguration.OnSelectDown.AddListener((eventData) => { onSelectDown = true; });
            eventConfiguration.OnSelectHold.AddListener((eventData) => { onSelectHold = true; });
            eventConfiguration.OnSelectClicked.AddListener((eventData) => { onSelectClicked = true; });
            eventConfiguration.OnSelectUp.AddListener((eventData) => { onSelectUp = true; });

            // Create a new hand and initialize it with an object in focus
            var leftHand = new TestHand(Handedness.Left);

            // Show hand at starting position
            yield return(ShowHandWithObjectInFocus(leftHand));

            yield return(MoveHandOutOfFocus(leftHand));

            // Click the hand to trigger far select events
            yield return(leftHand.Click());

            Assert.True(onSelectDown);
            Assert.True(onSelectHold);
            Assert.True(onSelectClicked);
            Assert.True(onSelectUp);

            eventConfiguration.Global = false;

            yield return(leftHand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // Make sure the SelectFar state is not active after setting global to false without an object in focus
            Assert.AreEqual(0, selectFar.Value);
        }
Exemple #21
0
        public IEnumerator ConstrainRotationFixToWorld()
        {
            TestUtilities.PlayspaceToOriginLookingForward();

            var testObject = GameObject.CreatePrimitive(PrimitiveType.Cube);

            testObject.transform.localScale = Vector3.one * 0.2f;
            testObject.transform.position   = Vector3.forward;
            var manipHandler = testObject.AddComponent <ObjectManipulator>();

            manipHandler.HostTransform           = testObject.transform;
            manipHandler.SmoothingActive         = false;
            manipHandler.ManipulationType        = ManipulationHandFlags.OneHanded;
            manipHandler.OneHandRotationModeNear = ObjectManipulator.RotateInOneHandType.RotateAboutGrabPoint;

            var rotConstraint = manipHandler.EnsureComponent <FixedRotationToWorldConstraint>();

            rotConstraint.TargetTransform = manipHandler.HostTransform;

            // Face user first
            const int numHandSteps = 1;
            TestHand  hand         = new TestHand(Handedness.Right);

            yield return(hand.Show(new Vector3(0.05f, -0.1f, 0.45f)));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(null);

            // rotate the hand
            yield return(hand.SetRotation(Quaternion.Euler(45, 45, 45), numHandSteps));

            yield return(null);

            Assert.AreEqual(Quaternion.identity, testObject.transform.rotation);

            // move the hand
            yield return(hand.Move(Vector3.right * 0.2f, numHandSteps));

            yield return(null);

            Assert.AreEqual(Quaternion.identity, testObject.transform.rotation);

            // rotate the head
            CameraCache.Main.transform.Rotate(new Vector3(0, 10, 0));
            yield return(new WaitForFixedUpdate());

            yield return(null);

            Assert.AreEqual(Quaternion.identity, testObject.transform.rotation);
        }
Exemple #22
0
        private IEnumerator DirectPinchAndMoveSlider(PinchSlider slider, float toSliderValue)
        {
            Debug.Log($"moving hand to value {toSliderValue}");
            var     rightHand  = new TestHand(Handedness.Right);
            Vector3 initialPos = new Vector3(0.05f, 0, 1.0f);

            yield return(rightHand.Show(initialPos));

            yield return(rightHand.MoveTo(slider.ThumbRoot.transform.position));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            if (!(toSliderValue >= 0 && toSliderValue <= 1))
            {
                throw new System.ArgumentException("toSliderValue must be between 0 and 1");
            }

            yield return(rightHand.MoveTo(Vector3.Lerp(slider.SliderStartPosition, slider.SliderEndPosition, toSliderValue)));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.Open));

            yield return(rightHand.Hide());
        }
        public IEnumerator ConstrainMovementFixedDistance()
        {
            TestUtilities.PlayspaceToArbitraryPose();

            // set up cube with manipulation handler
            var testObject = GameObject.CreatePrimitive(PrimitiveType.Cube);

            testObject.transform.localScale = Vector3.one * 0.2f;
            testObject.transform.position   = TestUtilities.PositionRelativeToPlayspace(new Vector3(0f, 0f, 1f));
            var manipHandler = testObject.AddComponent <ObjectManipulator>();

            manipHandler.HostTransform          = testObject.transform;
            manipHandler.SmoothingFar           = false;
            manipHandler.ManipulationType       = ManipulationHandFlags.OneHanded;
            manipHandler.OneHandRotationModeFar = ObjectManipulator.RotateInOneHandType.RotateAboutObjectCenter;

            var constraint = manipHandler.EnsureComponent <FixedDistanceConstraint>();

            constraint.ConstraintTransform = CameraCache.Main.transform;

            float originalDist = (CameraCache.Main.transform.position - testObject.transform.position).magnitude;

            yield return(new WaitForFixedUpdate());

            yield return(null);

            const int numHandSteps = 1;

            // Hand pointing at middle of cube
            TestHand hand = new TestHand(Handedness.Right);

            yield return(hand.Show(TestUtilities.PositionRelativeToPlayspace(new Vector3(0.044f, -0.1f, 0.45f))));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            Vector3 worldDelta = TestUtilities.DirectionRelativeToPlayspace(Vector3.one);

            // Move and test that still same distance from head
            yield return(hand.Move(worldDelta * 0.5f, numHandSteps));

            yield return(null);

            Assert.AreEqual(originalDist, (CameraCache.Main.transform.position - testObject.transform.position).magnitude, 0.001f);

            yield return(hand.Move(worldDelta * -1f, numHandSteps));

            yield return(null);

            Assert.AreEqual(originalDist, (CameraCache.Main.transform.position - testObject.transform.position).magnitude, 0.001f);
        }
Exemple #24
0
        public IEnumerator TestTeleportLayers()
        {
            var iss = PlayModeTestUtilities.GetInputSimulationService();

            // Create a floor and make sure it's below the camera
            var floor = GameObject.CreatePrimitive(PrimitiveType.Plane);

            floor.transform.position = -0.5f * Vector3.up;

            // Bring out the right hand and set it to the teleport gesture
            TestUtilities.PlayspaceToOriginLookingForward();
            float initialForwardPosition = MixedRealityPlayspace.Position.z;

            TestHand rightHand = new TestHand(Handedness.Right);

            // Make sure the hand is in front of the camera
            yield return(rightHand.Show(Vector3.forward * 0.6f));

            rightHand.SetRotation(Quaternion.identity);

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.TeleportStart));

            // Wait for the hand to animate
            yield return(PlayModeTestUtilities.WaitForInputSystemUpdate());

            yield return(new WaitForSeconds(1.0f / iss.InputSimulationProfile.HandGestureAnimationSpeed + 0.1f));

            TeleportPointer teleportPointer = rightHand.GetPointer <TeleportPointer>();

            teleportPointer.PrioritizedLayerMasksOverride = new LayerMask[1] {
                UnityEngine.Physics.AllLayers ^ (1 << LayerMask.NameToLayer("UI"))
            };

            floor.layer = LayerMask.NameToLayer("Default");
            yield return(PlayModeTestUtilities.WaitForInputSystemUpdate());

            Assert.AreEqual(Physics.TeleportSurfaceResult.Valid, teleportPointer.TeleportSurfaceResult);

            floor.layer = LayerMask.NameToLayer("Ignore Raycast");
            yield return(PlayModeTestUtilities.WaitForInputSystemUpdate());

            Assert.AreEqual(Physics.TeleportSurfaceResult.Invalid, teleportPointer.TeleportSurfaceResult);

            floor.layer = LayerMask.NameToLayer("UI");
            yield return(PlayModeTestUtilities.WaitForInputSystemUpdate());

            Assert.AreEqual(Physics.TeleportSurfaceResult.None, teleportPointer.TeleportSurfaceResult);
        }
        public IEnumerator GrabLayerMasksWithOverlap()
        {
            // Initialize hand
            var rightHand = new TestHand(Handedness.Right);

            yield return(rightHand.Show(Vector3.zero));

            yield return(rightHand.SetGesture(ArticulatedHandPose.GestureId.OpenSteadyGrabPoint));

            var pointer = rightHand.GetPointer <SpherePointer>();

            Assert.IsNotNull(pointer, "Expected to find SpherePointer in the hand controller");
            Vector3 interactionEnabledPos = new Vector3(0.05f, 0, colliderSurfaceZ - pointer.SphereCastRadius);

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Initialize overlapRect
            overlapRect.SetActive(true);

            // Set the cube's layer to spatial mesh, which sphere pointer should be ignoring
            // assumption: layer 31 is the spatial mesh layer
            cube.SetLayerRecursively(31);
            yield return(null);

            Assert.False(pointer.IsNearObject);
            Assert.False(pointer.IsInteractionEnabled);

            // Move hand to object, IsNearObject, IsInteractionEnabled should be true
            yield return(rightHand.MoveTo(interactionEnabledPos));

            Assert.True(CoreServices.InputSystem.FocusProvider.GetFocusedObject(pointer) == overlapRect, " the overlapping rectangle was not in focus");
            Assert.True(pointer.IsNearObject);
            Assert.True(pointer.IsInteractionEnabled);

            // Set cube's layer back to default
            // Set overlapRect's layer to spatial mesh, which sphere pointer should be ignoring
            // assumption: layer 31 is the spatial mesh layer
            overlapRect.SetLayerRecursively(31);
            cube.SetLayerRecursively(0);
            yield return(null);

            Assert.True(CoreServices.InputSystem.FocusProvider.GetFocusedObject(pointer) == cube, " the inner cube was not in focus");

            // Reinitialize the overlapRect
            overlapRect.SetLayerRecursively(0);
            overlapRect.SetActive(false);
        }
        public IEnumerator TestDisableFocusLockedObject()
        {
            TestUtilities.PlayspaceToOriginLookingForward();
            var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);

            cube.AddComponent <ManipulationHandler>(); // Add focus handler so focus can lock
            cube.transform.position   = Vector3.right;
            cube.transform.localScale = Vector3.one * 0.2f;

            const int numHandSteps = 1;

            // No initial focus
            cube.transform.position = Vector3.forward;
            TestHand hand = new TestHand(Handedness.Right);

            yield return(hand.Show(Vector3.forward * 0.5f));

            yield return(null);

            var handRayPointer = hand.GetPointer <ShellHandRayPointer>();

            Assert.IsNull(handRayPointer.Result.CurrentPointerTarget);

            // Focus lock cube
            yield return(hand.MoveTo(new Vector3(0.06f, -0.1f, 0.5f), numHandSteps));

            yield return(hand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(null);

            Assert.IsTrue(handRayPointer.IsFocusLocked);
            Assert.AreEqual(cube, handRayPointer.Result.CurrentPointerTarget);

            // Deactivate the cube
            cube.SetActive(false);
            yield return(null);

            Assert.IsFalse(handRayPointer.IsFocusLocked);
            Assert.IsNull(handRayPointer.Result.CurrentPointerTarget);

            // Reactivate the cube
            cube.SetActive(true);
            yield return(null);

            Assert.IsFalse(handRayPointer.IsFocusLocked);
            Assert.AreEqual(cube, handRayPointer.Result.CurrentPointerTarget);
        }
        public IEnumerator TestPressEvents()
        {
            var testHand = new TestHand(Handedness.Right);

            interactable.gameObject.AddComponent <NearInteractionGrabbable>();
            var  pressReceiver = interactable.AddReceiver <InteractableOnPressReceiver>();
            bool didPress      = false;

            pressReceiver.OnPress.AddListener(() => didPress = true);
            yield return(testHand.Show(interactable.transform.position));

            yield return(testHand.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(testHand.Hide());

            Assert.True(didPress, "did not receive press event");
        }
        public IEnumerator PrefabScrollUpZoomOutLimited()
        {
            var maxPanHorizontal = 4f;
            var maxPanVertical   = 4f;

            InstantiatePanLimitedSlateFromPrefab(maxPanHorizontal: maxPanHorizontal, maxPanVertical: maxPanVertical);

            yield return(ScrollToLimit(new Vector3(1, 1, 0)));

            // Right hand pinches slate
            TestHand handRight = new TestHand(Handedness.Right);

            yield return(handRight.MoveTo(panZoom.transform.position + Vector3.forward * -0.5f + Vector3.right * 0.2f));

            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.5f + Vector3.right * -0.2f));

            yield return(handLeft.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // Use both hands to zoom out
            yield return(handLeft.Move(new Vector3(0.1f, 0.1f, 0f), 10));

            var uvs = new List <Vector2>();

            meshFilter.mesh.GetUVs(0, uvs);

#if UNITY_2019_1_OR_NEWER
            Assert.AreEqual(maxPanHorizontal * material.mainTextureScale.x, uvs[3].x, 0.05, "mesh uv is not correct");
            Assert.AreEqual(maxPanVertical * material.mainTextureScale.y, uvs[3].y, 0.05, "mesh uv is not correct");
#else
            Assert.AreEqual(maxPanHorizontal * material.mainTextureScale.x, uvs[1].x, 0.05, "mesh uv is not correct");
            Assert.AreEqual(maxPanVertical * material.mainTextureScale.y, uvs[1].y, 0.05, "mesh uv is not correct");
#endif

            yield return(handRight.Hide());

            yield return(handLeft.Hide());
        }
Exemple #29
0
        /// <summary>
        /// Scroll a slate using GGV and ensure that the slate scrolls
        /// expected amount. Assumes panZoom has already been created.
        /// </summary>
        /// <param name="expectedScroll">The amount panZoom is expected to scroll</param>
        private IEnumerator RunGGVScrollTest(float expectedScroll)
        {
            PlayModeTestUtilities.SetHandSimulationMode(HandSimulationMode.Gestures);

            Vector2 totalPanDelta = Vector2.zero;

            panZoom.PanUpdated.AddListener((hpd) => totalPanDelta += hpd.PanDelta);

            TestHand h = new TestHand(Handedness.Right);

            yield return(h.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            yield return(h.Show(Vector3.zero));

            yield return(h.Move(new Vector3(0.0f, -0.1f, 0f)));

            Assert.AreEqual(expectedScroll, totalPanDelta.y, 0.05, "pan delta is not correct");

            yield return(h.Hide());
        }
        public IEnumerator PrefabZoomOutWithoutJitter()
        {
            var maxPan = 1.2f;

            InstantiatePanLimitedSlateFromPrefab(maxPanHorizontal: maxPan, maxPanVertical: maxPan);

            yield return(ScrollToLimit(new Vector3(-1, -1, 0)));

            // Right hand pinches slate
            TestHand handRight = new TestHand(Handedness.Right);

            yield return(handRight.MoveTo(panZoom.transform.position + Vector3.forward * -0.5f + Vector3.right * 0.2f));

            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.5f + Vector3.right * -0.2f));

            yield return(handLeft.SetGesture(ArticulatedHandPose.GestureId.Pinch));

            // Use both hands to zoom out
            var previousUvs = new List <Vector2>();

            meshFilter.mesh.GetUVs(0, previousUvs);
            for (var i = 0; i < 10; i++)
            {
                yield return(handLeft.Move(new Vector3(0f, 0.02f, 0f), 1));

                var uvs = new List <Vector2>();
                meshFilter.mesh.GetUVs(0, uvs);
                for (int j = 0; j < uvs.Count; j++)
                {
                    Assert.AreEqual(previousUvs[j].x, uvs[j].x, 0.05, "mesh is jittering");
                    Assert.AreEqual(previousUvs[j].y, uvs[j].y, 0.05, "mesh is jittering");
                }
                previousUvs = uvs;
            }
        }