void UpdateDrag()
    {
        bool  dragButtonPressed = Input.GetKey(KeyCode.P);
        float dragPos           = mouseSource.MousePos.y;

        if (dragButtonPressed)
        {
            if (!wasDragButtonPressed)
            {
                dragStartPosY = dragPos;
                //DragDistanceDelta = 0;
                dragControl.ApplyPos(true, Vector2.zero);
            }
            else
            {
                // Also update drag distance delta
                dragControl.ApplyPos(true, new Vector2(0, dragPos - dragStartPosY));
            }
        }
        else
        {
            dragControl.ApplyPos(false, Vector2.zero);
        }

        wasDragButtonPressed = dragButtonPressed;
    }
    void UpdateDrag()
    {
        bool isDragging = buttonSelectPressed && IsManipulating();

        if (isDragging)
        {
            if (!prevDragging)
            {
                dragControl.ApplyPos(false, Vector2.zero);
            }
            else
            {
                Vector3 posDelta  = position - prevPosition;
                float   pdDot     = Vector3.Dot(posDelta.normalized, rotation * Vector3.forward);
                float   distDelta = pdDot * posDelta.magnitude;

                dragControl.ApplyDelta(true, new Vector2(0, distDelta));
            }
        }
        else
        {
            dragControl.ApplyPos(false, Vector2.zero);
        }

        // Debug sphere
        if (debugSphere != null)
        {
            debugSphere.transform.position = position;            // + rotation * Vector3.forward;
        }

        prevPosition = position;
        prevDragging = isDragging;
    }
    public override void _Update()
    {
        if (!IsEnabled)
        {
            return;
        }

        debugInputAmount  = inputAmount;
        debugHandControlX = dragControl.pos.x;
        debugHandControlY = dragControl.pos.y;

        if (handsSource.NumHandsVisible > 0)
        {
            if (prevHandsVisible == 0)
            {
                startRotationInv = Quaternion.Inverse(getLocalHandRotation());
            }
            else
            {
                Quaternion dragOffsetRotation = startRotationInv * getLocalHandRotation();
                dragControl.ApplyPos(true, normalizedYawPitch(dragOffsetRotation));
            }
        }
        else
        {
            dragControl.ApplyPos(false, Vector2.zero);
        }

        prevHandsVisible = handsSource.NumHandsVisible;

        base._Update();
    }
    /// <summary>
    /// Updates all control states.  See InputControlState.cs.
    /// </summary>
    void UpdateStates()
    {
        // Hand zoom gesture
        stateHandZoom.ApplyState(inputSources.hands.dragControl);

        // Mouse wheel zoom
        stateMouseWheelZoom.AddState(false, new Vector2(0, inputSources.editor.mouseSource.ScrollWheelDelta));
        stateMouseWheelZoom.AddState(false, new Vector2(0, inputSources.worldCursorMouse.mouseSource.ScrollWheelDelta));
        stateMouseWheelZoom.FinalizeState();

        // SixDOF zoom gesture
        stateSixDOFZoom.ApplyDelta(false, new Vector2(0, inputSources.touch6D.dragControl.delta.y));

        // Controller input maps to scrolling, zooming, and freecam input
        stateLeftJoyScroll.ApplyDelta(false, inputSources.gamepad.leftJoyVector);
        stateLeftJoyTranslate.ApplyDelta(false, inputSources.gamepad.leftJoyVector);
        stateTrigZoom.ApplyDelta(false, new Vector2(0, -inputSources.gamepad.trigVector.x + inputSources.gamepad.trigVector.y));
        stateRightJoyRotate.ApplyDelta(false, inputSources.gamepad.rightJoyVector);
        statePadTranslate.ApplyDelta(false, inputSources.gamepad.padVector);
        statePadCardinal.ApplyDelta(false, inputSources.gamepad.padVector);

        // Joystick cardinal state
        Vector2 joyPos = Vector2.zero;

        if (inputSources.gamepadCardinal.IsActiveTargetingSource())
        {
            float mag = 0.7f;
            if (inputSources.gamepad.leftJoyVector.sqrMagnitude > mag * mag)
            {
                joyPos = inputSources.gamepad.leftJoyVector;
                // Quantize
                joyPos.x = (float)Mathf.RoundToInt(5f * joyPos.x) / 5f;
                joyPos.y = (float)Mathf.RoundToInt(5f * joyPos.y) / 5f;
            }
        }
        stateLeftJoyCardinal.ApplyDelta(false, joyPos);

        // Update drag gesture
        InputSourceBase curSource = inputSwitchLogic.CurrentTargetingSource as InputSourceBase;

        if (curSource != null && curSource.IsManipulating())
        {
            stateTargetScroll.ApplyPos(true, curSource.GetManipulationPlaneProjection());
            //Debug.Log("scroll state delta: " + stateTargetScroll.delta);
        }
        else
        {
            stateTargetScroll.ApplyPos(false, Vector2.zero);
            //Debug.Log("scroll (done) state delta: " + stateTargetScroll.delta);
        }
    }
    public override void _Update()
    {
        // We can't trust button input from the remote mouse, since it generates a lot of click events when you're only trying to move it
        touchState.ApplyPos(false, InputSources.Instance.netMouse.mousePos);

        UpdateDrag();

        UpdatePrecision();

        bool prev = m_Select;

        m_Select = (this as ITargetingInputSource).IsSelectPressed();
        if (prev != m_Select)
        {
            OnSelectChanged(this, m_Select);
        }

        prev   = m_Menu;
        m_Menu = (this as ITargetingInputSource).IsMenuPressed();
        if (prev != m_Menu)
        {
            OnMenuChanged(this, m_Menu);
        }

        base._Update();
    }
    void UpdateHandDrag()
    {
        bool    isDragging = NumFingersPressed > 0 && IsManipulating();
        Vector3 handPos    = GetWorldPosition(0);

        if (isDragging)
        {
            if (!prevDragging)
            {
                dragStartHeadPosition = HeadTransform.position;

                if (handPos.y < dragStartHeadPosition.y)
                {
                    dragStartHeadPosition.y = handPos.y;
                }

                dragStartHeadRotation = HeadTransform.rotation;

                dragLocalHandStart = Quaternion.Inverse(dragStartHeadRotation) * (handPos - dragStartHeadPosition);

                dragControl.ApplyPos(false, Vector2.zero);

                prevHandPosition = handPos;
                prevDragging     = isDragging;
                UpdateHandDrag();
            }
            else
            {
                // Use the head position pivot, but at the starting height
                Vector3 pivotPos = HeadTransform.position;
                pivotPos.y = dragStartHeadPosition.y;

                // Find where the hand has moved relative to the starting pose (of head and hand)
                Vector3 localOffsetFromStart = Quaternion.Inverse(dragStartHeadRotation) * (handPos - pivotPos);

                // Get the difference in rotation
                Quaternion handRotation = Quaternion.FromToRotation(dragLocalHandStart, localOffsetFromStart);

                // Apply to original head direction
                adjustedHandTargetRot = dragStartHeadRotation * handRotation;

                // Update drag distance
                Vector3 posDelta  = handPos - prevHandPosition;
                float   pdDot     = Vector3.Dot(posDelta.normalized, HeadTransform.forward);
                float   distDelta = pdDot * posDelta.magnitude;

                // Deadzone?
                //distDelta = Mathf.Sign(distDelta) * Mathf.Max(0, Mathf.Abs(distDelta) - dragDistanceDeadzone);

                // Update the drag control
                dragControl.ApplyDelta(true, new Vector2(0, distDelta));
            }
        }
        else
        {
            dragControl.ApplyPos(false, Vector2.zero);
        }

        prevHandPosition = handPos;
        prevDragging     = isDragging;
    }