예제 #1
0
        private void PreviewArea()
        {
            //visualize:
            Vector3[] playspacePerimeterOnFloor   = new Vector3[_playspaceCorners.Count];
            Vector3[] playspacePerimeterOnCeiling = new Vector3[_playspaceCorners.Count];

            //populate floor and ceiling corner loops:
            for (int i = 0; i < _playspaceCorners.Count; i++)
            {
                //cache floor and ceiling locations:
                Vector3 floor   = TransformUtilities.WorldPosition(pcfAnchor.Position, pcfAnchor.Rotation, _playspaceCorners[i]);
                Vector3 ceiling = floor + new Vector3(0, Height);
                playspacePerimeterOnFloor[i]   = floor;
                playspacePerimeterOnCeiling[i] = ceiling;

                //draw corner supports:
                LineRenderer cornerSupportsOutline = Lines.DrawLine($"PlayspaceCornerSupport{i}", Color.green, Color.green, .005f, floor, ceiling);
                cornerSupportsOutline.material = perimeterOutlineMaterial;
            }

            //draw ceiling and floor perimeter:
            LineRenderer ceilingOutline = Lines.DrawLine($"PlayspaceCeilingOutline", Color.green, Color.green, .005f, playspacePerimeterOnCeiling);
            LineRenderer floorOutline   = Lines.DrawLine($"PlayspaceFloorOutline", Color.green, Color.green, .005f, playspacePerimeterOnFloor);

            ceilingOutline.material = perimeterOutlineMaterial;
            floorOutline.material   = perimeterOutlineMaterial;
        }
예제 #2
0
        //Loops:
        private void Update()
        {
            //wait for hand input to come online:
            if (!HandInput.Ready)
            {
                return;
            }

            //shoulder:
            float shoulderDistance = shoulderWidth * .5f;

            //swap for the left shoulder:
            if (handedness == MLHandTracking.HandType.Left)
            {
                shoulderDistance *= -1;
            }

            //source locations:
            Vector3 flatForward   = Vector3.ProjectOnPlane(_mainCamera.forward, Vector3.up);
            Vector3 shoulder      = TransformUtilities.WorldPosition(_mainCamera.position, Quaternion.LookRotation(flatForward), new Vector2(shoulderDistance, Mathf.Abs(shoulderDistanceBelowHead) * -1));
            Vector3 pointerOrigin = Vector3.Lerp(Hand.Skeleton.Thumb.Knuckle.positionFiltered, Hand.Skeleton.Position, .5f);

            //direction:
            Quaternion orientation = Quaternion.LookRotation(Vector3.Normalize(pointerOrigin - shoulder), Hand.Skeleton.Rotation * Vector3.up);

            //application:
            transform.position = pointerOrigin;
            transform.rotation = orientation;
        }
