Beispiel #1
0
        public void Receive(Pose data)
        {
            if (buffer.Capacity == 0)
            {
                OnSend(data);
            }

            bool bufferWasNotFull = false;

            if (!buffer.IsFull)
            {
                bufferWasNotFull = true;
            }

            buffer.Add(data);

            if (buffer.IsFull)
            {
                if (bufferWasNotFull)
                {
                    for (int i = 1; i < buffer.Count; i += 2)
                    {
                        OnSend(getAverage(0, i));
                    }
                }

                OnSend(getAverage(0, buffer.Count));
            }
        }
Beispiel #2
0
        public void Receive(Pose data)
        {
            bool wasNotFull = false;

            if (!_poseBuffer.IsFull)
            {
                wasNotFull = true;
            }

            if (_poseBuffer.Count == 0)
            {
                _firstPose = data;
            }
            if (_poseBuffer.Count == 1)
            {
                _secondPose = data;
            }
            if (_poseBuffer.Count == 2)
            {
                _thirdPose = data;
            }
            _poseBuffer.Add(data);

            if (_poseBuffer.IsFull)
            {
                if (wasNotFull && !loop)
                {
                    send(_poseBuffer.Get(0), _poseBuffer.Get(0),
                         _poseBuffer.Get(1), _poseBuffer.Get(2));
                }
                send(_poseBuffer.Get(0), _poseBuffer.Get(1),
                     _poseBuffer.Get(2), _poseBuffer.Get(3));
            }
        }
