/* * 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 System; using UnityEngine; namespace Oculus.Interaction.Input { // A top level component that provides hand pose data, pinch states, and more. // Rather than sourcing data directly from the runtime layer, provides one // level of abstraction so that the aforementioned data can be injected // from other sources. public class Hand : DataModifier, IHand { /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public Handedness Handedness => GetData().Config.Handedness; /// /// Returns an for the tracking space associated with this hand, capable /// of transforming tracking data from tracking space to world space. /// public ITrackingToWorldTransformer TrackingToWorldTransformer => GetData().Config.TrackingToWorldTransformer; /// /// Returns the containing the most recent tracking data from the system for the represented /// tracked hand. /// public HandSkeleton HandSkeleton => GetData().Config.HandSkeleton; private HandJointCache _jointPosesCache; /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public event Action WhenHandUpdated = delegate { }; /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool IsConnected => GetData().IsDataValidAndConnected; /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool IsHighConfidence => GetData().IsHighConfidence; /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool IsDominantHand => GetData().IsDominantHand; /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public float Scale => (TrackingToWorldTransformer != null ? TrackingToWorldTransformer.Transform.lossyScale.x : 1) * GetData().HandScale; private static readonly Vector3 PALM_LOCAL_OFFSET = new Vector3(0.08f, -0.01f, 0.0f); protected override void Apply(HandDataAsset data) { // Default implementation does nothing, to allow instantiation of this modifier directly } /// /// Implementation of ; for details, please refer to the related /// documentation provided for that interface. /// public override void MarkInputDataRequiresUpdate() { base.MarkInputDataRequiresUpdate(); if (Started) { InitializeJointPosesCache(); WhenHandUpdated.Invoke(); } } private void InitializeJointPosesCache() { if (_jointPosesCache == null && GetData().IsDataValidAndConnected) { #if ISDK_OPENXR_HAND _jointPosesCache = new HandJointCache(); #else _jointPosesCache = new HandJointCache(HandSkeleton); #endif } } private void CheckJointPosesCacheUpdate() { if (_jointPosesCache != null && CurrentDataVersion != _jointPosesCache.LocalDataVersion) { #if ISDK_OPENXR_HAND _jointPosesCache.Update(GetData(), CurrentDataVersion, TrackingToWorldTransformer?.Transform); #else _jointPosesCache.Update(GetData(), CurrentDataVersion); #endif } } #region IHandState implementation /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool GetFingerIsPinching(HandFinger finger) { HandDataAsset currentData = GetData(); return currentData.IsConnected && currentData.IsFingerPinching[(int)finger]; } /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool GetIndexFingerIsPinching() { return GetFingerIsPinching(HandFinger.Index); } /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool IsPointerPoseValid => IsPoseOriginAllowed(GetData().PointerPoseOrigin); /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool GetPointerPose(out Pose pose) { HandDataAsset currentData = GetData(); return ValidatePose(currentData.PointerPose, currentData.PointerPoseOrigin, out pose); } /// /// Implementation of ; for details, please refer to the related /// documentation provided for that interface. /// public bool GetJointPose(HandJointId handJointId, out Pose pose) { pose = Pose.identity; if (!IsTrackedDataValid || _jointPosesCache == null || !GetRootPose(out Pose rootPose)) { return false; } CheckJointPosesCacheUpdate(); #if ISDK_OPENXR_HAND pose = _jointPosesCache.GetWorldJointPose(handJointId); #else pose = _jointPosesCache.WorldJointPose(handJointId, rootPose, Scale); #endif return true; } /// /// Implementation of ; for details, please refer to the related /// documentation provided for that interface. /// public bool GetJointPoseLocal(HandJointId handJointId, out Pose pose) { pose = Pose.identity; if (!GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses)) { return false; } pose = localJointPoses[(int)handJointId]; return true; } /// /// Implementation of ; for details, please refer to the /// related documentation provided for that interface. /// public bool GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses) { if (!IsTrackedDataValid || _jointPosesCache == null) { localJointPoses = ReadOnlyHandJointPoses.Empty; return false; } CheckJointPosesCacheUpdate(); return _jointPosesCache.GetAllLocalPoses(out localJointPoses); } /// /// Implementation of ; for details, please refer to the /// related documentation provided for that interface. /// public bool GetJointPoseFromWrist(HandJointId handJointId, out Pose pose) { pose = Pose.identity; if (!GetJointPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist)) { return false; } pose = jointPosesFromWrist[(int)handJointId]; return true; } /// /// Implementation of ; for details, please refer to /// the related documentation provided for that interface. /// public bool GetJointPosesFromWrist(out ReadOnlyHandJointPoses jointPosesFromWrist) { if (!IsTrackedDataValid || _jointPosesCache == null) { jointPosesFromWrist = ReadOnlyHandJointPoses.Empty; return false; } CheckJointPosesCacheUpdate(); return _jointPosesCache.GetAllPosesFromWrist(out jointPosesFromWrist); } /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool GetPalmPoseLocal(out Pose pose) { Quaternion rotationQuat = Quaternion.identity; Vector3 offset = PALM_LOCAL_OFFSET; if (Handedness == Handedness.Left) { offset = -offset; } pose = new Pose(offset * Scale, rotationQuat); return true; } /// /// Implementation of ; for details, please refer to the related /// documentation provided for that interface. /// public bool GetFingerIsHighConfidence(HandFinger finger) { return GetData().IsFingerHighConfidence[(int)finger]; } /// /// Implementation of ; for details, please refer to the related /// documentation provided for that interface. /// public float GetFingerPinchStrength(HandFinger finger) { return GetData().FingerPinchStrength[(int)finger]; } /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool IsTrackedDataValid => IsPoseOriginAllowed(GetData().RootPoseOrigin); /// /// Implementation of ; for details, please refer to the related documentation /// provided for that interface. /// public bool GetRootPose(out Pose pose) { HandDataAsset currentData = GetData(); return ValidatePose(currentData.Root, currentData.RootPoseOrigin, out pose); } #endregion private bool ValidatePose(in Pose sourcePose, PoseOrigin sourcePoseOrigin, out Pose pose) { if (IsPoseOriginDisallowed(sourcePoseOrigin)) { pose = Pose.identity; return false; } pose = TrackingToWorldTransformer != null ? TrackingToWorldTransformer.ToWorldPose(sourcePose) : sourcePose; return true; } private bool IsPoseOriginAllowed(PoseOrigin poseOrigin) { return poseOrigin != PoseOrigin.None; } private bool IsPoseOriginDisallowed(PoseOrigin poseOrigin) { return poseOrigin == PoseOrigin.None; } #region Inject /// /// Injects all required dependencies for a dynamically instantiated Hand; because the only required dependencies /// are those of the underlying , this simply wraps /// . /// This method exists to support Interaction SDK's dependency injection pattern and is not needed for typical Unity /// Editor-based usage. /// public void InjectAllHand(UpdateModeFlags updateMode, IDataSource updateAfter, DataModifier modifyDataFromSource, bool applyModifier) { base.InjectAllDataModifier(updateMode, updateAfter, modifyDataFromSource, applyModifier); } #endregion } }