VR4RoboticArm2/VR4RoboticArm/Library/PackageCache/com.meta.xr.sdk.interaction/Runtime/UnityXR/Scripts/FromOpenXRHandDataSource.cs
IonutMocanu 48cccc22ad Main2
2025-09-08 11:13:29 +03:00

251 lines
9.6 KiB
C#

/*
* 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.GrabAPI;
using System;
using UnityEngine;
using UnityEngine.Scripting;
namespace Oculus.Interaction.Input.UnityXR
{
/// <summary>
/// <para>Accepts OpenXR Hand Data returning an ISDK compatible HandDataAsset.</para>
/// </summary>
public abstract class FromOpenXRHandDataSource : DataSource<HandDataAsset>
{
#if ISDK_OPENXR_HAND
// class should only exist if !ISDK_OPENXR_HAND
#else
[Serializable]
public class OpenXRHandDataAsset : ICopyFrom<OpenXRHandDataAsset>
{
public static class Constants
{
public const int NUM_HAND_JOINTS = 26;
public const int NUM_FINGERS = 5;
}
[Flags]
public enum JointTrackingState
{
None = 0,
Radius = 1,
Pose = 2,
LinearVelocity = 4,
AngularVelocity = 8,
WillNeverBeValid = 16
}
[Flags]
[Preserve]
public enum AimFlagsFB : ulong
{
None = 0,
Computed = 1,
Valid = 2,
IndexPinching = 4,
MiddlePinching = 8,
RingPinching = 16, // 0x0000000000000010
LittlePinching = 32, // 0x0000000000000020
SystemGesture = 64, // 0x0000000000000040
DominantHand = 128, // 0x0000000000000080
MenuPressed = 256, // 0x0000000000000100
}
public bool IsDataValid;
public bool IsConnected;
public bool IsTracked;
// XR_EXT_hand_tracking
public Pose Root;
public PoseOrigin RootPoseOrigin;
public JointTrackingState[] JointStates = new JointTrackingState[Constants.NUM_HAND_JOINTS];
public Pose[] JointPoses = new Pose[Constants.NUM_HAND_JOINTS];
public float[] JointRadiuses = new float[Constants.NUM_HAND_JOINTS];
public Vector3[] JointAngularVelocities = new Vector3[Constants.NUM_HAND_JOINTS];
public Vector3[] JointLinearVelocities = new Vector3[Constants.NUM_HAND_JOINTS];
// XR_FB_hand_tracking_aim
public AimFlagsFB AimFlags;
public float[] FingerPinchStrength = new float[Constants.NUM_FINGERS];
public Pose PointerPose;
public PoseOrigin PointerPoseOrigin;
public HandDataSourceConfig Config = new();
public void CopyFrom(OpenXRHandDataAsset source)
{
IsDataValid = source.IsDataValid;
IsConnected = source.IsConnected;
IsTracked = source.IsTracked;
AimFlags = source.AimFlags;
Config = source.Config;
CopyPosesFrom(source);
}
private void CopyPosesFrom(OpenXRHandDataAsset source)
{
Root = source.Root;
RootPoseOrigin = source.RootPoseOrigin;
Array.Copy(source.JointStates, JointStates, Constants.NUM_HAND_JOINTS);
Array.Copy(source.JointPoses, JointPoses, Constants.NUM_HAND_JOINTS);
Array.Copy(source.JointRadiuses, JointRadiuses, Constants.NUM_HAND_JOINTS);
Array.Copy(source.JointAngularVelocities, JointAngularVelocities, Constants.NUM_HAND_JOINTS);
Array.Copy(source.JointLinearVelocities, JointLinearVelocities, Constants.NUM_HAND_JOINTS);
Array.Copy(source.FingerPinchStrength, FingerPinchStrength, FingerPinchStrength.Length);
PointerPose = source.PointerPose;
PointerPoseOrigin = source.PointerPoseOrigin;
Config = source.Config;
}
}
protected abstract OpenXRHandDataAsset OpenXRData { get; }
#endif
private readonly static float DefaultSkeletonIndexMagnitude = HandSkeleton.DefaultLeftSkeleton[(int)HandJointId.HandIndex2].pose.position
.magnitude;
private const float PressThreshold = 0.8f;
static readonly Vector3 TrackedRemoteAimOffset = new(0.0f, 0.0f, -0.055f);
[SerializeField, Interface(typeof(IHmd))]
private UnityEngine.Object _hmdData;
private IHmd HmdData;
#if ISDK_OPENXR_HAND
protected readonly HandDataAsset _dataAsset = new();
#else
private readonly HandDataAsset _dataAsset = new();
#endif
// Meta Hand Aim Mocking
#if ISDK_OPENXR_HAND
protected bool _shouldMockHandTrackingAim = false;
private PinchGrabAPI _fingerGrabAPI;
#else
private HandJointCache _jointCache;
private FingerPinchGrabAPI _fingerGrabAPI;
#endif
protected virtual void Awake()
{
HmdData = _hmdData as IHmd;
}
protected override void Start()
{
this.BeginStart(ref _started, () => base.Start());
this.AssertField(HmdData, nameof(HmdData));
this.EndStart(ref _started);
}
protected override void UpdateData()
{
#if ISDK_OPENXR_HAND
// Legacy local rotations
for (int i = 0; i < Constants.NUM_HAND_JOINTS; i++)
{
int parent = (int)HandJointUtils.JointParentList[i];
_dataAsset.Joints[i] = parent < 0 ? Quaternion.identity :
Quaternion.Inverse(_dataAsset.JointPoses[parent].rotation) *
_dataAsset.JointPoses[i].rotation;
}
UpdateHandScale(
_dataAsset.JointPoses[(int)HandJointId.HandIndex1].position, // IndexProximal
_dataAsset.JointPoses[(int)HandJointId.HandIndex2].position); // IndexIntermediate
// if XR_FB_hand_tracking_aim is unavailable
if (_dataAsset.IsDataValidAndConnected && _shouldMockHandTrackingAim)
{
PopulateMockHandTrackingAim(_dataAsset.JointPoses[0]);
}
#else
var openXRData = OpenXRData;
_dataAsset.CopyFrom(openXRData);
UpdateHandScale(
OpenXRData.JointPoses[7].position, // IndexProximal
OpenXRData.JointPoses[8].position); // IndexIntermediate
// if XR_FB_hand_tracking_aim is unavailable
if (_dataAsset.IsDataValidAndConnected && openXRData.AimFlags == OpenXRHandDataAsset.AimFlagsFB.None)
{
PopulateMockHandTrackingAim(openXRData.JointPoses[0]);
}
#endif
}
private void UpdateHandScale(Vector3 indexProximal, Vector3 indexIntermediate)
{
// calculate scale comparing Index Proximal -> Intermediate distance
var indexDistance = Vector3.Distance(
indexProximal,
indexIntermediate);
_dataAsset.HandScale = indexDistance / DefaultSkeletonIndexMagnitude;
#if ISDK_OPENXR_HAND
// normalize joint poses
var unscaleFactor = 1 / _dataAsset.HandScale;
for (int i = 0; i < Constants.NUM_HAND_JOINTS; i++)
{
_dataAsset.JointPoses[i].position *= unscaleFactor;
}
#endif
}
private void PopulateMockHandTrackingAim(Pose xrPalmPose)
{
_dataAsset.PointerPose =
xrPalmPose.GetTransformedBy(new Pose(TrackedRemoteAimOffset, Quaternion.identity));
_dataAsset.PointerPoseOrigin = PoseOrigin.SyntheticPose;
_dataAsset.IsDominantHand = _dataAsset.Config.Handedness == Handedness.Right;
#if ISDK_OPENXR_HAND
var localJointPoses = _dataAsset.JointPoses;
_fingerGrabAPI ??= new PinchGrabAPI(HmdData);
_fingerGrabAPI.Update(localJointPoses, _dataAsset.Config.Handedness, _dataAsset.Root, _dataAsset.HandScale);
#else
// Update Joint Cache
_jointCache ??= new HandJointCache(_dataAsset.Config.HandSkeleton);
_jointCache.Update(_dataAsset, CurrentDataVersion);
_jointCache.GetAllPosesFromWrist(out var localJointPoses);
_fingerGrabAPI ??= new FingerPinchGrabAPI(HmdData);
_fingerGrabAPI.Update(localJointPoses, _dataAsset.Config.Handedness, _dataAsset.Root);
#endif
PopulateMockHandTrackingAimFinger(HandFinger.Index);
PopulateMockHandTrackingAimFinger(HandFinger.Middle);
PopulateMockHandTrackingAimFinger(HandFinger.Ring);
PopulateMockHandTrackingAimFinger(HandFinger.Pinky);
}
private void PopulateMockHandTrackingAimFinger(HandFinger finger)
{
var fingerIndex = (int)finger;
_dataAsset.FingerPinchStrength[fingerIndex] =
_fingerGrabAPI.GetFingerGrabScore(finger);
_dataAsset.IsFingerPinching[fingerIndex] =
_dataAsset.FingerPinchStrength[fingerIndex] > PressThreshold;
}
protected override HandDataAsset DataAsset => _dataAsset;
}
}