/* * 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 Meta.XR.Util; using UnityEngine; using static OVRSkeleton; namespace Oculus.Interaction.Input { /// /// An implementation of IHand that provides hand tracking data to Interaction SDK from a an OVRHand instance. /// [Feature(Feature.Interaction)] public class FromOVRHandDataSource : DataSource { [Header("OVR Data Source")] [SerializeField, Interface(typeof(IOVRCameraRigRef))] private UnityEngine.Object _cameraRigRef; [SerializeField] private bool _processLateUpdates = false; public bool ProcessLateUpdates { get { return _processLateUpdates; } set { _processLateUpdates = value; } } [Header("Shared Configuration")] [SerializeField] private Handedness _handedness; [SerializeField, Optional(OptionalAttribute.Flag.AutoGenerated)] private OVRHand _ovrHand; [SerializeField, Interface(typeof(ITrackingToWorldTransformer))] private UnityEngine.Object _trackingToWorldTransformer; private ITrackingToWorldTransformer TrackingToWorldTransformer; [SerializeField, Interface(typeof(IHandSkeletonProvider))] private UnityEngine.Object _handSkeletonProvider; private IHandSkeletonProvider HandSkeletonProvider; private readonly HandDataAsset _handDataAsset = new HandDataAsset(); private float _lastHandScale; private HandDataSourceConfig _config; private IOVRCameraRigRef CameraRigRef; protected override HandDataAsset DataAsset => _handDataAsset; // Wrist rotations that come from OVR need correcting. public static Quaternion WristFixupRotation { get; } = new Quaternion(0.0f, 1.0f, 0.0f, 0.0f); protected virtual void Awake() { TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer; CameraRigRef = _cameraRigRef as IOVRCameraRigRef; HandSkeletonProvider = _handSkeletonProvider as IHandSkeletonProvider; UpdateConfig(); } protected override void Start() { this.BeginStart(ref _started, () => base.Start()); this.AssertField(CameraRigRef, nameof(CameraRigRef)); this.AssertField(TrackingToWorldTransformer, nameof(TrackingToWorldTransformer)); this.AssertField(HandSkeletonProvider, nameof(HandSkeletonProvider)); if (_ovrHand == null) { _ovrHand = _handedness == Handedness.Left ? CameraRigRef.LeftHand : CameraRigRef.RightHand; } this.AssertField(_ovrHand, nameof(_ovrHand)); UpdateConfig(); var globalVersion = OVRRuntimeSettings.GetRuntimeSettings().HandSkeletonVersion; #if ISDK_OPENXR_HAND UnityEngine.Assertions.Assert.AreEqual(globalVersion, OVRHandSkeletonVersion.OpenXR, $"Hand Skeleton Version in OVRManager must be set to {OVRHandSkeletonVersion.OpenXR}."); #else UnityEngine.Assertions.Assert.AreEqual(globalVersion, OVRHandSkeletonVersion.OVR, $"Hand Skeleton Version in OVRManager must be set to {OVRHandSkeletonVersion.OVR}."); #endif this.EndStart(ref _started); } protected override void OnEnable() { base.OnEnable(); if (_started) { CameraRigRef.WhenInputDataDirtied += HandleInputDataDirtied; } } protected override void OnDisable() { if (_started) { CameraRigRef.WhenInputDataDirtied -= HandleInputDataDirtied; } base.OnDisable(); MarkInputDataRequiresUpdate(); } private void HandleInputDataDirtied(bool isLateUpdate) { if (isLateUpdate && !_processLateUpdates) { return; } MarkInputDataRequiresUpdate(); } private HandDataSourceConfig Config { get { if (_config != null) { return _config; } _config = new HandDataSourceConfig() { Handedness = _handedness }; return _config; } } private void UpdateConfig() { Config.Handedness = _handedness; Config.TrackingToWorldTransformer = TrackingToWorldTransformer; Config.HandSkeleton = HandSkeletonProvider[_handedness]; } protected override void UpdateData() { _handDataAsset.Config = Config; _handDataAsset.IsDataValid = true; _handDataAsset.IsConnected = false; if (_ovrHand != null && _ovrHand.isActiveAndEnabled && this.isActiveAndEnabled) { IOVRSkeletonDataProvider skeletonProvider = _ovrHand; SkeletonPoseData poseData = skeletonProvider.GetSkeletonPoseData(); _handDataAsset.IsConnected = poseData.IsDataValid && poseData.RootScale > 0.0f; if (!_handDataAsset.IsConnected) { if (_lastHandScale <= 0.0f) { poseData.IsDataValid = false; } else { poseData.RootScale = _lastHandScale; } } else { _lastHandScale = poseData.RootScale; } if (poseData.IsDataValid && _handDataAsset.IsConnected) { UpdateDataPoses(poseData); return; } } // revert state fields to their defaults _handDataAsset.IsConnected = default; _handDataAsset.IsTracked = default; _handDataAsset.RootPoseOrigin = default; _handDataAsset.PointerPoseOrigin = default; _handDataAsset.IsHighConfidence = default; for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; fingerIdx++) { _handDataAsset.IsFingerPinching[fingerIdx] = default; _handDataAsset.IsFingerHighConfidence[fingerIdx] = default; } } private void UpdateDataPoses(SkeletonPoseData poseData) { _handDataAsset.HandScale = poseData.RootScale; _handDataAsset.IsTracked = _ovrHand.IsTracked; _handDataAsset.IsHighConfidence = poseData.IsDataHighConfidence; _handDataAsset.IsDominantHand = _ovrHand.IsDominantHand; _handDataAsset.RootPoseOrigin = _handDataAsset.IsTracked ? PoseOrigin.RawTrackedPose : PoseOrigin.None; for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; fingerIdx++) { var ovrFingerIdx = (OVRHand.HandFinger)fingerIdx; bool isPinching = _ovrHand.GetFingerIsPinching(ovrFingerIdx); _handDataAsset.IsFingerPinching[fingerIdx] = isPinching; bool isHighConfidence = _ovrHand.GetFingerConfidence(ovrFingerIdx) == OVRHand.TrackingConfidence.High; _handDataAsset.IsFingerHighConfidence[fingerIdx] = isHighConfidence; float fingerPinchStrength = _ovrHand.GetFingerPinchStrength(ovrFingerIdx); _handDataAsset.FingerPinchStrength[fingerIdx] = fingerPinchStrength; } // Read the poses directly from the poseData, so it isn't in conflict with // any modifications that the application makes to OVRSkeleton _handDataAsset.Root = new Pose() { position = poseData.RootPose.Position.FromFlippedZVector3f(), rotation = poseData.RootPose.Orientation.FromFlippedZQuatf() }; if (_ovrHand.IsPointerPoseValid) { _handDataAsset.PointerPoseOrigin = PoseOrigin.RawTrackedPose; _handDataAsset.PointerPose = new Pose(_ovrHand.PointerPose.localPosition, _ovrHand.PointerPose.localRotation); } else { _handDataAsset.PointerPoseOrigin = PoseOrigin.None; } #if ISDK_OPENXR_HAND float scaleFixup = _handDataAsset.HandScale > 0 ? 1f / _handDataAsset.HandScale : 0; var ovrSkeleton = (_handedness == Handedness.Left)? OVRSkeletonData.LeftSkeleton : OVRSkeletonData.RightSkeleton; for (int i = 0; i < Constants.NUM_HAND_JOINTS; i++) { Pose jointPose = new Pose( poseData.BoneTranslations[i].FromFlippedZVector3f(), poseData.BoneRotations[i].FromFlippedZQuatf()); Pose fromRoot = PoseUtils.Delta(_handDataAsset.Root, jointPose); fromRoot.position *= scaleFixup; _handDataAsset.JointPoses[i] = fromRoot; _handDataAsset.JointRadii[i] = HandSkeletonOVR.GetBoneRadius(ovrSkeleton, i); } // Legacy local rotations #pragma warning disable 0618 HandJointUtils.WristJointPosesToLocalRotations(_handDataAsset.JointPoses, ref _handDataAsset.Joints); #pragma warning restore 0618 #else // Hand joint rotations X axis needs flipping to get to Unity's coordinate system. var bones = poseData.BoneRotations; for (int i = 0; i < bones.Length; i++) { // When using Link in the Unity Editor, the first frame of hand data // sometimes contains bad joint data. _handDataAsset.Joints[i] = float.IsNaN(bones[i].w) ? Config.HandSkeleton.joints[i].pose.rotation : bones[i].FromFlippedXQuatf(); } _handDataAsset.Joints[0] = WristFixupRotation; #endif } #region Inject public void InjectAllFromOVRHandDataSource(UpdateModeFlags updateMode, IDataSource updateAfter, Handedness handedness, ITrackingToWorldTransformer trackingToWorldTransformer, IHandSkeletonProvider handSkeletonProvider) { base.InjectAllDataSource(updateMode, updateAfter); InjectHandedness(handedness); InjectTrackingToWorldTransformer(trackingToWorldTransformer); InjectHandSkeletonProvider(handSkeletonProvider); } public void InjectHandedness(Handedness handedness) { _handedness = handedness; } public void InjectTrackingToWorldTransformer(ITrackingToWorldTransformer trackingToWorldTransformer) { _trackingToWorldTransformer = trackingToWorldTransformer as UnityEngine.Object; TrackingToWorldTransformer = trackingToWorldTransformer; } public void InjectHandSkeletonProvider(IHandSkeletonProvider handSkeletonProvider) { _handSkeletonProvider = handSkeletonProvider as UnityEngine.Object; HandSkeletonProvider = handSkeletonProvider; } public void InjectOptionalOVRHand(OVRHand ovrHand) { _ovrHand = ovrHand; } #endregion } }