예제 #1
0
        private void renderNewReceiverGained(Receiver receiver)
        {
            if (getControllerPointFunc == null)
            {
                getControllerPointFunc = getControllerPoint;
            }

            if (getReceiverPointFunc == null)
            {
                getReceiverPointFunc = getReceiverPoint;
            }

            DebugPing.PingCone(getControllerPointFunc,
                               getReceiverPointFunc,
                               renderColor,
                               0.3f,
                               DebugPing.AnimType.Fade);

            DebugPing.Ping(getReceiverPointFunc,
                           renderColor, 0.40f, DebugPing.AnimType.ExpandAndFade);
            DebugPing.Ping(getReceiverPointFunc,
                           renderColor, 0.42f, DebugPing.AnimType.ExpandAndFade);
            DebugPing.Ping(getReceiverPointFunc,
                           renderColor, 0.44f, DebugPing.AnimType.ExpandAndFade);
            DebugPing.Ping(getReceiverPointFunc,
                           renderColor, 0.46f, DebugPing.AnimType.ExpandAndFade);
            DebugPing.Ping(getReceiverPointFunc,
                           renderColor, 0.48f, DebugPing.AnimType.ExpandAndFade);
        }
예제 #2
0
        protected override void WhileHandTracked(Hand hand)
        {
            // Update pose with the position of the pinch, which is theoretical if there's no
            // pinch but absolute when a pinch is actually occuring.
            var pinchPosition = hand.GetPredictedPinchPosition();

            var avgIndexThumbTip = ((hand.GetIndex().TipPosition
                                     + hand.GetThumb().TipPosition) / 2f).ToVector3();

            pinchPosition = Vector3.Lerp(pinchPosition, avgIndexThumbTip, _latestPinchStrength);

            _lastPinchPose = new Pose()
            {
                position = pinchPosition,
                rotation = hand.Rotation.ToQuaternion()
            };

            // Reset the "degenerate conditions" timer if we detect that we're looking down
            // the wrist of the hand; here fingers are usually occluded, so we want to ignore
            // pinch information in this case.
            var lookingDownWrist = Vector3.Angle(hand.DistalAxis(),
                                                 hand.PalmPosition.ToVector3() - Camera.main.transform.position) < 25f;

            if (lookingDownWrist)
            {
                if (_drawDebug)
                {
                    DebugPing.Ping(hand.WristPosition.ToVector3(), Color.black, 0.10f);
                }
                minReactivateSinceDegenerateConditionsTimer = 0;
            }
        }
        protected override bool ShouldGestureActivate(Hand leftHand, Hand rightHand)
        {
            Vector3 positionOfInterest;
            bool    isGesturePoseHeld = IsGesturePoseHeld(leftHand, rightHand, out positionOfInterest);

            _lastKnownPositionOfInterest = positionOfInterest;

            if (!isGesturePoseHeld && !_gestureReady)
            {
                _gestureReady = true;
            }

            bool isRelativeHandVelocityLow = updateIsRelativeHandVelocityLow(leftHand,
                                                                             rightHand);

            if (isRelativeHandVelocityLow && isGesturePoseHeld && _gestureReady)
            {
                if (drawHeldPoseDebug)
                {
                    DebugPing.Ping(positionOfInterest, LeapColor.red, 0.10f);
                }

                return(true);
            }

            return(false);
        }
예제 #4
0
        public Pose GetPose()
        {
            var handleKinematicState = handleKinematicStateProvider.GetKinematicState();

            var handlePose = handleKinematicState.pose;

            var handleToAttachedUIPose = handleAttachmentPoseProvider.GetHandleToAttachmentPose();

            var layoutPos = LayoutUtils.LayoutThrownUIPosition2(
                Camera.main.transform.ToPose(),

                //handlePose.position,

                handlePose.Then(handleToAttachedUIPose).position,

                handleKinematicState.movement.velocity,
                optimalHeightFromHead: optimalHeightFromHead,
                optimalDistance: optimalDistance);

            if (drawDebug)
            {
                DebugPing.Ping(handlePose, LeapColor.red, 0.2f);
                DebugPing.PingCapsule(handlePose.position, layoutPos, LeapColor.purple, 0.2f);
                DebugPing.Ping(layoutPos, LeapColor.blue, 0.2f);
            }

            var solvedHandlePose =
                new Pose(layoutPos,
                         Utils.FaceTargetWithoutTwist(layoutPos,
                                                      Camera.main.transform.position,
                                                      flip180))
                .Then(handleToAttachedUIPose.inverse);

            return(solvedHandlePose);
        }