예제 #3
0
        private void FindWalls_GenerateGeometry()
        {
            //transform matrix:
            Vector3    correctedForward  = Vector3.ProjectOnPlane(pcfAnchor.Rotation * Vector3.forward, Vector3.up).normalized;
            Quaternion correctedRotation = Quaternion.LookRotation(correctedForward);
            Matrix4x4  transformMatrix   = Matrix4x4.TRS(pcfAnchor.Position, correctedRotation, Vector3.one);

            //build wall geometry:
            Vector3        verticalOffset = Vector3.up * Height;
            List <Vector3> wallVerticies  = new List <Vector3>();
            List <int>     wallTriangles  = new List <int>();

            for (int i = 0; i < _playspaceCorners.Count - 1; i++)
            {
                Vector3 pointA = TransformUtilities.WorldPosition(pcfAnchor.Position, pcfAnchor.Rotation, _playspaceCorners[i]);
                Vector3 pointB = TransformUtilities.WorldPosition(pcfAnchor.Position, pcfAnchor.Rotation, _playspaceCorners[i + 1]);

                //locate verticies (local space):
                Vector3 a = transformMatrix.inverse.MultiplyPoint3x4(pointA);
                Vector3 b = transformMatrix.inverse.MultiplyPoint3x4(pointB);
                Vector3 c = b + verticalOffset;
                Vector3 d = a + verticalOffset;

                //store verticies (reverse order so normals face inwards):
                wallVerticies.Add(d);
                wallVerticies.Add(c);
                wallVerticies.Add(b);
                wallVerticies.Add(a);

                //store triangles
                wallTriangles.Add(wallVerticies.Count - 4);
                wallTriangles.Add(wallVerticies.Count - 3);
                wallTriangles.Add(wallVerticies.Count - 2);
                wallTriangles.Add(wallVerticies.Count - 2);
                wallTriangles.Add(wallVerticies.Count - 1);
                wallTriangles.Add(wallVerticies.Count - 4);
            }

            //get points for polygon construction:
            Vector2[] polygonPoints    = new Vector2[_playspaceCorners.Count - 1];
            Vector3[] floorVerticies   = new Vector3[_playspaceCorners.Count - 1];
            Vector3[] ceilingVerticies = new Vector3[_playspaceCorners.Count - 1];
            for (int i = 0; i < _playspaceCorners.Count - 1; i++)
            {
                Vector3 point = TransformUtilities.WorldPosition(pcfAnchor.Position, pcfAnchor.Rotation, _playspaceCorners[i]);
                Vector3 local = transformMatrix.inverse.MultiplyPoint3x4(point);
                polygonPoints[i]    = new Vector2(local.x, local.z);
                floorVerticies[i]   = local;
                ceilingVerticies[i] = local + verticalOffset;
            }

            //triangles:
            int[] floorTriangles = new Triangulator(polygonPoints).Triangulate();

            BuildGeometry(wallVerticies.ToArray(), wallTriangles.ToArray(), floorVerticies, floorTriangles, ceilingVerticies);

            //FindWalls_Analyze();
            ChangeState(State.ConfirmArea);
        }
        /// <summary>
        /// Updates peers on the position, scale, and rotation of this transmission object.
        /// </summary>
        public void Synchronize()
        {
            if (_isMine)
            {
                //update relative:
                localPosition  = TransformUtilities.LocalPosition(Transmission.Instance.sharedOrigin.position, Transmission.Instance.sharedOrigin.rotation, transform.position);
                rotationOffset = TransformUtilities.GetRotationOffset(Transmission.Instance.sharedOrigin.rotation, transform.rotation);

                //send out the change to this transform:
                Transmission.Send(new TransformSyncMessage(this));
            }
        }
        private void HandleTransformSync(TransformSyncMessage transformSyncMessage)
        {
            //is this a pose message for us?
            if (transformSyncMessage.ig != guid)
            {
                return;
            }

            //unpack:
            Vector3    receivedLocalPosition  = new Vector3((float)transformSyncMessage.px, (float)transformSyncMessage.py, (float)transformSyncMessage.pz);
            Quaternion receivedRotationOffset = new Quaternion((float)transformSyncMessage.rx, (float)transformSyncMessage.ry, (float)transformSyncMessage.rz, (float)transformSyncMessage.rw);

            _targetPosition = TransformUtilities.WorldPosition(Transmission.Instance.sharedOrigin.position, Transmission.Instance.sharedOrigin.rotation, receivedLocalPosition);
            _targetRotation = TransformUtilities.ApplyRotationOffset(Transmission.Instance.sharedOrigin.rotation, receivedRotationOffset);
            targetScale     = new Vector3((float)transformSyncMessage.sx, (float)transformSyncMessage.sy, (float)transformSyncMessage.sz);
        }
        //Init:
        private void Awake()
        {
            //sets:
            localPosition  = TransformUtilities.LocalPosition(Transmission.Instance.sharedOrigin.position, Transmission.Instance.sharedOrigin.rotation, transform.position);
            rotationOffset = TransformUtilities.GetRotationOffset(Transmission.Instance.sharedOrigin.rotation, transform.rotation);
            targetScale    = transform.localScale;

            //catalog:
            _all.Add(guid, this);

            //hooks:
            Transmission.Instance.OnPeerFound.AddListener(HandlePeerFound);
            Transmission.Instance.OnTransformSync.AddListener(HandleTransformSync);
            Transmission.Instance.OnOwnershipGained.AddListener(HandleOwnershipGained);
            Transmission.Instance.OnOwnershipTransferDenied.AddListener(HandleOwnershipTransferDenied);

            StartCoroutine("ShareTransformStatus");
        }
        public void Update(ManagedHand managedHand, Vector3 keyPointLocation, params Vector3[] decayPoints)
        {
            if (!managedHand.Visible)
            {
                //lost:
                if (Visible)
                {
                    FireLostEvent();
                    _progress.locationHistory.Clear();
                }

                return;
            }

            //visibility status:
            bool currentVisibility = true;

            //too close to next joint in chain means visibility failed:
            if (Vector3.Distance(keyPointLocation, _mainCamera.transform.position) < _minHeadDistance)
            {
                currentVisibility = false;
            }
            else
            {
                for (int i = 0; i < decayPoints.Length; i++)
                {
                    if (Vector3.Distance(keyPointLocation, decayPoints[i]) < _lostKeyPointDistance)
                    {
                        currentVisibility = false;
                        break;
                    }
                }
            }

            positionRaw = keyPointLocation;

            //lost visibility:
            if (!currentVisibility && Visible)
            {
                FireLostEvent();
                _progress.locationHistory.Clear();
                return;
            }

            //history cache:
            _progress.locationHistory.Add(keyPointLocation);

            //only need 3 in our history:
            if (_progress.locationHistory.Count > 3)
            {
                _progress.locationHistory.RemoveAt(0);
            }

            //we have enough history:
            if (_progress.locationHistory.Count == 3)
            {
                //movement intent stats:
                Vector3 vectorA = _progress.locationHistory[_progress.locationHistory.Count - 2] - _progress.locationHistory[_progress.locationHistory.Count - 3];
                Vector3 vectorB = _progress.locationHistory[_progress.locationHistory.Count - 1] - _progress.locationHistory[_progress.locationHistory.Count - 2];
                float   delta   = Vector3.Distance(_progress.locationHistory[_progress.locationHistory.Count - 3], _progress.locationHistory[_progress.locationHistory.Count - 1]);
                float   angle   = Vector3.Angle(vectorA, vectorB);
                Stability = 1 - Mathf.Clamp01(delta / _maxDistance);

                //moving in a constant direction?
                if (angle < 90)
                {
                    _progress.target = _progress.locationHistory[_progress.locationHistory.Count - 1];
                }

                //snap or smooth:
                if (Stability == 0)
                {
                    positionFiltered = _progress.target;
                }
                else
                {
                    positionFiltered = Vector3.SmoothDamp(positionFiltered, _progress.target, ref _progress.velocity, _smoothTime * Stability);
                }
            }
            else
            {
                positionFiltered = keyPointLocation;
            }

            //inside the camera plane - flatten against the plane?
            InsideClipPlane = TransformUtilities.InsideClipPlane(positionFiltered);
            if (InsideClipPlane)
            {
                positionFiltered = TransformUtilities.LocationOnClipPlane(positionFiltered);
            }

            //gained visibility:
            if (currentVisibility && !Visible)
            {
                //we must also break distance for point proximity:
                for (int i = 0; i < decayPoints.Length; i++)
                {
                    if (Vector3.Distance(keyPointLocation, decayPoints[i]) < _foundKeyPointDistance)
                    {
                        currentVisibility = false;
                        break;
                    }
                }

                //still good?
                if (currentVisibility)
                {
                    FireFoundEvent();
                }
            }
        }
        //Public Methods:
        public void Update()
        {
            if (!_managedHand.Visible)
            {
                return;
            }

            //pinch rotation offset mirror:
            Vector3 rotationOffset = _pinchAbsoluteRotationOffset;

            if (_managedHand.Hand.Type == MLHandTracking.HandType.Left)
            {
                rotationOffset.y *= -1;
            }

            //holders:
            Vector3    pinchPosition = Vector3.zero;
            Quaternion pinchRotation = Quaternion.identity;

            //pinch interaction point radius:
            if (_managedHand.Skeleton.Thumb.Tip.Visible && _managedHand.Skeleton.Index.Tip.Visible)
            {
                Pinch.radius = Vector3.Distance(_managedHand.Skeleton.Thumb.Tip.positionFiltered, _managedHand.Skeleton.Index.Tip.positionFiltered);
            }

            if (_managedHand.Skeleton.Thumb.Tip.Visible) //absolute placement:
            {
                //are we swapping modes?
                if (_pinchIsRelative)
                {
                    _pinchIsRelative          = false;
                    _pinchTransitioning       = true;
                    _pinchTransitionStartTime = Time.realtimeSinceStartup;
                }

                pinchPosition = _managedHand.Skeleton.Thumb.Tip.positionFiltered;
                pinchRotation = TransformUtilities.RotateQuaternion(_managedHand.Skeleton.Rotation, rotationOffset);

                //gather offset distance:
                if (_managedHand.Skeleton.Index.Knuckle.Visible && _managedHand.Skeleton.Thumb.Knuckle.Visible)
                {
                    Vector3 mcpMidpoint = Vector3.Lerp(_managedHand.Skeleton.Index.Knuckle.positionFiltered, _managedHand.Skeleton.Thumb.Knuckle.positionFiltered, .5f);
                    _pinchRelativePositionDistance = Vector3.Distance(mcpMidpoint, pinchPosition);
                }
            }
            else //relative placement:
            {
                //are we swapping modes?
                if (!_pinchIsRelative)
                {
                    _pinchIsRelative          = true;
                    _pinchTransitioning       = true;
                    _pinchTransitionStartTime = Time.realtimeSinceStartup;
                }

                //place between available mcps:
                if (_managedHand.Skeleton.Index.Knuckle.Visible && _managedHand.Skeleton.Thumb.Knuckle.Visible)
                {
                    pinchPosition = Vector3.Lerp(_managedHand.Skeleton.Index.Knuckle.positionFiltered, _managedHand.Skeleton.Thumb.Knuckle.positionFiltered, .5f);

                    //rotate:
                    pinchRotation = TransformUtilities.RotateQuaternion(_managedHand.Skeleton.Rotation, _pinchRelativeRotationOffset);

                    //move out along rotation forward:
                    pinchPosition += pinchRotation * Vector3.forward * _pinchRelativePositionDistance;
                }
                else
                {
                    //just use previous:
                    pinchPosition = Pinch.position;
                    pinchRotation = Pinch.rotation;
                }
            }

            //sticky release reduction:
            if (_collapsed)
            {
                if (_managedHand.Skeleton.Thumb.Tip.Visible && _managedHand.Skeleton.Index.Tip.Visible)
                {
                    //if starting to release, start using a point between the thumb and index tips:
                    if (Vector3.Distance(_managedHand.Skeleton.Thumb.Tip.positionFiltered, _managedHand.Skeleton.Index.Tip.positionFiltered) > _dynamicReleaseDistance)
                    {
                        pinchPosition = Vector3.Lerp(_managedHand.Skeleton.Thumb.Tip.positionFiltered, _managedHand.Skeleton.Index.Tip.positionFiltered, .3f);
                    }
                }
            }

            //apply pinch pose - to avoid jumps when relative placement is used we smooth until close enough:
            if (_pinchTransitioning)
            {
                //position:
                Pinch.position = Vector3.SmoothDamp(Pinch.position, pinchPosition, ref _pinchArrivalPositionVelocity, _pinchTransitionTime);
                float positionDelta = Vector3.Distance(Pinch.position, pinchPosition);

                //rotation:
                Pinch.rotation = MotionUtilities.SmoothDamp(Pinch.rotation, pinchRotation, ref _pinchArrivalRotationVelocity, _pinchTransitionTime);
                float rotationDelta = Quaternion.Angle(Pinch.rotation, pinchRotation);

                //close enough to hand off?
                if (positionDelta < .001f && rotationDelta < 5)
                {
                    _pinchTransitioning = false;
                }

                //taking too long?
                if (Time.realtimeSinceStartup - _pinchTransitionStartTime > _pinchTransitionMaxDuration)
                {
                    _pinchTransitioning = false;
                }
            }
            else
            {
                Pinch.position = pinchPosition;
                Pinch.rotation = pinchRotation;
            }

            //grasp interaction point:
            Bounds graspBounds = CalculateGraspBounds
                                 (
                _managedHand.Skeleton.Thumb.Knuckle,
                _managedHand.Skeleton.Thumb.Joint,
                _managedHand.Skeleton.Thumb.Tip,
                _managedHand.Skeleton.Index.Knuckle,
                _managedHand.Skeleton.Index.Joint,
                _managedHand.Skeleton.Index.Tip,
                _managedHand.Skeleton.Middle.Knuckle,
                _managedHand.Skeleton.Middle.Joint,
                _managedHand.Skeleton.Middle.Tip
                                 );

            Grasp.position = _managedHand.Skeleton.Position;
            //when points are being initially found they can be wildly off and this could cause a massively large volume:
            Grasp.radius   = Mathf.Min(graspBounds.size.magnitude, _maxGraspRadius);
            Grasp.rotation = _managedHand.Skeleton.Rotation;

            //intent updated:
            if (_currentInteractionState != null)
            {
                _currentInteractionState.FireUpdate();
            }

            //keypose change proposed:
            if (_managedHand.Hand.KeyPose != VerifiedGesture && _managedHand.Hand.KeyPose != _proposedKeyPose)
            {
                //queue a new proposed change to keypose:
                _proposedKeyPose    = _managedHand.Hand.KeyPose;
                _keyPoseChangedTime = Time.realtimeSinceStartup;
            }

            //keypose change acceptance:
            if (_managedHand.Hand.KeyPose != VerifiedGesture && Time.realtimeSinceStartup - _keyPoseChangedTime > _keyPoseStabailityDuration)
            {
                //reset:
                Point.active = false;
                Pinch.active = false;
                Grasp.active = false;

                if (_collapsed)
                {
                    //intent end:
                    if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.C || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.OpenHand || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.L || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Finger)
                    {
                        if (_managedHand.Skeleton.Thumb.Tip.Visible && _managedHand.Skeleton.Index.Tip.Visible)
                        {
                            //dynamic release:
                            if (Vector3.Distance(_managedHand.Skeleton.Thumb.Tip.positionFiltered, _managedHand.Skeleton.Index.Tip.positionFiltered) > _dynamicReleaseDistance)
                            {
                                //end intent:
                                _collapsed = false;
                                _currentInteractionState.FireEnd();
                                _currentInteractionState = null;

                                //accept keypose change:
                                VerifiedGesture  = _managedHand.Hand.KeyPose;
                                _proposedKeyPose = _managedHand.Hand.KeyPose;
                                OnVerifiedGestureChanged?.Invoke(_managedHand, VerifiedGesture);

                                if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Finger || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.L)
                                {
                                    Intent = IntentPose.Pointing;
                                    OnIntentChanged?.Invoke(_managedHand, Intent);
                                }
                                else if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.C || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.OpenHand || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Thumb)
                                {
                                    Intent = IntentPose.Relaxed;
                                    OnIntentChanged?.Invoke(_managedHand, Intent);
                                }
                            }
                        }
                    }
                }
                else
                {
                    //intent begin:
                    if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Pinch || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Ok || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Fist)
                    {
                        _collapsed = true;

                        if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Pinch || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Ok)
                        {
                            Intent                   = IntentPose.Pinching;
                            Pinch.active             = true;
                            _currentInteractionState = Pinch.Touch;
                            _currentInteractionState.FireBegin();
                            OnIntentChanged?.Invoke(_managedHand, Intent);
                        }
                        else if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Fist)
                        {
                            Intent                   = IntentPose.Grasping;
                            Grasp.active             = true;
                            _currentInteractionState = Grasp.Touch;
                            _currentInteractionState.FireBegin();
                            OnIntentChanged?.Invoke(_managedHand, Intent);
                        }
                    }

                    if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Finger || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.L)
                    {
                        Intent       = IntentPose.Pointing;
                        Point.active = true;
                        OnIntentChanged?.Invoke(_managedHand, Intent);
                    }
                    else if (_managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.C || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.OpenHand || _managedHand.Hand.KeyPose == MLHandTracking.HandKeyPose.Thumb)
                    {
                        Intent = IntentPose.Relaxed;
                        OnIntentChanged?.Invoke(_managedHand, Intent);
                    }

                    //accept keypose change:
                    VerifiedGesture  = _managedHand.Hand.KeyPose;
                    _proposedKeyPose = _managedHand.Hand.KeyPose;
                    OnVerifiedGestureChanged?.Invoke(_managedHand, VerifiedGesture);
                }
            }

            UpdateCurrentInteractionPoint();
            UpdatePalmEvents();
        }
