private bool UpdateHandDataFromState(SimulatedHandData frame, SimulatedHandState state)
        {
            bool handDataChanged = false;
            bool wasTracked      = frame.IsTracked;
            bool wasPinching     = frame.IsPinching;

            frame.IsTracked  = state.IsVisible;
            frame.IsPinching = state.IsPinching;
            if (wasTracked != frame.IsTracked || wasPinching != frame.IsPinching)
            {
                handDataChanged = true;
            }

            if (frame.IsTracked)
            {
                var prevTime = frame.Timestamp;
                frame.Timestamp = DateTime.UtcNow.Ticks;
                if (frame.Timestamp != prevTime)
                {
                    state.FillCurrentFrame(frame.Joints);
                    handDataChanged = true;
                }
            }
            else
            {
                // If frame is not tracked, set timestamp to zero
                frame.Timestamp = 0;
            }

            return(handDataChanged);
        }
        public CustomSimulatedHandDataProvider(MixedRealityInputSimulationProfile _profile)
        {
            profile = _profile;

            stateLeft  = new SimulatedHandState(Handedness.Left);
            stateRight = new SimulatedHandState(Handedness.Right);
        }
        public SimulatedHandDataProvider(MixedRealityInputSimulationProfile _profile)
        {
            profile = _profile;

            HandStateLeft  = new SimulatedHandState(Handedness.Left);
            HandStateRight = new SimulatedHandState(Handedness.Right);

            HandStateLeft.Gesture  = profile.DefaultHandGesture;
            HandStateRight.Gesture = profile.DefaultHandGesture;
        }
        /// Apply changes to one hand and update tracking
        private void SimulateHandInput(
            ref long lastSimulatedTimestamp,
            SimulatedHandState state,
            bool isSimulating,
            bool isAlwaysVisible,
            Vector3 mouseDelta,
            Vector3 rotationDeltaEulerAngles)
        {
            bool enableTracking = isAlwaysVisible || isSimulating;

            if (!state.IsTracked && enableTracking)
            {
                // Start at current mouse position
                Vector3 mousePos = UnityEngine.Input.mousePosition;
                state.ScreenPosition = new Vector3(mousePos.x, mousePos.y, profile.DefaultHandDistance);
            }

            if (isSimulating)
            {
                state.SimulateInput(mouseDelta, profile.HandJitterAmount, rotationDeltaEulerAngles);

                if (isAlwaysVisible)
                {
                    // Toggle gestures on/off
                    state.Gesture = ToggleGesture(state.Gesture);
                }
                else
                {
                    // Enable gesture while mouse button is pressed
                    state.Gesture = SelectGesture();
                }
            }

            // Update tracked state of a hand.
            // If hideTimeout value is null, hands will stay visible after tracking stops.
            // TODO: DateTime.UtcNow can be quite imprecise, better use Stopwatch.GetTimestamp
            // https://stackoverflow.com/questions/2143140/c-sharp-datetime-now-precision
            DateTime currentTime = DateTime.UtcNow;

            if (enableTracking)
            {
                state.IsTracked        = true;
                lastSimulatedTimestamp = currentTime.Ticks;
            }
            else
            {
                float timeSinceTracking = (float)currentTime.Subtract(new DateTime(lastSimulatedTimestamp)).TotalSeconds;
                if (timeSinceTracking > profile.HandHideTimeout)
                {
                    state.IsTracked = false;
                }
            }
        }
 private void AnimateGesture(SimulatedHandState state, float gestureAnimDelta)
 {
     if (state.IsAlwaysTracked)
     {
         // Toggle gestures on/off
         state.AnimateGesture(ToggleGesture(state.Gesture), gestureAnimDelta);
     }
     else
     {
         // Enable gesture while mouse button is pressed
         state.AnimateGesture(SelectGesture(), gestureAnimDelta);
     }
 }
        /// Apply changes to one hand and update tracking
        private void SimulateHandInput(
            ref long lastHandTrackedTimestamp,
            SimulatedHandState state,
            bool isSimulating,
            bool isAlwaysVisible,
            MouseDelta mouseDelta,
            bool useMouseRotation)
        {
            bool enableTracking = isAlwaysVisible || isSimulating;

            if (!state.IsTracked && enableTracking)
            {
                ResetHand(state, isSimulating);
            }

            if (isSimulating)
            {
                state.SimulateInput(mouseDelta, useMouseRotation, profile.MouseRotationSensitivity, profile.MouseHandRotationSpeed, profile.HandJitterAmount);

                if (isAlwaysVisible)
                {
                    // Toggle gestures on/off
                    state.Gesture = ToggleGesture(state.Gesture);
                }
                else
                {
                    // Enable gesture while mouse button is pressed
                    state.Gesture = SelectGesture();
                }
            }

            // Update tracked state of a hand.
            // If hideTimeout value is null, hands will stay visible after tracking stops.
            // TODO: DateTime.UtcNow can be quite imprecise, better use Stopwatch.GetTimestamp
            // https://stackoverflow.com/questions/2143140/c-sharp-datetime-now-precision
            DateTime currentTime = DateTime.UtcNow;

            if (enableTracking)
            {
                state.IsTracked          = true;
                lastHandTrackedTimestamp = currentTime.Ticks;
            }
            else
            {
                float timeSinceTracking = (float)currentTime.Subtract(new DateTime(lastHandTrackedTimestamp)).TotalSeconds;
                if (timeSinceTracking > profile.HandHideTimeout)
                {
                    state.IsTracked = false;
                }
            }
        }
        public SimulatedHandDataProvider(MixedRealityInputSimulationProfile _profile) : base(_profile)
        {
            InputStateLeft  = new SimulatedHandState(Handedness.Left);
            InputStateRight = new SimulatedHandState(Handedness.Right);
            InputStateGaze  = new SimulatedHandState(Handedness.None);

            SimulatedHandState handStateLeft  = InputStateLeft as SimulatedHandState;
            SimulatedHandState handStateRight = InputStateRight as SimulatedHandState;
            SimulatedHandState handStateGaze  = InputStateGaze as SimulatedHandState;

            handStateLeft.Gesture  = profile.DefaultHandGesture;
            handStateRight.Gesture = profile.DefaultHandGesture;
            handStateGaze.Gesture  = profile.DefaultHandGesture;
        }
        /// <summary>
        /// Update hand state based on keyboard and mouse input
        /// </summary>
        protected override void SimulateUserInput(MouseDelta mouseDelta)
        {
            base.SimulateUserInput(mouseDelta);

            SimulatedHandState handStateLeft  = InputStateLeft as SimulatedHandState;
            SimulatedHandState handStateRight = InputStateRight as SimulatedHandState;
            SimulatedHandState handStateGaze  = InputStateGaze as SimulatedHandState;

            // This line explicitly uses unscaledDeltaTime because we don't want input simulation
            // to lag when the time scale is set to a value other than 1. Input should still continue
            // to move freely.
            float gestureAnimDelta = profile.HandGestureAnimationSpeed * Time.unscaledDeltaTime;

            handStateLeft.GestureBlending  += gestureAnimDelta;
            handStateRight.GestureBlending += gestureAnimDelta;
            handStateGaze.GestureBlending   = 1.0f;
        }
        private void ResetHand(SimulatedHandState state, bool isSimulating)
        {
            if (isSimulating)
            {
                // Start at current mouse position
                Vector3 mousePos = UnityEngine.Input.mousePosition;
                state.ViewportPosition = CameraCache.Main.ScreenToViewportPoint(new Vector3(mousePos.x, mousePos.y, profile.DefaultHandDistance));
            }
            else
            {
                state.ViewportPosition = new Vector3(0.5f, 0.5f, profile.DefaultHandDistance);
            }

            state.Gesture = profile.DefaultHandGesture;
            state.ResetGesture();
            state.ResetRotation();
        }
        public void RecenterHand(Handedness handedness)
        {
            SimulatedHandState handToMove = stateRight;

            if (handedness == Handedness.Left)
            {
                handToMove = stateLeft;
            }
            else if (handedness == Handedness.Right)
            {
                handToMove = stateRight;
            }
            else
            {
                RecenterHand(Handedness.Left);
                RecenterHand(Handedness.Right);
                return;
            }

            handToMove.RecenterAtDistance(profile.DefaultHandDistance);
            AnimateGesture(handToMove, profile.HandGestureAnimationSpeed * Time.deltaTime);
            ApplyHandData();
        }
        /// <summary>
        /// Capture a snapshot of simulated hand data based on current state.
        /// </summary>
        public bool UpdateHandData(SimulatedHandData handDataLeft, SimulatedHandData handDataRight, SimulatedHandData handDataGaze, MouseDelta mouseDelta)
        {
            SimulateUserInput(mouseDelta);

            SimulatedHandState handStateLeft  = InputStateLeft as SimulatedHandState;
            SimulatedHandState handStateRight = InputStateRight as SimulatedHandState;
            SimulatedHandState handStateGaze  = InputStateGaze as SimulatedHandState;

            handStateLeft.Update();
            handStateRight.Update();
            handStateGaze.Update();

            bool handDataChanged = false;

            // Cache the generator delegates so we don't gc alloc every frame
            if (generatorLeft == null)
            {
                generatorLeft = handStateLeft.FillCurrentFrame;
            }

            if (generatorRight == null)
            {
                generatorRight = handStateRight.FillCurrentFrame;
            }

            if (generatorGaze == null)
            {
                generatorGaze = handStateGaze.FillCurrentFrame;
            }

            handDataChanged |= handDataLeft.Update(handStateLeft.IsTracked, handStateLeft.IsPinching, generatorLeft);
            handDataChanged |= handDataRight.Update(handStateRight.IsTracked, handStateRight.IsPinching, generatorRight);
            handDataChanged |= handDataGaze.Update(handStateGaze.IsTracked, handStateGaze.IsPinching, generatorGaze);

            return(handDataChanged);
        }