예제 #5
0
        protected override void WhileGestureActive(Hand hand)
        {
            if (_drawDebugPath)
            {
                DebugPing.Ping(hand.GetPredictedPinchPosition(), LeapColor.amber, 0.05f);
            }

            // TODO: Make this a part of OneHandedGesture so this doesn't have to be explicit!
            OnSend(this.pose);
        }
        private void fireOnMoved(Pose movedToPose)
        {
            OnMoved();
            OnMovedHandle(this, movedToPose);

            if (drawDebugGizmos)
            {
                DebugPing.Ping(intObj.transform.position, LeapColor.blue, 0.075f);
            }
        }
        private void fireOnPickedUp()
        {
            if (anchObj.isAttached)
            {
                OnPlaced();
            }

            OnPickedUp();
            OnPickedUpHandle(this);

            if (drawDebugGizmos)
            {
                DebugPing.Ping(intObj.transform.position, LeapColor.cyan, 0.5f);
            }
        }
        private bool updateIsRelativeHandVelocityLow(Hand leftHand, Hand rightHand)
        {
            var leftHandPos  = leftHand.PalmPosition.ToVector3();
            var rightHandPos = rightHand.PalmPosition.ToVector3();
            var leftMinusRightHandPosition = leftHandPos - rightHandPos;

            leftMinusRightHandPosBuffer.Add(leftMinusRightHandPosition, Time.time);

            if (leftMinusRightHandPosBuffer.IsFull)
            {
                var relativeHandVelocity = leftMinusRightHandPosBuffer.Delta();

                if (drawHeldPoseDebug)
                {
                    RuntimeGizmoDrawer drawer = null;
                    if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                    {
                        drawer.DrawBar(relativeHandVelocity.magnitude,
                                       (leftHandPos + rightHandPos) / 2f,
                                       Vector3.up, 0.1f);
                    }
                }

                if (relativeHandVelocity.sqrMagnitude < MAX_RELATIVE_HAND_VELOCITY_SQR)
                {
                    return(true);
                }
                else
                {
                    if (drawHeldPoseDebug)
                    {
                        DebugPing.Ping((leftHandPos + rightHandPos) / 2f, LeapColor.black, 0.2f);
                    }
                    return(false);
                }
            }

            if (drawHeldPoseDebug)
            {
                DebugPing.Ping((leftHandPos + rightHandPos) / 2f, LeapColor.gray, 0.1f);
            }

            return(false);
        }
        protected override bool ShouldGestureDeactivate(Hand leftHand, Hand rightHand,
                                                        out DeactivationReason?deactivationReason)
        {
            Vector3 positionOfInterest;
            bool    isGesturePoseHeld = IsGesturePoseHeld(leftHand, rightHand, out positionOfInterest);

            _lastKnownPositionOfInterest = positionOfInterest;

            if (updateIsRelativeHandVelocityLow(leftHand, rightHand) &&
                isGesturePoseHeld)
            {
                if (_gestureActiveTime > holdActivationDuration)
                {
                    // Gesture finished successfully.
                    if (drawHeldPoseDebug)
                    {
                        DebugPing.Ping(positionOfInterest, LeapColor.cerulean, 0.20f);
                    }

                    deactivationReason = DeactivationReason.FinishedGesture;
                    _gestureReady      = false;

                    return(true);
                }
                else
                {
                    // Continue gesture activation.
                    deactivationReason = null;
                    return(false);
                }
            }
            else
            {
                // Gesture was cancelled.
                deactivationReason = DeactivationReason.CancelledGesture;
                return(true);
            }
        }
예제 #10
0
        private void onGraspEnd()
        {
            if (anchObj != null && anchObj.preferredAnchor != null)
            {
                OnPlacedInContainer();
                OnPlacedHandleInContainer(this);
            }
            else if (intObj.rigidbody.velocity.magnitude > PhysicalInterfaceUtils.MIN_THROW_SPEED)
            {
                OnThrown(intObj.rigidbody.velocity);
                OnThrownHandle(this, intObj.rigidbody.velocity);
            }
            else
            {
                OnPlaced();
                OnPlacedHandle(this);
            }

            if (drawDebugGizmos)
            {
                DebugPing.Ping(intObj.transform.position, LeapColor.orange, 0.5f);
            }
        }