예제 #9
0
        //Public Methods:
        public void Update()
        {
            //update keypoints and fingers:
            WristCenter.Update(_managedHand, _managedHand.Hand.Wrist.Center.Position, _managedHand.Hand.Center);
            HandCenter.Update(_managedHand, _managedHand.Hand.Center);
            _thumbMCP.Update(_managedHand, _managedHand.Hand.Thumb.MCP.Position, _managedHand.Hand.Index.MCP.Position, _managedHand.Hand.Center);
            _thumbPIP.Update(_managedHand, _managedHand.Hand.Thumb.IP.Position, _managedHand.Hand.Thumb.MCP.Position, _managedHand.Hand.Center);
            _thumbTip.Update(_managedHand, _managedHand.Hand.Thumb.Tip.Position, _managedHand.Hand.Thumb.IP.Position, _managedHand.Hand.Middle.Tip.Position, _managedHand.Hand.Thumb.MCP.Position, _managedHand.Hand.Center);
            Thumb.Update();
            _indexMCP.Update(_managedHand, _managedHand.Hand.Index.MCP.Position, _managedHand.Hand.Center);
            _indexPIP.Update(_managedHand, _managedHand.Hand.Index.PIP.Position, _managedHand.Hand.Index.MCP.Position, _managedHand.Hand.Center);
            _indexTip.Update(_managedHand, _managedHand.Hand.Index.Tip.Position, _managedHand.Hand.Index.PIP.Position, _managedHand.Hand.Index.MCP.Position, _managedHand.Hand.Center, _managedHand.Hand.Thumb.IP.Position, _managedHand.Hand.Middle.Tip.Position);
            Index.Update();
            _middleMCP.Update(_managedHand, _managedHand.Hand.Middle.MCP.Position, _managedHand.Hand.Center);
            _middlePIP.Update(_managedHand, _managedHand.Hand.Middle.PIP.Position, _managedHand.Hand.Middle.MCP.Position, _managedHand.Hand.Center);
            _middleTip.Update(_managedHand, _managedHand.Hand.Middle.Tip.Position, _managedHand.Hand.Middle.PIP.Position, _managedHand.Hand.Middle.MCP.Position, _managedHand.Hand.Ring.Tip.Position, _managedHand.Hand.Center);
            Middle.Update();
            _ringMCP.Update(_managedHand, _managedHand.Hand.Ring.MCP.Position, _managedHand.Hand.Center);
            _ringTip.Update(_managedHand, _managedHand.Hand.Ring.Tip.Position, _managedHand.Hand.Ring.MCP.Position, _managedHand.Hand.Pinky.Tip.Position, _managedHand.Hand.Middle.Tip.Position, _managedHand.Hand.Center);
            Ring.Update();
            _pinkyMCP.Update(_managedHand, _managedHand.Hand.Pinky.MCP.Position, _managedHand.Hand.Center);
            _pinkyTip.Update(_managedHand, _managedHand.Hand.Pinky.Tip.Position, _managedHand.Hand.Pinky.MCP.Position, _managedHand.Hand.Ring.Tip.Position, _managedHand.Hand.Center);
            Pinky.Update();

            //we need a hand to continue:
            if (!_managedHand.Visible)
            {
                return;
            }

            //correct distances:
            float thumbMcpToWristDistance = Vector3.Distance(_thumbMCP.positionFiltered, WristCenter.positionFiltered) * .5f;
            //fix the distance between the wrist and thumbMcp as it incorrectly expands as the hand gets further from the camera:
            float distancePercentage = Mathf.Clamp01(Vector3.Distance(_mainCamera.transform.position, WristCenter.positionFiltered) / .5f);

            distancePercentage       = 1 - Percentage(distancePercentage, .90f, 1) * .4f;
            thumbMcpToWristDistance *= distancePercentage;
            Vector3 wristToPalmDirection = Vector3.Normalize(Vector3.Normalize(HandCenter.positionFiltered - WristCenter.positionFiltered));
            Vector3 center = WristCenter.positionFiltered + (wristToPalmDirection * thumbMcpToWristDistance);
            Vector3 camToWristDirection = Vector3.Normalize(WristCenter.positionFiltered - _mainCamera.transform.position);

            //rays needed for planarity discovery for in/out palm facing direction:
            Vector3 camToWrist    = new Ray(WristCenter.positionFiltered, camToWristDirection).GetPoint(1);
            Vector3 camToThumbMcp = new Ray(_thumbMCP.positionFiltered, Vector3.Normalize(_thumbMCP.positionFiltered - _mainCamera.transform.position)).GetPoint(1);
            Vector3 camToPalm     = new Ray(center, Vector3.Normalize(center - _mainCamera.transform.position)).GetPoint(1);

            //discover palm facing direction to camera:
            Plane palmFacingPlane = new Plane(camToWrist, camToPalm, camToThumbMcp);

            if (_managedHand.Hand.HandType == MLHandType.Left)
            {
                palmFacingPlane.Flip();
            }
            float palmForwardFacing = Mathf.Sign(Vector3.Dot(palmFacingPlane.normal, _mainCamera.transform.forward));

            //use thumb/palm/wrist alignment to determine amount of roll in the hand:
            Vector3 toThumbMcp     = Vector3.Normalize(_thumbMCP.positionFiltered - center);
            Vector3 toPalm         = Vector3.Normalize(center - WristCenter.positionFiltered);
            float   handRollAmount = (1 - Vector3.Dot(toThumbMcp, toPalm)) * palmForwardFacing;

            //where between the wrist and thumbMcp should we slide inwards to get the palm in the center:
            Vector3 toPalmOrigin = Vector3.Lerp(WristCenter.positionFiltered, _thumbMCP.positionFiltered, .35f);

            //get a direction from the camera to toPalmOrigin as psuedo up for use in quaternion construction:
            Vector3 toCam = Vector3.Normalize(_mainCamera.transform.position - toPalmOrigin);

            //construct a quaternion that helps get angles needed between the wrist and thumbMCP to point towards the palm center:
            Vector3    wristToThumbMcp       = Vector3.Normalize(_thumbMCP.positionFiltered - WristCenter.positionFiltered);
            Quaternion towardsCamUpReference = Quaternion.identity;

            if (wristToThumbMcp != Vector3.zero && toCam != Vector3.zero)
            {
                towardsCamUpReference = Quaternion.LookRotation(wristToThumbMcp, toCam);
            }

            //rotate the inwards vector depending on hand roll to know where to push the palm back:
            float inwardsVectorRotation = 90;

            if (_managedHand.Hand.HandType == MLHandType.Left)
            {
                inwardsVectorRotation = -90;
            }
            towardsCamUpReference = Quaternion.AngleAxis(handRollAmount * inwardsVectorRotation, towardsCamUpReference * Vector3.forward) * towardsCamUpReference;
            Vector3 inwardsVector = towardsCamUpReference * Vector3.up;

            //slide palm location along inwards vector to get it into proper physical location in the center of the hand:
            center = toPalmOrigin - inwardsVector * thumbMcpToWristDistance;
            Vector3 deadCenter = center;

            //as the hand flattens back out balance corrected location with originally provided location for better forward origin:
            center = Vector3.Lerp(center, HandCenter.positionFiltered, Mathf.Abs(handRollAmount));

            //get a forward using the corrected palm location:
            Vector3 forward = Vector3.Normalize(center - WristCenter.positionFiltered);

            //switch back to physical center of hand - this reduces surface-to-surface movement of the center between back and palm:
            center = deadCenter;

            //get an initial hand up:
            Plane handPlane = new Plane(WristCenter.positionFiltered, _thumbMCP.positionFiltered, center);

            if (_managedHand.Hand.HandType == MLHandType.Left)
            {
                handPlane.Flip();
            }
            Vector3 up = handPlane.normal;

            //find out how much the back of the hand is facing the camera so we have a safe set of features for a stronger forward:
            Vector3 centerToCam = Vector3.Normalize(_mainCamera.transform.position - WristCenter.positionFiltered);
            float   facingDot   = Vector3.Dot(centerToCam, up);

            if (facingDot > .5f)
            {
                float handBackFacingCamAmount = Percentage(facingDot, .5f, 1);

                //steer forward for more accuracy based on the visibility of the back of the hand:
                if (_middleMCP.Visible)
                {
                    Vector3 toMiddleMcp = Vector3.Normalize(_middleMCP.positionFiltered - center);
                    forward = Vector3.Lerp(forward, toMiddleMcp, handBackFacingCamAmount);
                }
                else if (_indexMCP.Visible)
                {
                    Vector3 inIndexMcp = Vector3.Normalize(_indexMCP.positionFiltered - center);
                    forward = Vector3.Lerp(forward, inIndexMcp, handBackFacingCamAmount);
                }
            }

            //make sure palm distance from wrist is consistant while also leveraging steered forward:
            center = WristCenter.positionFiltered + (forward * thumbMcpToWristDistance);

            //an initial rotation of the hand:
            Quaternion orientation = Quaternion.identity;

            if (forward != Vector3.zero && up != Vector3.zero)
            {
                orientation = Quaternion.LookRotation(forward, up);
            }

            //as the hand rolls counter-clockwise the thumbMcp loses accuracy so we need to interpolate to the back of the hand's features:
            if (_indexMCP.Visible && _middleMCP.Visible)
            {
                Vector3 knucklesVector = Vector3.Normalize(_middleMCP.positionFiltered - _indexMCP.positionFiltered);
                float   knucklesDot    = Vector3.Dot(knucklesVector, Vector3.up);
                if (knucklesDot > .5f)
                {
                    float counterClockwiseRoll = Percentage(Vector3.Dot(knucklesVector, Vector3.up), .35f, .7f);
                    center  = Vector3.Lerp(center, HandCenter.positionFiltered, counterClockwiseRoll);
                    forward = Vector3.Lerp(forward, Vector3.Normalize(_middleMCP.positionFiltered - HandCenter.positionFiltered), counterClockwiseRoll);
                    Plane backHandPlane = new Plane(HandCenter.positionFiltered, _indexMCP.positionFiltered, _middleMCP.positionFiltered);
                    if (_managedHand.Hand.HandType == MLHandType.Left)
                    {
                        backHandPlane.Flip();
                    }
                    up          = Vector3.Lerp(up, backHandPlane.normal, counterClockwiseRoll);
                    orientation = Quaternion.LookRotation(forward, up);
                }
            }

            //as the wrist tilts away from the camera (with the thumb down) at extreme angles the hand center will move toward the thumb:
            float   handTiltAwayAmount          = 1 - Percentage(Vector3.Distance(HandCenter.positionFiltered, WristCenter.positionFiltered), .025f, .04f);
            Vector3 handTiltAwayCorrectionPoint = WristCenter.positionFiltered + camToWristDirection * thumbMcpToWristDistance;

            center  = Vector3.Lerp(center, handTiltAwayCorrectionPoint, handTiltAwayAmount);
            forward = Vector3.Lerp(forward, Vector3.Normalize(handTiltAwayCorrectionPoint - WristCenter.positionFiltered), handTiltAwayAmount);
            Plane wristPlane = new Plane(WristCenter.positionFiltered, _thumbMCP.positionFiltered, center);

            if (_managedHand.Hand.HandType == MLHandType.Left)
            {
                wristPlane.Flip();
            }
            up = Vector3.Lerp(up, wristPlane.normal, handTiltAwayAmount);
            if (forward != Vector3.zero && up != Vector3.zero)
            {
                orientation = Quaternion.LookRotation(forward, up);
            }

            //steering for if thumb/index are not available from self-occlusion to help rotate the hand better outwards better:
            float forwardUpAmount = Vector3.Dot(forward, Vector3.up);

            if (forwardUpAmount > .7f && _indexMCP.Visible && _ringMCP.Visible)
            {
                float angle = 0;
                if (_managedHand.Hand.HandType == MLHandType.Right)
                {
                    Vector3 knucklesVector = Vector3.Normalize(_ringMCP.positionFiltered - _indexMCP.positionFiltered);
                    angle  = Vector3.Angle(knucklesVector, orientation * Vector3.right);
                    angle *= -1;
                }
                else
                {
                    Vector3 knucklesVector = Vector3.Normalize(_indexMCP.positionFiltered - _ringMCP.positionFiltered);
                    angle = Vector3.Angle(knucklesVector, orientation * Vector3.right);
                }
                Quaternion selfOcclusionSteering = Quaternion.AngleAxis(angle, forward);
                orientation = selfOcclusionSteering * orientation;
            }
            else
            {
                //when palm is facing down we need to rotate some to compensate for an offset:
                float rollCorrection       = Mathf.Clamp01(Vector3.Dot(orientation * Vector3.up, Vector3.up));
                float rollCorrectionAmount = -30;
                if (_managedHand.Hand.HandType == MLHandType.Left)
                {
                    rollCorrectionAmount = 30;
                }
                orientation = Quaternion.AngleAxis(rollCorrectionAmount * rollCorrection, forward) * orientation;
            }

            //inside the camera plane:
            InsideClipPlane = TransformUtilities.InsideClipPlane(center);

            //set pose:
            Position = center;
            Rotation = orientation;
        }
