/* * 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.Collections.Generic; using UnityEngine; namespace Oculus.Interaction.Input { /// /// A thin HandJoint skeleton implementation that can be used for computing /// world joints from local joints data. /// public class ShadowHand { private readonly Pose[] _localJointMap = new Pose[(int)HandJointId.HandEnd]; private readonly Pose[] _worldJointMap = new Pose[(int)HandJointId.HandEnd]; private Pose _rootPose; private float _rootScale; private ulong _dirtyMap; public ShadowHand() { for (int i = 0; i < _localJointMap.Length; i++) { _localJointMap[i] = Pose.identity; _worldJointMap[i] = Pose.identity; } _rootPose = Pose.identity; _rootScale = 1f; _dirtyMap = 0; } public Pose GetLocalPose(HandJointId handJointId) { return _localJointMap[(int)handJointId]; } public void SetLocalPose(HandJointId jointId, Pose pose) { _localJointMap[(int)jointId] = pose; MarkDirty(jointId); } public Pose GetWorldPose(HandJointId jointId) { UpdateDirty(jointId); return _worldJointMap[(int)jointId]; } public Pose[] GetWorldPoses() { UpdateDirty(HandJointId.HandWristRoot); return _worldJointMap; } public Pose GetRoot() => _rootPose; public void SetRoot(Pose rootPose) { _rootPose = rootPose; MarkDirty(HandJointId.HandStart); } public float GetRootScale() => _rootScale; public void SetRootScale(float scale) { _rootScale = scale; MarkDirty(HandJointId.HandStart); } private bool CheckDirtyBit(int i) => ((_dirtyMap >> i) & 1UL) == 1UL; private void SetDirtyBit(int i) => _dirtyMap = _dirtyMap | (1UL << i); private void ClearDirtyBit(int i) => _dirtyMap = _dirtyMap & ~(1UL << i); private void MarkDirty(HandJointId jointId) { if (CheckDirtyBit((int)jointId)) { return; } SetDirtyBit((int)jointId); foreach (HandJointId child in HandJointUtils.JointChildrenList[(int)jointId]) { MarkDirty(child); } } private void UpdateDirty(HandJointId jointId) { if (!CheckDirtyBit((int)jointId)) { return; } HandJointId parent = HandJointUtils.JointParentList[(int)jointId]; if (parent != HandJointId.Invalid) { UpdateDirty(parent); } ClearDirtyBit((int)jointId); Pose parentWorldPose = parent != HandJointId.Invalid ? GetWorldPose(parent) : _rootPose; Pose localPose = _localJointMap[(int)jointId]; localPose.position *= _rootScale; PoseUtils.Multiply(parentWorldPose, localPose, ref _worldJointMap[(int)jointId]); } public void Copy(ShadowHand hand) { SetRoot(hand.GetRoot()); SetRootScale(hand.GetRootScale()); for (int i = 0; i < (int)HandJointId.HandEnd; i++) { HandJointId jointId = (HandJointId)i; SetLocalPose(jointId, hand.GetLocalPose(jointId)); } } } public static class ShadowHandExtensions { public static void FromHandRoot(this ShadowHand shadow, IHand hand) { hand.GetRootPose(out Pose root); shadow.SetRoot(root); shadow.SetRootScale(hand.Scale); } public static void FromHandFingers(this ShadowHand shadow, IHand hand, bool flipHandedness = false) { hand.GetJointPosesLocal(out ReadOnlyHandJointPoses localJointPoses); shadow.FromJoints(localJointPoses, flipHandedness); } public static void FromJoints(this ShadowHand shadow, IReadOnlyList localJointPoses, bool flipHandedness) { if (localJointPoses.Count != (int)HandJointId.HandEnd) { return; } for (int i = 0; i < (int)HandJointId.HandEnd; i++) { Pose localJointPose = localJointPoses[i]; if (flipHandedness) { localJointPose = HandMirroring.Mirror(localJointPose); } shadow.SetLocalPose((HandJointId)i, localJointPose); } } public static void FromHand(this ShadowHand shadow, IHand hand, bool flipHandedness = false) { FromHandRoot(shadow, hand); FromHandFingers(shadow, hand, flipHandedness); } } }