/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using Oculus.Interaction.Input; using UnityEngine; namespace Oculus.Interaction.HandGrab { /// /// Reads a HandGrabState and applies hand visual constraints to a SyntheticHand /// public class HandGrabStateVisual : MonoBehaviour { [SerializeField] [Interface(typeof(IHandGrabState))] private UnityEngine.Object _handGrabState; private IHandGrabState HandGrabState; [SerializeField] private SyntheticHand _syntheticHand; private bool _areFingersFree = true; private bool _isWristFree = true; private bool _wasCompletelyFree = true; protected bool _started = false; protected virtual void Awake() { HandGrabState = _handGrabState as IHandGrabState; } protected virtual void Start() { this.BeginStart(ref _started); this.AssertField(HandGrabState, nameof(HandGrabState)); this.AssertField(_syntheticHand, nameof(_syntheticHand)); this.EndStart(ref _started); } private void LateUpdate() { ConstrainingForce(HandGrabState, out float fingersConstraint, out float wristConstraint); UpdateHandPose(HandGrabState, fingersConstraint, wristConstraint); bool isCompletelyFree = _areFingersFree && _isWristFree; if (!isCompletelyFree || isCompletelyFree && !_wasCompletelyFree) { _syntheticHand.MarkInputDataRequiresUpdate(); } _wasCompletelyFree = isCompletelyFree; } private void ConstrainingForce(IHandGrabState grabSource, out float fingersConstraint, out float wristConstraint) { HandGrabTarget grabData = grabSource.HandGrabTarget; fingersConstraint = wristConstraint = 0; if (grabData == null) { return; } bool isGrabbing = grabSource.IsGrabbing; if (isGrabbing && grabData.HandAlignment != HandAlignType.None) { fingersConstraint = grabSource.FingersStrength; wristConstraint = grabSource.WristStrength; } else if (grabData.HandAlignment == HandAlignType.AttractOnHover) { fingersConstraint = grabSource.FingersStrength; wristConstraint = grabSource.WristStrength; } else if (grabData.HandAlignment == HandAlignType.AlignFingersOnHover) { fingersConstraint = grabSource.FingersStrength; } } private void UpdateHandPose(IHandGrabState grabSource, float fingersConstraint, float wristConstraint) { HandGrabTarget grabTarget = grabSource.HandGrabTarget; if (grabTarget == null) { FreeFingers(); FreeWrist(); return; } if (fingersConstraint > 0f && grabTarget.HandPose != null) { UpdateFingers(grabTarget.HandPose, grabSource.GrabbingFingers(), fingersConstraint); _areFingersFree = false; } else { FreeFingers(); } if (wristConstraint > 0f) { Pose wristPose = grabSource.GetVisualWristPose(); _syntheticHand.LockWristPose(wristPose, wristConstraint, SyntheticHand.WristLockMode.Full, true); _isWristFree = false; } else { FreeWrist(); } } /// /// Writes the desired rotation values for each joint based on the provided HandGrabState. /// Apart from the rotations it also writes in the syntheticHand if it should allow rotations /// past that. /// When no snap is provided, it frees all fingers allowing unconstrained tracked motion. /// private void UpdateFingers(HandPose handPose, HandFingerFlags grabbingFingers, float strength) { Quaternion[] desiredRotations = handPose.JointRotations; _syntheticHand.OverrideAllJoints(desiredRotations, strength); for (int fingerIndex = 0; fingerIndex < Constants.NUM_FINGERS; fingerIndex++) { int fingerFlag = 1 << fingerIndex; JointFreedom fingerFreedom = handPose.FingersFreedom[fingerIndex]; if (fingerFreedom == JointFreedom.Constrained && ((int)grabbingFingers & fingerFlag) != 0) { fingerFreedom = JointFreedom.Locked; } _syntheticHand.SetFingerFreedom((HandFinger)fingerIndex, fingerFreedom); } } private bool FreeFingers() { if (!_areFingersFree) { _syntheticHand.FreeAllJoints(); _areFingersFree = true; return true; } return false; } private bool FreeWrist() { if (!_isWristFree) { _syntheticHand.FreeWrist(); _isWristFree = true; return true; } return false; } #region Inject public void InjectAllHandGrabInteractorVisual(IHandGrabState handGrabState, SyntheticHand syntheticHand) { InjectHandGrabState(handGrabState); InjectSyntheticHand(syntheticHand); } public void InjectHandGrabState(IHandGrabState handGrabState) { HandGrabState = handGrabState; _handGrabState = handGrabState as UnityEngine.Object; } public void InjectSyntheticHand(SyntheticHand syntheticHand) { _syntheticHand = syntheticHand; } #endregion } }