예제 #10
0
        private void FindWalls_LocateBoundry()
        {
            //remove initial guides:
            RemovePlottedBounds();

            //sort by angles from a base vector to find clockwise order:
            Vector3 baseVector = Vector3.Normalize(_virtualWalls[0].plane.Center - _plottedBounds.center);
            SortedDictionary <float, int> sortedDirection = new SortedDictionary <float, int>();

            for (int i = 0; i < _virtualWalls.Count; i++)
            {
                Vector3 toNext = Vector3.Normalize(_virtualWalls[i].plane.Center - _plottedBounds.center);
                float   angle  = Vector3.SignedAngle(baseVector, toNext, Vector3.up) + 180;

                if (sortedDirection.ContainsKey(angle))
                {
                    //we have a bad set of locations so it is best to just everything:
                    Create();
                    break;
                }
                else
                {
                    sortedDirection.Add(angle, i);
                }
            }
            int[] clockwiseOrder = sortedDirection.Values.ToArray <int>();

            //find and connect 'betweens' which end up being final walls of playspace
            List <bool> physicalStatus = new List <bool>();

            _playspaceCorners = new List <Vector3>();
            for (int i = 0; i < clockwiseOrder.Length; i++)
            {
                //parts:
                int   next  = (i + 1) % clockwiseOrder.Length;
                float angle = Vector3.Angle(_virtualWalls[clockwiseOrder[i]].plane.Rotation * Vector3.right, _virtualWalls[clockwiseOrder[next]].plane.Rotation * Vector3.right);

                //save physical status:
                physicalStatus.Add(_virtualWalls[clockwiseOrder[next]].physical);

                //add solved between:
                if (angle < 45 || angle > 135)
                {
                    //wall - use mid point:
                    Vector3 mid = Vector3.Lerp(_virtualWalls[clockwiseOrder[i]].plane.Center, _virtualWalls[clockwiseOrder[next]].plane.Center, .5f);
                    mid.y = _roomFloorHeight;
                    _playspaceCorners.Add(TransformUtilities.LocalPosition(pcfAnchor.Position, pcfAnchor.Rotation, mid));
                }
                else
                {
                    //corner - use intersection by creating inverted lines from each plane:
                    Vector3 point = Vector2.zero;
                    if (MathUtilities.RayIntersection(
                            new Ray(_virtualWalls[clockwiseOrder[i]].plane.Center, _virtualWalls[clockwiseOrder[i]].plane.Rotation * Vector3.right),
                            new Ray(_virtualWalls[clockwiseOrder[next]].plane.Center, _virtualWalls[clockwiseOrder[next]].plane.Rotation * Vector3.left),
                            ref point))
                    {
                        point.y = _roomFloorHeight;
                        _playspaceCorners.Add(TransformUtilities.LocalPosition(pcfAnchor.Position, pcfAnchor.Rotation, point));
                    }
                }
            }

            //close loop:
            _playspaceCorners.Add(_playspaceCorners[0]);

            //store walls:
            List <PlayspaceWall> playspaceWalls = new List <PlayspaceWall>();

            for (int i = 0; i < _playspaceCorners.Count - 1; i++)
            {
                Vector3 pointA = TransformUtilities.WorldPosition(pcfAnchor.Position, pcfAnchor.Rotation, _playspaceCorners[i]);
                Vector3 pointB = TransformUtilities.WorldPosition(pcfAnchor.Position, pcfAnchor.Rotation, _playspaceCorners[i + 1]);
                Vector3 vector = pointB - pointA;
                Vector3 normal = Quaternion.AngleAxis(90, Vector3.up) * vector.normalized;
                Vector3 center = Vector3.Lerp(pointA, pointB, .5f);
                center.y = _roomVerticalCenter;
                float         width = vector.magnitude;
                PlayspaceWall wall  = new PlayspaceWall(center, Quaternion.LookRotation(normal), width, Height, physicalStatus[i]);
                playspaceWalls.Add(wall);
            }
            Walls = playspaceWalls.ToArray();

            FindWalls_GenerateGeometry();
        }