예제 #11
0
        public float GetCustomPinchDistance(Hand h)
        {
            Vector3 c0, c1;
            var     pinchDistance = PinchSegment2SegmentDisplacement(h, out c0, out c1).magnitude;

            pinchDistance -= 0.01f;
            pinchDistance  = pinchDistance.Clamped01();

//      if (Input.GetKeyDown(KeyCode.C)) {
//        Debug.Log(pinchDistance);
//      }

            if (drawDebugPinchDistance)
            {
                DebugPing.Line("RH pinch", c0, c1, LeapColor.blue);
                DebugPing.Label("RH pinch",
                                labelText: pinchDistance.ToString("F3"),
                                labeledPosition: ((c1 + c0) / 2f),
                                color: LeapColor.blue);
            }

            return(pinchDistance);
        }
예제 #12
0
        protected override bool ShouldGestureDeactivate(Hand hand,
                                                        out DeactivationReason?
                                                        deactivationReason)
        {
            deactivationReason = DeactivationReason.FinishedGesture;

            bool shouldDeactivate = false;

            _latestPinchStrength = 1f;
            OnPinchStrengthEvent.Invoke(_latestPinchStrength);

            if (minDeactivateTimer > MIN_DEACTIVATE_TIME)
            {
                var pinchDistance = PinchSegment2SegmentDisplacement(hand).magnitude;

                if (pinchDistance > pinchDeactivateDistance)
                {
                    shouldDeactivate = true;

                    if (_drawDebug)
                    {
                        DebugPing.Ping(hand.GetPredictedPinchPosition(), Color.black, 0.20f);
                    }
                }
            }
            else
            {
                minDeactivateTimer++;
            }

            if (shouldDeactivate)
            {
                minReactivateTimer = 0;
            }

            return(shouldDeactivate);
        }
예제 #13
0
        void Update()
        {
            _wasActivated   = false;
            _wasDeactivated = false;
            _wasCancelled   = false;
            _wasFinished    = false;

            // Update the sequence.
            if (sequenceGraph.Length != 0)
            {
                var curGestureNode = sequenceGraph[_curSequenceIdx];

                bool shouldActivate = false;
                bool shouldCancel   = false;
                bool shouldFinish   = false;

                // This gesture sequence as long as the current gesture in the sequence is
                // active.
                if (curGestureNode.gesture.isActive)
                {
                    shouldActivate = true;

                    // Check if the current gesture is an IPoseGesture, in which case, inherit its
                    // pose.
                    if (curGestureNode.gesture is IPoseGesture)
                    {
                        this._latestGesturePose = (curGestureNode.gesture as IPoseGesture).pose;
                    }
                }

                if (curGestureNode.gesture.wasFinished)
                {
                    // This gesture was completed, so next frame we'll look at the next
                    // gesture in the sequence.
                    _curSequenceIdx += 1;
                    if (drawDebug)
                    {
                        DebugPing.Ping(Camera.main.transform.position
                                       + Camera.main.transform.forward * 0.5f,
                                       LeapColor.blue, 0.1f * _curSequenceIdx);
                    }

                    // Also reset the gesture timer.
                    _nextGestureTimer = 0f;

                    if (_curSequenceIdx == sequenceGraph.Length)
                    {
                        // We hit the end of the sequence successfully!
                        shouldFinish = true;
                        if (drawDebug)
                        {
                            DebugPing.Ping(Camera.main.transform.position
                                           + Camera.main.transform.forward * 0.5f,
                                           LeapColor.green, 0.11f * _curSequenceIdx);
                            DebugPing.Ping(Camera.main.transform.position
                                           + Camera.main.transform.forward * 0.5f,
                                           LeapColor.yellow, 0.105f * _curSequenceIdx);
                        }
                    }
                }
                else
                {
                    // Wait for the gesture to begin, or cancel this sequence if the
                    // current gesture in the sequence was cancelled.
                    if (curGestureNode.gesture.wasCancelled)
                    {
                        if (drawDebug)
                        {
                            DebugPing.Ping(Camera.main.transform.position
                                           + Camera.main.transform.forward * 0.5f,
                                           LeapColor.black, 0.11f * _curSequenceIdx);
                            DebugPing.Ping(Camera.main.transform.position
                                           + Camera.main.transform.forward * 0.5f,
                                           LeapColor.red, 0.105f * _curSequenceIdx);
                        }

                        shouldCancel = true;
                    }
                    else if (!curGestureNode.gesture.isActive)
                    {
                        _nextGestureTimer += Time.deltaTime;

                        if (_nextGestureTimer > curGestureNode.waitDuration)
                        {
                            if (drawDebug)
                            {
                                DebugPing.Ping(Camera.main.transform.position
                                               + Camera.main.transform.forward * 0.5f,
                                               LeapColor.black, 0.11f * _curSequenceIdx);
                                DebugPing.Ping(Camera.main.transform.position
                                               + Camera.main.transform.forward * 0.5f,
                                               LeapColor.black, 0.105f * _curSequenceIdx);
                            }

                            shouldCancel = true;
                        }
                    }
                }

                // Set this gesture state appropriately.
                if (shouldActivate)
                {
                    if (!_isActive)
                    {
                        _wasActivated = true;
                    }
                    _isActive = true;
                }
                else if (shouldCancel)
                {
                    if (_isActive)
                    {
                        _isActive       = false;
                        _wasDeactivated = true;
                        _wasCancelled   = true;
                        _wasFinished    = false;
                    }
                }
                else if (shouldFinish)
                {
                    if (_isActive)
                    {
                        _isActive       = false;
                        _wasDeactivated = true;
                        _wasCancelled   = false;
                        _wasFinished    = true;
                    }
                }

                if (_wasCancelled || _wasFinished)
                {
                    _curSequenceIdx = 0;
                }
            }
        }
