//Loops: private void Update() { if (!_isMine) { transform.position = Vector3.SmoothDamp(transform.position, _targetPosition, ref _positionVelocity, smoothTime); transform.rotation = MotionUtilities.SmoothDamp(transform.rotation, _targetRotation, ref _rotationVelocity, smoothTime); transform.localScale = Vector3.SmoothDamp(transform.localScale, targetScale, ref _scaleVelocity, smoothTime); } }
//Coroutines: private IEnumerator TweenPose() { while (Quaternion.Angle(transform.rotation, _targetRotation) > 0) { transform.position = Vector3.SmoothDamp(transform.position, _targetPosition, ref _positionalVelocity, _smoothTime); transform.rotation = MotionUtilities.SmoothDamp(transform.rotation, _targetRotation, ref _rotationalVelocity, _smoothTime); yield return(null); } }
//Loops: private void LateUpdate() { if (!_isMine) { transform.localPosition = Vector3.SmoothDamp(transform.localPosition, targetPosition, ref _positionaVelocity, smoothTime); transform.localRotation = MotionUtilities.SmoothDamp(transform.localRotation, targetRotation, ref _rotationVelocity, smoothTime); transform.localScale = Vector3.SmoothDamp(transform.localScale, targetScale, ref _scaleVelocity, smoothTime); } else { //automatically follow motion source if set: if (motionSource != null) { transform.position = motionSource.position; transform.rotation = motionSource.rotation; transform.localScale = motionSource.localScale; } } }
//Loops: private void LateUpdate() { //remove any negative durations: arrivalDuration = Mathf.Max(0, arrivalDuration); //direction: _direction = Vector3.Normalize(transform.position - _mainCamera.position); if (_direction == Vector3.zero) { return; } //match gravity? if (matchGravity) { _direction = Vector3.ProjectOnPlane(_direction, Vector3.up); } //flipped? if (flippedForward) { _direction *= -1; } //get rotation: if (_direction != Vector3.zero) { _rotation = Quaternion.LookRotation(_direction); } //apply: if (Application.isPlaying) { transform.rotation = MotionUtilities.SmoothDamp(transform.rotation, _rotation, ref _velocity, arrivalDuration); } else { //time doesn't update smoothly while not playing in the editor so let's just force snapping: transform.rotation = _rotation; } }
//Loops: private void Update() { //lerp us to the world-aligned location: transform.position = Vector3.SmoothDamp(transform.position, _targetPosition, ref _positionalVelocity, _smoothTime); transform.rotation = MotionUtilities.SmoothDamp(transform.rotation, _targetRotation, ref _rotationalVelocity, _smoothTime); }
//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(); }
public void GrabUpdate(InteractionPoint interactionPoint) { //dragging started? if (!Dragging) { foreach (var item in _initialInteractionPoses) { if (Vector3.Distance(item.Key.position, item.Value.position) > _dragThreshold) { //halt physics: if (_rigidBody != null) { _rigidBody.velocity = Vector3.zero; _rigidBody.angularVelocity = Vector3.zero; _rigidBody.isKinematic = true; } //find offset: Matrix4x4 matrix = Matrix4x4.TRS(item.Key.position, item.Key.rotation, Vector3.one); Vector3 positionOffset = matrix.inverse.MultiplyPoint3x4(transform.position); Quaternion rotationOffset = Quaternion.Inverse(item.Key.rotation) * transform.rotation; _offset = new Pose(positionOffset, rotationOffset); //resets: _position = Vector3.zero; _rotation = Quaternion.identity; //status: Dragging = true; OnDragBegin?.Invoke(item.Key); break; } } } //dragging? if (Dragging) { //find center of interaction points involved with this drag: Bounds dragBounds = new Bounds(activeInteractionPoints[0].position, Vector3.zero); for (int i = 1; i < activeInteractionPoints.Count; i++) { dragBounds.Encapsulate(activeInteractionPoints[i].position); } //discover drag distance and percentage; float biManualDistance = dragBounds.size.magnitude; //holders: Vector3 dragLocation = Vector3.zero; Quaternion dragOrientation = Quaternion.identity; Vector3 forward = Vector3.zero; float scaleDelta = 0; //rotation: if (activeInteractionPoints.Count == 2) { //get rotated amount: forward = activeInteractionPoints[0].position - activeInteractionPoints[1].position; Vector3 forwardNormalized = forward.normalized; Vector3 previousForward = _bimanualBaseRotation * Vector3.forward; Vector3 up = Vector3.Cross(forwardNormalized, previousForward).normalized; float angle = Vector3.SignedAngle(previousForward, forwardNormalized, up); //update rotation: Quaternion rotationDelta = Quaternion.AngleAxis(angle, up); _bimanualBaseRotation = rotationDelta * _bimanualBaseRotation; dragOrientation = _bimanualBaseRotation * _offset.rotation; } else { dragOrientation = activeInteractionPoints[0].rotation * _offset.rotation; } //position: if (activeInteractionPoints.Count == 2) { Matrix4x4 matrix = Matrix4x4.TRS(dragBounds.center, _bimanualBaseRotation, Vector3.one); dragLocation = matrix.MultiplyPoint3x4(_offset.position); } else { Matrix4x4 matrix = Matrix4x4.TRS(activeInteractionPoints[0].position, activeInteractionPoints[0].rotation, Vector3.one); dragLocation = matrix.MultiplyPoint3x4(_offset.position); } //scale: if (activeInteractionPoints.Count == 2) { scaleDelta = biManualDistance - _scaleInitialDistance; } //set smoothing origins: if (_position == Vector3.zero || _rotation == Quaternion.identity) { _position = dragLocation; _rotation = dragOrientation; } //application: if (draggable) { _position = Vector3.SmoothDamp(_position, dragLocation, ref _positionVelocity, positionSmoothTime); if (_rigidBody != null) { _rigidBody.MovePosition(_position); _averageVelocity.Add(_rigidBody.velocity); _averageAngularVelocity.Add(_rigidBody.angularVelocity); } else { transform.position = _position; } } if (rotatable) { _rotation = MotionUtilities.SmoothDamp(_rotation, dragOrientation, ref _rotationVelocity, rotationSmoothTime); if (_rigidBody != null) { _rigidBody.MoveRotation(_rotation); } else { transform.rotation = _rotation; } } if (scalable && activeInteractionPoints.Count == 2) { Vector3 proposedScale = _scaleBase + (Vector3.one * scaleDelta); //constrain scale and do not not assume uniform initial scale: if (Mathf.Max(proposedScale.x, proposedScale.y, proposedScale.z) <= maxScale && Mathf.Min(proposedScale.x, proposedScale.y, proposedScale.z) >= minScale) { transform.localScale = _scaleBase + (Vector3.one * scaleDelta); } } //visuals: if (activeInteractionPoints.Count == 2) { Lines.SetVisibility(_handConnectionLine, true); Lines.DrawLine(_handConnectionLine, Color.cyan, Color.cyan, .0005f, activeInteractionPoints[0].position, activeInteractionPoints[1].position); } //status: OnDragUpdate?.Invoke(activeInteractionPoints.ToArray(), _position, _rotation, scaleDelta); } }