예제 #11
0
        private void ConfirmPrimaryWall_HandleTriggerDown(byte controllerId, float triggerValue)
        {
            //we must have a primary wall:
            if (PrimaryWall == -1)
            {
                return;
            }

            //reset walls:
            RightWall = -1;
            LeftWall  = -1;
            RearWall  = -1;

            //find room center (this may need additional calculation for more accuracy):
            Bounds finalBounds = new Bounds(Walls[0].Center, Vector3.zero);

            for (int i = 1; i < Walls.Length; i++)
            {
                finalBounds.Encapsulate(Walls[i].Center);
            }

            //make center relative to pcf so relocalization keeps this value accurate:
            _playspaceCenter = TransformUtilities.LocalPosition(pcfAnchor.Position, pcfAnchor.Rotation, finalBounds.center);

            //find additional largest key walls:
            SortedDictionary <float, int> rightWalls = new SortedDictionary <float, int>();
            SortedDictionary <float, int> rearWalls  = new SortedDictionary <float, int>();
            SortedDictionary <float, int> leftWalls  = new SortedDictionary <float, int>();

            for (int i = 0; i < Walls.Length; i++)
            {
                //skip walls that match the primary:
                if (i != PrimaryWall) //skip primary wall and walls on the same plane
                {
                    //get angle relationship:
                    float angle = Vector3.SignedAngle(Walls[PrimaryWall].Normal, Walls[i].Normal, Vector3.up);
                    if (angle < 0)
                    {
                        angle += 360;
                    }

                    //get size:
                    float wallSize = Walls[i].width * Walls[i].height;

                    //right:
                    if (angle >= 225 && angle < 315)
                    {
                        leftWalls.Add(wallSize, i);
                    }

                    //rear:
                    if (angle > 135 && angle < 225)
                    {
                        rearWalls.Add(wallSize, i);
                    }

                    //left:
                    if (angle >= 45 && angle <= 135)
                    {
                        rightWalls.Add(wallSize, i);
                    }
                }
            }

            //set key walls to largest (might need to verify they are also facing the correct direction as well later):
            if (rightWalls.Count != 0)
            {
                RightWall = rightWalls.ElementAt(rightWalls.Count - 1).Value;
            }

            if (rearWalls.Count != 0)
            {
                RearWall = rearWalls.ElementAt(rearWalls.Count - 1).Value;
            }

            if (leftWalls.Count != 0)
            {
                LeftWall = leftWalls.ElementAt(leftWalls.Count - 1).Value;
            }

            //unhook:
            MLInput.OnTriggerDown -= ConfirmPrimaryWall_HandleTriggerDown;

            //something went wrong with the shape of the room, best to force a rest:
            if (PrimaryWall == -1 || LeftWall == -1 || RightWall == -1 || RearWall == -1)
            {
                Create();
            }
            else
            {
                ChangeState(State.Complete);
            }
        }