예제 #14
0
            public void UpdateCentroidMovement(Vector3?[] positions,
                                               float[] strengths = null,
                                               bool drawDebug    = false)
            {
                if (strengths != null && positions.Length != strengths.Length)
                {
                    throw new InvalidOperationException(
                              "positions and strengths Indexables must have the same Count.");
                }


                bool[] useableIndices = new bool[_lastPositions.Length];

                _didCentroidAppear    = false;
                _didCentroidDisappear = false;

                int numLastValidPositions = CountValid(_lastPositions);
                int numCurValidPositions  = CountValid(positions);

                if (numLastValidPositions == 0 && numCurValidPositions > 0)
                {
                    _didCentroidAppear = true;
                }
                if (numLastValidPositions > 0 && numCurValidPositions == 0)
                {
                    _didCentroidDisappear = true;
                }

                // Useable indices have valid positions in both the "last" and "current" arrays.
                for (int i = 0; i < _lastPositions.Length; i++)
                {
                    if (i >= positions.Length)
                    {
                        break;
                    }

                    var lastV = _lastPositions[i];
                    var curV  = positions[i];

                    if (lastV.HasValue && curV.HasValue)
                    {
                        useableIndices[i] = true;
                    }
                    else if (!lastV.HasValue && !curV.HasValue)
                    {
                        // One index has a value in one array and no value in the other;
                        // this means the Centroid is going to teleport.
                        _didCentroidTeleport = true;
                    }
                }

                _isMoving = false;
                _avgDelta = Vector3.zero;
                int count = 0;

                for (int i = 0; i < useableIndices.Length; i++)
                {
                    if (useableIndices[i])
                    {
                        _isMoving = true;
                        var addedDelta = (positions[i] - _lastPositions[i]).Value;
                        if (strengths != null)
                        {
                            addedDelta *= strengths[i];
                        }
                        _avgDelta += addedDelta;
                        count++;
                    }
                }
                if (count > 0)
                {
                    _avgDelta /= count;
                }


                // Update centroid state.

                if (_didCentroidAppear)
                {
                    _centroid = positions.Query()
                                .Select(maybeV => maybeV.GetValueOrDefault())
                                .Fold((acc, v) => acc + v)
                                / numCurValidPositions;

                    if (drawDebug)
                    {
                        DebugPing.Ping(_centroid.Value, LeapColor.cyan, 0.20f);
                    }
                }

                if (_centroid != null)
                {
                    _centroid += _avgDelta;

                    if (drawDebug)
                    {
                        DebugPing.Ping(_centroid.Value, LeapColor.green, 0.15f);
                    }
                }

                if (_didCentroidDisappear)
                {
                    if (drawDebug)
                    {
                        DebugPing.Ping(_centroid.Value, LeapColor.black, 0.20f);
                    }

                    _centroid = null;
                }


                // Set last positions with the current positions.

                for (int i = 0; i < _lastPositions.Length; i++)
                {
                    if (i >= positions.Length)
                    {
                        _lastPositions[i] = null;
                    }
                    else
                    {
                        _lastPositions[i] = positions[i];
                    }
                }
            }