Beispiel #3
0
        public void Add(SampleType sample, float sampleTime)
        {
            if (!IsEmpty && sampleTime == GetLatestTime())
            {
                SetLatest(sample, sampleTime);
                return;
            }

            _buffer.Add(new ValueTimePair {
                value = sample, time = sampleTime
            });
        }
        public void Receive(Pose data)
        {
            bool wasNotFull = false;

            if (!_poseBuffer.IsFull)
            {
                wasNotFull = true;
            }

            _poseBuffer.Add(data);

            if (_poseBuffer.IsFull)
            {
                if (wasNotFull)
                {
                    send(_poseBuffer.Get(0), _poseBuffer.Get(0),
                         _poseBuffer.Get(1), _poseBuffer.Get(2));
                }
                send(_poseBuffer.Get(0), _poseBuffer.Get(1),
                     _poseBuffer.Get(2), _poseBuffer.Get(3));
            }
        }
        private void Update()
        {
            // Calculate information about the current angle of the hinge element relative to
            // the hinge resting direction.
            _elementAngle = Vector3.SignedAngle(hingeRestAngleDir,
                                                this.transform.rotation * _elementLocalExtensionAxis,
                                                hingeAxis);

            // Apply resting forces to the elementAngle.
            float?restingAngleCorrection = null;
            int   restwardPolarity       = -_elementAngle > 0 ? 1 : -1;

            if (_elementAngle != 0)
            {
                // Rotate the element towards its resting angle.
                var restForce = _elementAngle * -1f * restSpringForce;

                _restCorrectionVelocity += restForce * Time.deltaTime;

                var friction
                    = -_restCorrectionVelocity * restSpringFriction;
                _restCorrectionVelocity += friction * Time.deltaTime;

                var dragSign = _restCorrectionVelocity > 0 ? -1 : 1;
                var drag     = dragSign * _restCorrectionVelocity.Squared() * restSpringDrag;
                _restCorrectionVelocity += drag * Time.deltaTime;

                if (_restCorrectionVelocity < 0.2f)
                {
                    restwardPolarity = 0;
                }

                restingAngleCorrection = _restCorrectionVelocity * Time.deltaTime;

                _elementAngle += restingAngleCorrection.Value;
            }
            else
            {
                restwardPolarity = 0;
            }

            // Update interactors.
            LocalSegment3?interactorMotion = null;
            {
                if (_interactorPos.HasValue && _lastInteractorPos.HasValue)
                {
                    _lastInteractorPos = Vector3.Lerp(_lastInteractorPos.Value,
                                                      _interactorPos.Value,
                                                      20f * Time.deltaTime);
                }
                else
                {
                    _lastInteractorPos = _interactorPos;
                }

                if (restingAngleCorrection.HasValue && _interactorPos.HasValue)
                {
                    // Shift the last interactor position by the translation of it produced by
                    // the resting angle correct. This is akin to computing interactor sweeps in
                    // hinge-element-local space, where the correction of the hinge element to rest
                    // accounts for a sweep of the interactor position across its motion.
                    var rotatedPos = _lastInteractorPos.Value
                                     .RotatedAround(hingeTransform.position,
                                                    ((float)restingAngleCorrection.Value) * 0.05f,
                                                    hingeAxis);
                    var toRotated = rotatedPos - _lastInteractorPos.Value;
                    toRotated          = toRotated.RotatedBy(Quaternion.AngleAxis(-30f, hingeAxis));
                    _lastInteractorPos = _lastInteractorPos + toRotated * 20f;
                }

                _interactorPos = null;
                if (intObj.isPrimaryHovered)
                {
                    _interactorPos = intObj.primaryHoveringControllerPoint;
                }
                if (_lastInteractorPos.HasValue && _interactorPos.HasValue)
                {
                    var lastToCurrentDir
                        = (_interactorPos.Value - _lastInteractorPos.Value).normalized;

                    interactorMotion
                        = new LocalSegment3(
                              _lastInteractorPos.Value - lastToCurrentDir * _interactorRadius * 0.01f,
                              _interactorPos.Value);
                }

                // Update debug data.
                _interactorMotionsBuffer.Add(interactorMotion);
            }

            // Update interactor collision.
            var isSweptInteractorColliding = false;
            var isCurrInteractorColliding  = false;
            var currInteractor             = (Sphere?)null;

            {
                if (interactorMotion.HasValue)
                {
                    var segment       = interactorMotion.Value;
                    var motionSqrDist = segment.Intersect(buttonSurface);

                    if (motionSqrDist < _interactorRadius * _interactorRadius)
                    {
                        isSweptInteractorColliding = true;
                    }
                }
                if (_interactorPos.HasValue)
                {
                    currInteractor            = new Sphere(_interactorPos.Value, _interactorRadius);
                    isCurrInteractorColliding = currInteractor.Value.Overlaps(buttonSurface);
                }
                _wasMotionIntersectingBuffer.Add(isSweptInteractorColliding);
            }

            // Apply depenetration forces to the elementAngle.
            if (currInteractor.HasValue)
            {
                var interactor = new Sphere(_interactorPos.Value, _interactorRadius);

                // Determine whether or not we should attempt to hinge the button to depenetrate
                // from the interacting sphere geometry.
                bool shouldDepenetrate = false;
                if (isCurrInteractorColliding || isSweptInteractorColliding)
                {
                    shouldDepenetrate = true;
                }

                // Determine which way the element should depenetrate itself from the interacting
                // sphere -- here, we simply depenetrate to the closest side.
                var latestPos_button = this.transform.worldToLocalMatrix
                                       .MultiplyPoint3x4(_interactorPos.Value);
                var depenetrationPolarity
                    = Vector3.Dot(latestPos_button, _elementLocalActivationAxis) > 0 ? 1 : -1;
                // However, if the button velocity is large enough, we actually should use the
                // sign of the curl from its velocity vector about the hinge axis.
                if (_lastInteractorPos.HasValue)
                {
                    var angleToLastPos = Vector3.SignedAngle(
                        _lastInteractorPos.Value - _hingeTransform.position,
                        _hingeTransform.rotation
                        * Quaternion.AngleAxis((float)_elementAngle, _hingeLocalAxis)
                        * _elementLocalExtensionAxis,
                        hingeAxis);
                    depenetrationPolarity = angleToLastPos > 0 ? 1 : -1;

                    var velocity = (_lastInteractorPos.Value - _interactorPos.Value).magnitude
                                   / Time.deltaTime;

                    if (depenetrationPolarity == restwardPolarity && velocity < 1f)
                    {
                        shouldDepenetrate = false;
                    }
                }

                // TODO: Uncomment this and test with an applicable hinge element (with the
                // element extending over the hinge, notice how there is another sign problem,
                // where the facing of the element also flips).
                //// If the hinging element surface extends onto the other side of the hinge,
                //// the correct depenetration direction gets flipped when it is being pushed from
                //// that side.
                //if (Vector3.Dot(latestPos_button, _elementLocalExtensionAxis) < 0) {
                //  _depenetrationPolarity = -_depenetrationPolarity;
                //}

                // If we should depenetrate, we were "interacting" with the button. This is
                // publicly-exposed, read-only state.
                _isInteracting = shouldDepenetrate;

                // Depenetrate the button by moving it on its hinge.
                if (shouldDepenetrate)
                {
                    var depenetratingAngle
                                   = performTangencyCalculation(interactor, depenetrationPolarity);
                    _elementAngle += depenetratingAngle;

                    _restCorrectionVelocity = 0f;
                }
            }
            else
            {
                _isInteracting = false;
            }

            // Update the transform to reflect the latest angle.
            {
                // Get the rotation about the hinge axis from the updated hinging element angle.
                var elementRotation
                    = Quaternion.AngleAxis(_elementAngle, _hingeLocalAxis);

                // Start with the current hinge pose, rotate it with the element rotation, then
                // use the rotated hinge pose to recalculate where the hinging element should be
                // relative to it, using the rigid delta-pose that is calculated on Start.
                var hingeRotation    = _hingeTransform.rotation * elementRotation;
                var rotatedHingePose = _hingeTransform.ToPose().WithRotation(hingeRotation);
                this.transform.SetPose(rotatedHingePose * _elementRestPose_hinge);
            }
        }