예제 #15
0
        public Vector3 GetTargetPosition()
        {
            Vector3 layoutPos;

            if (!uiHandle.wasThrown)
            {
                layoutPos = uiHandle.pose.position;

                if (drawDebug)
                {
                    DebugPing.Ping(layoutPos, Color.white);
                }
            }
            else
            {
                // When the UI is thrown, utilize the static thrown UI util to calculate a decent
                // final position relative to the user's head given the position and velocity of
                // the throw.
                layoutPos = PhysicalInterfaceUtils.LayoutThrownUIPosition2(
                    Camera.main.transform.ToPose(),
                    uiHandle.pose.position,
                    uiHandle.movement.velocity,
                    layoutDistanceMultiplier
                    );

                // However, UIs whose central "look" anchor is in a different position than their
                // grabbed/thrown anchor shouldn't be placed directly at the determined position.
                // Rather, we need to adjust this position so that the _look anchor,_ not the
                // thrown handle, winds up in the calculated position from the throw.

                // Start with the "final" pose as it would currently be calculated.
                // We need to know the target rotation of the UI based on the target position in
                // order to adjust the final position properly.
                Pose finalUIPose = new Pose(layoutPos, GetTargetRotationForPosition(layoutPos));

                // We assume the uiAnchorHandle and the uiLookAnchor are rigidly connected.
                Vector3 curHandleToLookAnchorOffset = (uiLookPositionProvider.GetTargetWorldPosition()
                                                       - uiHandle.pose.position);

                // We undo the current rotation of the UI handle and apply that rotation
                // on the current world-space offset between the handle and the look anchor.
                // Then we apply the final rotation of the UI to this unrotated offset vector,
                // giving us the expected final offset between the position that was calculated
                // by the layout function and the handle.
                Vector3 finalRotatedLookAnchorOffset =
                    finalUIPose.rotation
                    * (Quaternion.Inverse(uiHandle.pose.rotation)
                       * curHandleToLookAnchorOffset);

                // We adjust the layout position by this offset, so now the UI should wind up
                // with its lookAnchor at the calculated location instead of the handle.
                layoutPos = layoutPos - finalRotatedLookAnchorOffset;

                // We also adjust any interface positions down a bit.
                layoutPos += (Camera.main.transform.parent != null ?
                              -Camera.main.transform.parent.up
                      : Vector3.down) * 0.19f;

                if (drawDebug)
                {
                    DebugPing.Ping(layoutPos, Color.red);
                }
            }

            return(layoutPos);
        }
예제 #16
0
        private void Update()
        {
            // Current facing.
            var curFacing = this.transform.forward.xz();

            // Movement input.
            float moveVecMag;
            var   moveVec          = getInputMovementVector(out moveVecMag);
            var   moveVecDir       = (moveVecMag > 0.01f ? moveVec / moveVecMag : curFacing);
            var   isMovingIntended = moveVecMag > 0.01f;

            if (cameraTransform != null && isMovingIntended)
            {
                var camXZToPlayer
                    = this.transform.position.ProjectedOnPlane(this.transform.up)
                      - cameraTransform.position.ProjectedOnPlane(this.transform.up);
                var camXZAngle  = Vector3.SignedAngle(camXZToPlayer, Vector3.forward, Vector3.up);
                var camRotation = Quaternion.AngleAxis(-camXZAngle, Vector3.up);
                moveVec    = (camRotation * moveVec.AsXZ()).xz();
                moveVecDir = (camRotation * moveVecDir.AsXZ()).xz();
            }
            if (drawDebug)
            {
                DebugPing.Line("moveVec", this.transform.position,
                               this.transform.position + moveVec.AsXZ(), LeapColor.red);
            }

            // Movement.
            if (!_lastPosMem.HasValue)
            {
                _lastPosMem = this.transform.position;
            }
            var curVelXZ = ((this.transform.position - _lastPosMem.Value) / Time.deltaTime).xz();

            if (isMovingIntended)
            {
                curVelXZ  = curVelXZ.RotatedTowards(moveVecDir, turnSpeed * Time.deltaTime);
                curVelXZ += speed * moveVec * Time.deltaTime;
            }
            curVelXZ                *= 1 - (movementDecay * Time.deltaTime).Clamped01();
            _lastPosMem              = this.transform.position;
            this.transform.position += curVelXZ.AsXZ() * Time.deltaTime;
            var curSpdXZ = curVelXZ.magnitude;

            // Facing.
            var facingIntent = _lastFacingIntentMem.ValueOr(moveVecDir);

            if (isMovingIntended)
            {
                var facingErrAngle
                    = Vector3.SignedAngle(curFacing.AsXZ(), moveVecDir.AsXZ(), Vector3.up);
                if (facingErrAngle.Abs() > 179.1f)
                {
                    curFacing = (Quaternion.AngleAxis(1f, Vector3.up) * curFacing.AsXZ()).xz();
                }

                facingIntent         = moveVecDir;
                _lastFacingIntentMem = moveVecDir;
            }
            curFacing = curFacing.SlerpedTo(facingIntent, turnSpeed * Time.deltaTime);
            this.transform.SetForward(curFacing.AsXZ());

            // Animation.
            if (curSpdXZ > 0.5f)
            {
                poser.curPoseIdx = poseIdx_dash;
            }
            else
            {
                poser.curPoseIdx = poseIdx_stand;
            }
        }
예제 #17
0
        protected override bool ShouldGestureActivate(Hand hand)
        {
            bool shouldActivate = false;

            var wasEligibleLastCheck = _isGestureEligible;

            _isGestureEligible = false;

            // Update curl samples for each index and middle.
            updateIndexCurl(hand);
            updateMiddleCurl(hand);

            // Need to update the "pinch strength" during processing.
            _latestPinchStrength = 0f;

            // Can only activate a pinch if we haven't already activated a pinch very recently.
            if (minReactivateTimer > MIN_REACTIVATE_TIME)
            {
                // Can only activate a pinch if we're a certain number of frames past the last
                // frame where the hand was in a tracking-degenerate orientation (e.g. looking
                // down the wrist.)
                if (minReactivateSinceDegenerateConditionsTimer
                    > MIN_REACTIVATE_TIME_SINCE_DEGENERATE_CONDITIONS)
                {
                    // Update pinch and hand position samples.
                    var latestPinchDistance = GetCustomPinchDistance(hand);
                    OnPinchStrengthEvent.Invoke(latestPinchDistance);
                    _handPositionBuffer.Add(hand.PalmPosition.ToVector3(), Time.time);

                    // Full buffer == optimally stable hand velocity, also implicitly enforces
                    // a hand lifetime. Hand velocity NOT CURRENTLY ACTUALLY USED.
                    if (_handPositionBuffer.IsFull)
                    {
                        // Determine whether the hand meets the FOV heuristic -- result may be
                        // ignored depending on public settings.
                        var handFOVAngle = Vector3.Angle(Camera.main.transform.forward,
                                                         hand.PalmPosition.ToVector3() - Camera.main.transform.position);
                        var handWithinFOV = handFOVAngle < Camera.main.fieldOfView / 2.2f;

                        // Heuristic: Higher hand velocity == more stringent pinch requirement.
                        // Goal: Reduce accidental pinches when e.g. dropping the hands by requiring
                        // a more "certain" pinch while the hand is moving.
                        // CURRENTLY UNUSED. Comment left here as potential inspiration for
                        // additional heuristics. TODO DELETEME.
            #pragma warning disable 0219
                        var handVelocity = _handPositionBuffer.Delta();
            #pragma warning restore 0219

                        #region Middle Finger Safety

                        var palmDir = hand.PalmarAxis();

                        var middleDir             = hand.GetMiddle().bones[1].Direction.ToVector3();
                        var signedMiddlePalmAngle = Vector3.SignedAngle(palmDir,
                                                                        middleDir,
                                                                        hand.RadialAxis());
                        if (hand.IsLeft)
                        {
                            signedMiddlePalmAngle *= -1f;
                        }

                        #endregion

                        #region Ring Finger Safety

                        var ringDir             = hand.GetRing().bones[1].Direction.ToVector3();
                        var signedRingPalmAngle = Vector3.SignedAngle(palmDir,
                                                                      ringDir,
                                                                      hand.RadialAxis());
                        if (hand.IsLeft)
                        {
                            signedRingPalmAngle *= -1f;
                        }

                        #endregion

                        #region Index Angle (Eligibility Only)

                        // Note: obviously pinching already requires the index finger to
                        // close relative to the palm -- this check simply drives the
                        // isEligible state for this pinch gesture so that the gesture isn't
                        // "eligible" when the hand is fully open.

                        var indexDir       = hand.GetIndex().bones[1].Direction.ToVector3();
                        var indexPalmAngle = Vector3.Angle(indexDir, palmDir);

                        #endregion

                        #region Thumb Angle (Eligibility Only)

                        // Note: obviously pinching already requires the thumb finger to
                        // close to touch the index finger -- this check simply drives the
                        // isEligible state for this pinch gesture so that the gesture isn't
                        // "eligible" when the hand is fully open.

                        var thumbDir       = hand.GetThumb().bones[2].Direction.ToVector3();
                        var thumbPalmAngle = Vector3.Angle(thumbDir, palmDir);

                        #endregion

                        #region Check: Eligibility

                        // Eligibility checks -- necessary, but not sufficient conditions to start
                        // a pinch, suitable for e.g. visual feedback on whether the gesture is
                        // "able to occur" or "about to occur."
                        if (

                            ((!wasEligibleLastCheck &&
                              signedMiddlePalmAngle >= minPalmMiddleAngle) ||
                             (wasEligibleLastCheck &&
                              signedMiddlePalmAngle >= minPalmMiddleAngle
                              * ringMiddleSafetyHysteresisMult) ||
                             !requireMiddleAndRingSafetyPinch)

                            && ((!wasEligibleLastCheck &&
                                 signedRingPalmAngle >= minPalmRingAngle) ||
                                (wasEligibleLastCheck &&
                                 signedRingPalmAngle >= minPalmRingAngle
                                 * ringMiddleSafetyHysteresisMult) ||
                                !requireMiddleAndRingSafetyPinch)

                            // Index angle (eligibility state only)
                            && ((!wasEligibleLastCheck &&
                                 indexPalmAngle < maxIndexAngleForEligibilityActivation) ||
                                (wasEligibleLastCheck &&
                                 indexPalmAngle < maxIndexAngleForEligibilityDeactivation))

                            // Thumb angle (eligibility state only)
                            && ((!wasEligibleLastCheck &&
                                 thumbPalmAngle < maxThumbAngleForEligibilityActivation) ||
                                (wasEligibleLastCheck &&
                                 thumbPalmAngle < maxThumbAngleForEligibilityDeactivation))

                            // FOV.
                            && (handWithinFOV)

                            // Must cross pinch threshold from a non-pinching / non-fist pose.
                            && (!requiresRepinch)

                            )
                        {
                            // Conceptually, this should be true when all but the most essential
                            // parameters for the gesture are satisfied, so the user can be notified
                            // that the gesture is imminent.
                            _isGestureEligible = true;
                        }

                        #endregion

                        #region Update Pinch Strength

                        // Update global "pinch strength".
                        // If the gesture is eligible, we'll have a non-zero pinch strength.
                        if (_isGestureEligible)
                        {
                            _latestPinchStrength = latestPinchDistance.Map(0f, pinchActivateDistance,
                                                                           1f, 0f);
                        }
                        else
                        {
                            _latestPinchStrength = 0f;
                        }

                        #endregion

                        #region Check: Pinch Distance

                        if (_isGestureEligible

                            // Absolute pinch strength.
                            && (latestPinchDistance < pinchActivateDistance)

                            )
                        {
                            shouldActivate = true;

                            if (_drawDebug)
                            {
                                DebugPing.Ping(hand.GetPredictedPinchPosition(), Color.red, 0.20f);
                            }
                        }

                        #endregion

                        #region Hysteresis for Failed Pinches

                        // "requiresRepinch" prevents a closed-finger configuration from beginning
                        // a pinch when the index and thumb never actually actively close from a
                        // valid position -- think, closed-fist to safety-pinch, as opposed to
                        // open-hand to safety-pinch -- without introducing any velocity-based
                        // requirement.
                        if (latestPinchDistance < pinchActivateDistance && !shouldActivate)
                        {
                            requiresRepinch = true;
                        }
                        if (requiresRepinch && latestPinchDistance > failedPinchResetDistance)
                        {
                            requiresRepinch = false;
                        }

                        #endregion
                    }
                }
                else
                {
                    minReactivateSinceDegenerateConditionsTimer += 1;
                }
            }
            else
            {
                minReactivateTimer += 1;
            }

            if (shouldActivate)
            {
                minDeactivateTimer = 0;
            }

            OnPinchStrengthEvent.Invoke(_latestPinchStrength);

            return(shouldActivate);
        }