Beispiel #6
0
        public void Receive(Pose data)
        {
            _buffer.Add(data);

            if (_buffer.Count == 2)
            {
                Pose a = _buffer.Get(0), b = _buffer.Get(1);
                var  ab = b.position - a.position;

                var handDorsal = a.rotation * Vector3.up;

                Quaternion initRot;

                var initAngle = Vector3.Angle(handDorsal, ab);
                if (initAngle < 10f)
                {
                    var handDistal  = a.rotation * Vector3.forward;
                    var ribbonRight = Vector3.Cross(ab, handDistal).normalized;
                    var ribbonUp    = Vector3.Cross(ribbonRight, ab).normalized;

                    initRot = Quaternion.LookRotation(ab, ribbonUp);
                }
                else
                {
                    var ribbonRight = Vector3.Cross(ab, handDorsal).normalized;
                    var ribbonUp    = Vector3.Cross(ribbonRight, ab).normalized;

                    initRot = Quaternion.LookRotation(ab, ribbonUp);
                }

                var initPose = new Pose(a.position, initRot);
                _buffer.Set(0, initPose);
                OnSend(initPose);
            }

            if (_buffer.IsFull)
            {
                Pose a = _buffer.Get(0), b = _buffer.Get(1), c = _buffer.Get(2);

                var rolllessRot = getRolllessRotation(a, b, c);

                var midPoseRot = Quaternion.Slerp(a.rotation, rolllessRot, 0.5f);
                var midPose    = new Pose(b.position, midPoseRot);

                // Canvas alignment.
                if (doCanvasAlignment)
                {
                    var ribbonNormal  = midPoseRot * Vector3.up;
                    var canvasNormal  = b.rotation * Vector3.up;
                    var ribbonForward = midPoseRot * Vector3.forward;

                    if (Vector3.Dot(ribbonNormal, canvasNormal) < 0f)
                    {
                        canvasNormal *= -1f;
                    }

                    var ribbonCanvasAngle = Vector3.SignedAngle(ribbonNormal, canvasNormal,
                                                                ribbonForward);

                    var alignmentStrengthParam = Mathf.Abs(ribbonCanvasAngle)
                                                 .Map(maximumAlignmentAngleFromY,
                                                      minimumAlignmentAngleFromY,
                                                      0f, 1f);
                    var alignmentStrength = alignmentStrengthCurve.Evaluate(alignmentStrengthParam);
                    var aligningAngle     = alignmentStrength * ribbonCanvasAngle;

                    // Limit canvas alignment based on how much the pitch is currently changing.
                    var right              = a.rotation * Vector3.right;
                    var forward            = a.rotation * Vector3.forward;
                    var bc                 = (c.position - b.position);
                    var bc_pitchProjection = Vector3.ProjectOnPlane(bc, right);
                    var pitchAngle         = Vector3.Angle(forward, bc_pitchProjection);
                    alignmentStrength *= pitchAngle.Map(0f, maxSegmentAngleToAlign, 1f, 0f);

                    var abDistInCM         = (b.position - a.position).magnitude * 100f;
                    var maxAngleCorrection = abDistInCM * maxAlignmentAnglePerCentimeter
                                             * alignmentStrength;
                    aligningAngle = Mathf.Clamp(aligningAngle,
                                                -1f * maxAngleCorrection, maxAngleCorrection);

                    var aligningRot = Quaternion.AngleAxis(aligningAngle, ribbonForward);
                    var alignedRot  = aligningRot * midPoseRot;

                    midPose = new Pose(midPose.position, alignedRot);
                }

                _buffer.Set(1, midPose);
                OnSend(midPose);
            }
        }