예제 #18
0
        private void Update()
        {
            GetComponentsInChildren <LODItem>(items);

            var camera = Camera.main;

            counter += 1;
            var pingThisFrame = false;

            if (counter % 10 == 0)
            {
                counter = 0;
                if (drawDebug)
                {
                    pingThisFrame = true;
                }
            }

            var selector = GetComponent <PullTabSelector>();

            var     closestAngle = float.PositiveInfinity;
            LODItem closestItem  = null;

            foreach (var item in items)
            {
                var testAngle = Vector3.Angle(camera.transform.forward,
                                              item.transform.position - camera.transform.position);
                var testDist = Vector3.Distance(item.transform.position, camera.transform.position);


                if (pingThisFrame)
                {
                    DebugPing.Ping(item.transform.position, LeapColor.blue, 0.08f);

                    Debug.Log(testAngle);
                }

                if (testAngle < closestAngle &&
                    testAngle <= maxCameraLookAngle &&
                    testDist <= maxDetailDistance)
                {
                    closestAngle = testAngle;

                    if (selector != null && selector.listOpenCloseAmount < 0.10f)
                    {
                        var activeMarbleItem = selector.activeMarbleParent.GetComponentInChildren <LODItem>();
                        if (item == activeMarbleItem)
                        {
                            closestItem = item;
                        }
                    }
                    else
                    {
                        closestItem = item;
                    }

                    if (pingThisFrame)
                    {
                        DebugPing.Ping(item.transform.position, LeapColor.red, 0.09f);
                    }
                }
            }


            if (viewMode == ViewMode.Full)
            {
                foreach (var item in items)
                {
                    if (closestItem != null)
                    {
                        if (item.propertySwitch.GetIsOffOrTurningOff())
                        {
                            item.propertySwitch.On();
                        }
                    }
                    else
                    {
                        if (item.propertySwitch.GetIsOnOrTurningOn())
                        {
                            item.propertySwitch.Off();
                        }
                    }
                }
            }
            else if (viewMode == ViewMode.Fade)
            {
                foreach (var item in items)
                {
                    var detailActivation = 0f;
                    if (closestItem != null)
                    {
                        float dist;
                        switch (fadeDistanceMode)
                        {
                        case FadeDistanceMode.FastFalloff:
                            dist             = Mathf.Sqrt((item.transform.position - closestItem.transform.position).magnitude);
                            detailActivation = dist.Map(0f, Mathf.Sqrt(fadeRadius), 1f, 0f);
                            break;

                        case FadeDistanceMode.SlowFalloff:
                            dist             = (item.transform.position - closestItem.transform.position).sqrMagnitude;
                            detailActivation = dist.Map(0f, fadeRadius * fadeRadius, 1f, 0f);
                            break;

                        case FadeDistanceMode.Linear:
                        default:
                            dist             = (item.transform.position - closestItem.transform.position).magnitude;
                            detailActivation = dist.Map(0f, fadeRadius, 1f, 0f);
                            break;
                        }

                        if (detailActivation < cutoffActivationPercent)
                        {
                            detailActivation = 0f;
                        }
                    }

                    if (item.tweenSwitch != null)
                    {
                        item.tweenSwitch.SetTweenTarget(detailActivation);
                    }
                    else
                    {
                        Debug.LogError("Can't use Fade view mode for this item; it doesn't use a "
                                       + "TweenSwitch.", item);
                    }
                }
            }
            else if (viewMode == ViewMode.Single)
            {
                foreach (var item in items)
                {
                    if (item != closestItem && item.tweenSwitch != null &&
                        item.propertySwitch.GetIsOnOrTurningOn())
                    {
                        item.propertySwitch.Off();
                    }
                }
                // Older: one at a time;
                if (closestItem != null && closestItem.tweenSwitch != null &&
                    closestItem.propertySwitch.GetIsOffOrTurningOff())
                {
                    closestItem.propertySwitch.On();
                }
            }

            if (pingThisFrame && closestItem != null)
            {
                DebugPing.Ping(closestItem.transform.position, LeapColor.purple, 0.10f);
            }
        }
예제 #19
0
 private void renderLossOfSignal()
 {
     // TODO: Currently unused, signal is never nullified.
     DebugPing.Ping(controllerPoint, Color.black);
 }