968 lines
38 KiB
C#
968 lines
38 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 System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
|
|
public class OVRSkeleton : MonoBehaviour
|
|
{
|
|
public interface IOVRSkeletonDataProvider
|
|
{
|
|
SkeletonType GetSkeletonType();
|
|
SkeletonPoseData GetSkeletonPoseData();
|
|
bool enabled { get; }
|
|
}
|
|
|
|
public struct SkeletonPoseData
|
|
{
|
|
public OVRPlugin.Posef RootPose { get; set; }
|
|
public float RootScale { get; set; }
|
|
public OVRPlugin.Quatf[] BoneRotations { get; set; }
|
|
public bool IsDataValid { get; set; }
|
|
public bool IsDataHighConfidence { get; set; }
|
|
public OVRPlugin.Vector3f[] BoneTranslations { get; set; }
|
|
public int SkeletonChangedCount { get; set; }
|
|
}
|
|
|
|
public enum SkeletonType
|
|
{
|
|
None = OVRPlugin.SkeletonType.None,
|
|
HandLeft = OVRPlugin.SkeletonType.HandLeft,
|
|
HandRight = OVRPlugin.SkeletonType.HandRight,
|
|
Body = OVRPlugin.SkeletonType.Body,
|
|
}
|
|
|
|
public enum BoneId
|
|
{
|
|
Invalid = OVRPlugin.BoneId.Invalid,
|
|
|
|
// hand bones
|
|
Hand_Start = OVRPlugin.BoneId.Hand_Start,
|
|
Hand_WristRoot = OVRPlugin.BoneId.Hand_WristRoot, // root frame of the hand, where the wrist is located
|
|
Hand_ForearmStub = OVRPlugin.BoneId.Hand_ForearmStub, // frame for user's forearm
|
|
Hand_Thumb0 = OVRPlugin.BoneId.Hand_Thumb0, // thumb trapezium bone
|
|
Hand_Thumb1 = OVRPlugin.BoneId.Hand_Thumb1, // thumb metacarpal bone
|
|
Hand_Thumb2 = OVRPlugin.BoneId.Hand_Thumb2, // thumb proximal phalange bone
|
|
Hand_Thumb3 = OVRPlugin.BoneId.Hand_Thumb3, // thumb distal phalange bone
|
|
Hand_Index1 = OVRPlugin.BoneId.Hand_Index1, // index proximal phalange bone
|
|
Hand_Index2 = OVRPlugin.BoneId.Hand_Index2, // index intermediate phalange bone
|
|
Hand_Index3 = OVRPlugin.BoneId.Hand_Index3, // index distal phalange bone
|
|
Hand_Middle1 = OVRPlugin.BoneId.Hand_Middle1, // middle proximal phalange bone
|
|
Hand_Middle2 = OVRPlugin.BoneId.Hand_Middle2, // middle intermediate phalange bone
|
|
Hand_Middle3 = OVRPlugin.BoneId.Hand_Middle3, // middle distal phalange bone
|
|
Hand_Ring1 = OVRPlugin.BoneId.Hand_Ring1, // ring proximal phalange bone
|
|
Hand_Ring2 = OVRPlugin.BoneId.Hand_Ring2, // ring intermediate phalange bone
|
|
Hand_Ring3 = OVRPlugin.BoneId.Hand_Ring3, // ring distal phalange bone
|
|
Hand_Pinky0 = OVRPlugin.BoneId.Hand_Pinky0, // pinky metacarpal bone
|
|
Hand_Pinky1 = OVRPlugin.BoneId.Hand_Pinky1, // pinky proximal phalange bone
|
|
Hand_Pinky2 = OVRPlugin.BoneId.Hand_Pinky2, // pinky intermediate phalange bone
|
|
Hand_Pinky3 = OVRPlugin.BoneId.Hand_Pinky3, // pinky distal phalange bone
|
|
Hand_MaxSkinnable = OVRPlugin.BoneId.Hand_MaxSkinnable,
|
|
|
|
// Bone tips are position only. They are not used for skinning but are useful for hit-testing.
|
|
// NOTE: Hand_ThumbTip == Hand_MaxSkinnable since the extended tips need to be contiguous
|
|
Hand_ThumbTip = OVRPlugin.BoneId.Hand_ThumbTip, // tip of the thumb
|
|
Hand_IndexTip = OVRPlugin.BoneId.Hand_IndexTip, // tip of the index finger
|
|
Hand_MiddleTip = OVRPlugin.BoneId.Hand_MiddleTip, // tip of the middle finger
|
|
Hand_RingTip = OVRPlugin.BoneId.Hand_RingTip, // tip of the ring finger
|
|
Hand_PinkyTip = OVRPlugin.BoneId.Hand_PinkyTip, // tip of the pinky
|
|
Hand_End = OVRPlugin.BoneId.Hand_End,
|
|
|
|
// body bones
|
|
Body_Start = OVRPlugin.BoneId.Body_Start,
|
|
Body_Root = OVRPlugin.BoneId.Body_Root,
|
|
Body_Hips = OVRPlugin.BoneId.Body_Hips,
|
|
Body_SpineLower = OVRPlugin.BoneId.Body_SpineLower,
|
|
Body_SpineMiddle = OVRPlugin.BoneId.Body_SpineMiddle,
|
|
Body_SpineUpper = OVRPlugin.BoneId.Body_SpineUpper,
|
|
Body_Chest = OVRPlugin.BoneId.Body_Chest,
|
|
Body_Neck = OVRPlugin.BoneId.Body_Neck,
|
|
Body_Head = OVRPlugin.BoneId.Body_Head,
|
|
Body_LeftShoulder = OVRPlugin.BoneId.Body_LeftShoulder,
|
|
Body_LeftScapula = OVRPlugin.BoneId.Body_LeftScapula,
|
|
Body_LeftArmUpper = OVRPlugin.BoneId.Body_LeftArmUpper,
|
|
Body_LeftArmLower = OVRPlugin.BoneId.Body_LeftArmLower,
|
|
Body_LeftHandWristTwist = OVRPlugin.BoneId.Body_LeftHandWristTwist,
|
|
Body_RightShoulder = OVRPlugin.BoneId.Body_RightShoulder,
|
|
Body_RightScapula = OVRPlugin.BoneId.Body_RightScapula,
|
|
Body_RightArmUpper = OVRPlugin.BoneId.Body_RightArmUpper,
|
|
Body_RightArmLower = OVRPlugin.BoneId.Body_RightArmLower,
|
|
Body_RightHandWristTwist = OVRPlugin.BoneId.Body_RightHandWristTwist,
|
|
Body_LeftHandPalm = OVRPlugin.BoneId.Body_LeftHandPalm,
|
|
Body_LeftHandWrist = OVRPlugin.BoneId.Body_LeftHandWrist,
|
|
Body_LeftHandThumbMetacarpal = OVRPlugin.BoneId.Body_LeftHandThumbMetacarpal,
|
|
Body_LeftHandThumbProximal = OVRPlugin.BoneId.Body_LeftHandThumbProximal,
|
|
Body_LeftHandThumbDistal = OVRPlugin.BoneId.Body_LeftHandThumbDistal,
|
|
Body_LeftHandThumbTip = OVRPlugin.BoneId.Body_LeftHandThumbTip,
|
|
Body_LeftHandIndexMetacarpal = OVRPlugin.BoneId.Body_LeftHandIndexMetacarpal,
|
|
Body_LeftHandIndexProximal = OVRPlugin.BoneId.Body_LeftHandIndexProximal,
|
|
Body_LeftHandIndexIntermediate = OVRPlugin.BoneId.Body_LeftHandIndexIntermediate,
|
|
Body_LeftHandIndexDistal = OVRPlugin.BoneId.Body_LeftHandIndexDistal,
|
|
Body_LeftHandIndexTip = OVRPlugin.BoneId.Body_LeftHandIndexTip,
|
|
Body_LeftHandMiddleMetacarpal = OVRPlugin.BoneId.Body_LeftHandMiddleMetacarpal,
|
|
Body_LeftHandMiddleProximal = OVRPlugin.BoneId.Body_LeftHandMiddleProximal,
|
|
Body_LeftHandMiddleIntermediate = OVRPlugin.BoneId.Body_LeftHandMiddleIntermediate,
|
|
Body_LeftHandMiddleDistal = OVRPlugin.BoneId.Body_LeftHandMiddleDistal,
|
|
Body_LeftHandMiddleTip = OVRPlugin.BoneId.Body_LeftHandMiddleTip,
|
|
Body_LeftHandRingMetacarpal = OVRPlugin.BoneId.Body_LeftHandRingMetacarpal,
|
|
Body_LeftHandRingProximal = OVRPlugin.BoneId.Body_LeftHandRingProximal,
|
|
Body_LeftHandRingIntermediate = OVRPlugin.BoneId.Body_LeftHandRingIntermediate,
|
|
Body_LeftHandRingDistal = OVRPlugin.BoneId.Body_LeftHandRingDistal,
|
|
Body_LeftHandRingTip = OVRPlugin.BoneId.Body_LeftHandRingTip,
|
|
Body_LeftHandLittleMetacarpal = OVRPlugin.BoneId.Body_LeftHandLittleMetacarpal,
|
|
Body_LeftHandLittleProximal = OVRPlugin.BoneId.Body_LeftHandLittleProximal,
|
|
Body_LeftHandLittleIntermediate = OVRPlugin.BoneId.Body_LeftHandLittleIntermediate,
|
|
Body_LeftHandLittleDistal = OVRPlugin.BoneId.Body_LeftHandLittleDistal,
|
|
Body_LeftHandLittleTip = OVRPlugin.BoneId.Body_LeftHandLittleTip,
|
|
Body_RightHandPalm = OVRPlugin.BoneId.Body_RightHandPalm,
|
|
Body_RightHandWrist = OVRPlugin.BoneId.Body_RightHandWrist,
|
|
Body_RightHandThumbMetacarpal = OVRPlugin.BoneId.Body_RightHandThumbMetacarpal,
|
|
Body_RightHandThumbProximal = OVRPlugin.BoneId.Body_RightHandThumbProximal,
|
|
Body_RightHandThumbDistal = OVRPlugin.BoneId.Body_RightHandThumbDistal,
|
|
Body_RightHandThumbTip = OVRPlugin.BoneId.Body_RightHandThumbTip,
|
|
Body_RightHandIndexMetacarpal = OVRPlugin.BoneId.Body_RightHandIndexMetacarpal,
|
|
Body_RightHandIndexProximal = OVRPlugin.BoneId.Body_RightHandIndexProximal,
|
|
Body_RightHandIndexIntermediate = OVRPlugin.BoneId.Body_RightHandIndexIntermediate,
|
|
Body_RightHandIndexDistal = OVRPlugin.BoneId.Body_RightHandIndexDistal,
|
|
Body_RightHandIndexTip = OVRPlugin.BoneId.Body_RightHandIndexTip,
|
|
Body_RightHandMiddleMetacarpal = OVRPlugin.BoneId.Body_RightHandMiddleMetacarpal,
|
|
Body_RightHandMiddleProximal = OVRPlugin.BoneId.Body_RightHandMiddleProximal,
|
|
Body_RightHandMiddleIntermediate = OVRPlugin.BoneId.Body_RightHandMiddleIntermediate,
|
|
Body_RightHandMiddleDistal = OVRPlugin.BoneId.Body_RightHandMiddleDistal,
|
|
Body_RightHandMiddleTip = OVRPlugin.BoneId.Body_RightHandMiddleTip,
|
|
Body_RightHandRingMetacarpal = OVRPlugin.BoneId.Body_RightHandRingMetacarpal,
|
|
Body_RightHandRingProximal = OVRPlugin.BoneId.Body_RightHandRingProximal,
|
|
Body_RightHandRingIntermediate = OVRPlugin.BoneId.Body_RightHandRingIntermediate,
|
|
Body_RightHandRingDistal = OVRPlugin.BoneId.Body_RightHandRingDistal,
|
|
Body_RightHandRingTip = OVRPlugin.BoneId.Body_RightHandRingTip,
|
|
Body_RightHandLittleMetacarpal = OVRPlugin.BoneId.Body_RightHandLittleMetacarpal,
|
|
Body_RightHandLittleProximal = OVRPlugin.BoneId.Body_RightHandLittleProximal,
|
|
Body_RightHandLittleIntermediate = OVRPlugin.BoneId.Body_RightHandLittleIntermediate,
|
|
Body_RightHandLittleDistal = OVRPlugin.BoneId.Body_RightHandLittleDistal,
|
|
Body_RightHandLittleTip = OVRPlugin.BoneId.Body_RightHandLittleTip,
|
|
Body_End = OVRPlugin.BoneId.Body_End,
|
|
|
|
|
|
// add new bones here
|
|
|
|
Max = OVRPlugin.BoneId.Max,
|
|
}
|
|
|
|
[SerializeField]
|
|
protected SkeletonType _skeletonType = SkeletonType.None;
|
|
|
|
[SerializeField]
|
|
private IOVRSkeletonDataProvider _dataProvider;
|
|
|
|
[SerializeField]
|
|
private bool _updateRootPose = false;
|
|
|
|
[SerializeField]
|
|
private bool _updateRootScale = false;
|
|
|
|
[SerializeField]
|
|
private bool _enablePhysicsCapsules = false;
|
|
|
|
[SerializeField]
|
|
private bool _applyBoneTranslations = true;
|
|
|
|
private GameObject _bonesGO;
|
|
private GameObject _bindPosesGO;
|
|
private GameObject _capsulesGO;
|
|
|
|
protected List<OVRBone> _bones;
|
|
private List<OVRBone> _bindPoses;
|
|
private List<OVRBoneCapsule> _capsules;
|
|
|
|
protected OVRPlugin.Skeleton2 _skeleton = new OVRPlugin.Skeleton2();
|
|
private readonly Quaternion wristFixupRotation = new Quaternion(0.0f, 1.0f, 0.0f, 0.0f);
|
|
|
|
public bool IsInitialized { get; private set; }
|
|
public bool IsDataValid { get; private set; }
|
|
public bool IsDataHighConfidence { get; private set; }
|
|
public IList<OVRBone> Bones { get; protected set; }
|
|
public IList<OVRBone> BindPoses { get; private set; }
|
|
public IList<OVRBoneCapsule> Capsules { get; private set; }
|
|
|
|
public SkeletonType GetSkeletonType()
|
|
{
|
|
return _skeletonType;
|
|
}
|
|
|
|
internal virtual void SetSkeletonType(SkeletonType type)
|
|
{
|
|
_skeletonType = type;
|
|
}
|
|
|
|
|
|
public bool IsValidBone(BoneId bone)
|
|
{
|
|
return OVRPlugin.IsValidBone((OVRPlugin.BoneId)bone, (OVRPlugin.SkeletonType)_skeletonType);
|
|
}
|
|
|
|
public int SkeletonChangedCount { get; private set; }
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
if (_dataProvider == null)
|
|
{
|
|
var foundDataProvider = SearchSkeletonDataProvider();
|
|
if (foundDataProvider != null)
|
|
{
|
|
_dataProvider = foundDataProvider;
|
|
if (_dataProvider is MonoBehaviour mb)
|
|
{
|
|
Debug.Log($"Found IOVRSkeletonDataProvider reference in {mb.name} due to unassigned field.");
|
|
}
|
|
}
|
|
}
|
|
|
|
_bones = new List<OVRBone>();
|
|
Bones = _bones.AsReadOnly();
|
|
|
|
_bindPoses = new List<OVRBone>();
|
|
BindPoses = _bindPoses.AsReadOnly();
|
|
|
|
_capsules = new List<OVRBoneCapsule>();
|
|
Capsules = _capsules.AsReadOnly();
|
|
}
|
|
|
|
internal IOVRSkeletonDataProvider SearchSkeletonDataProvider()
|
|
{
|
|
var dataProviders = gameObject.GetComponentsInParent<IOVRSkeletonDataProvider>();
|
|
foreach (var dataProvider in dataProviders)
|
|
{
|
|
if (dataProvider.GetSkeletonType() == _skeletonType)
|
|
{
|
|
return dataProvider;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start this instance.
|
|
/// Initialize data structures.
|
|
/// </summary>
|
|
protected virtual void Start()
|
|
{
|
|
if (_dataProvider == null && _skeletonType == SkeletonType.Body)
|
|
{
|
|
Debug.LogWarning("OVRSkeleton and its subclasses requires OVRBody to function.");
|
|
}
|
|
|
|
if (ShouldInitialize())
|
|
{
|
|
Initialize();
|
|
}
|
|
}
|
|
|
|
private bool ShouldInitialize()
|
|
{
|
|
if (IsInitialized)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_dataProvider != null && !_dataProvider.enabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_skeletonType == SkeletonType.None)
|
|
{
|
|
return false;
|
|
}
|
|
else if (IsHandSkeleton(_skeletonType))
|
|
{
|
|
#if UNITY_EDITOR
|
|
return OVRInput.IsControllerConnected(OVRInput.Controller.Hands);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private void Initialize()
|
|
{
|
|
if (OVRPlugin.GetSkeleton2((OVRPlugin.SkeletonType)_skeletonType, ref _skeleton))
|
|
{
|
|
InitializeBones();
|
|
InitializeBindPose();
|
|
InitializeCapsules();
|
|
|
|
IsInitialized = true;
|
|
}
|
|
}
|
|
|
|
protected virtual Transform GetBoneTransform(BoneId boneId) => null;
|
|
|
|
protected virtual void InitializeBones()
|
|
{
|
|
bool flipX = IsHandSkeleton(_skeletonType);
|
|
|
|
if (!_bonesGO)
|
|
{
|
|
_bonesGO = new GameObject("Bones");
|
|
_bonesGO.transform.SetParent(transform, false);
|
|
_bonesGO.transform.localPosition = Vector3.zero;
|
|
_bonesGO.transform.localRotation = Quaternion.identity;
|
|
}
|
|
|
|
if (_bones == null || _bones.Count != _skeleton.NumBones)
|
|
{
|
|
_bones = new List<OVRBone>(new OVRBone[_skeleton.NumBones]);
|
|
Bones = _bones.AsReadOnly();
|
|
}
|
|
|
|
bool newBonesCreated = false;
|
|
// pre-populate bones list before attempting to apply bone hierarchy
|
|
for (int i = 0; i < _bones.Count; ++i)
|
|
{
|
|
OVRBone bone = _bones[i] ?? (_bones[i] = new OVRBone());
|
|
bone.Id = (OVRSkeleton.BoneId)_skeleton.Bones[i].Id;
|
|
bone.ParentBoneIndex = _skeleton.Bones[i].ParentBoneIndex;
|
|
Assert.IsTrue((int)bone.Id >= 0 && bone.Id <= BoneId.Max);
|
|
|
|
// don't create new bones each time; rely on
|
|
// pre-existing bone transforms.
|
|
if (bone.Transform == null)
|
|
{
|
|
newBonesCreated = true;
|
|
bone.Transform = GetBoneTransform(bone.Id);
|
|
if (bone.Transform == null)
|
|
{
|
|
bone.Transform = new GameObject(BoneLabelFromBoneId(_skeletonType, bone.Id)).transform;
|
|
}
|
|
}
|
|
|
|
// if allocated bone here before, make sure the name is correct.
|
|
if (GetBoneTransform(bone.Id) == null)
|
|
{
|
|
bone.Transform.name = BoneLabelFromBoneId(_skeletonType, bone.Id);
|
|
}
|
|
|
|
var pose = _skeleton.Bones[i].Pose;
|
|
|
|
if (_applyBoneTranslations)
|
|
{
|
|
bone.Transform.localPosition = flipX
|
|
? pose.Position.FromFlippedXVector3f()
|
|
: pose.Position.FromFlippedZVector3f();
|
|
}
|
|
|
|
bone.Transform.localRotation = flipX
|
|
? pose.Orientation.FromFlippedXQuatf()
|
|
: pose.Orientation.FromFlippedZQuatf();
|
|
}
|
|
|
|
if (newBonesCreated)
|
|
{
|
|
for (int i = 0; i < _bones.Count; ++i)
|
|
{
|
|
if (!IsValidBone((BoneId)_bones[i].ParentBoneIndex) ||
|
|
IsBodySkeleton(_skeletonType)) // Body bones are always in tracking space
|
|
{
|
|
_bones[i].Transform.SetParent(_bonesGO.transform, false);
|
|
}
|
|
else
|
|
{
|
|
_bones[i].Transform.SetParent(_bones[_bones[i].ParentBoneIndex].Transform, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitializeBindPose()
|
|
{
|
|
if (!_bindPosesGO)
|
|
{
|
|
_bindPosesGO = new GameObject("BindPoses");
|
|
_bindPosesGO.transform.SetParent(transform, false);
|
|
_bindPosesGO.transform.localPosition = Vector3.zero;
|
|
_bindPosesGO.transform.localRotation = Quaternion.identity;
|
|
}
|
|
|
|
if (_bindPoses == null || _bindPoses.Count != _bones.Count)
|
|
{
|
|
_bindPoses = new List<OVRBone>(new OVRBone[_bones.Count]);
|
|
BindPoses = _bindPoses.AsReadOnly();
|
|
}
|
|
|
|
// pre-populate bones list before attempting to apply bone hierarchy
|
|
for (int i = 0; i < _bindPoses.Count; ++i)
|
|
{
|
|
OVRBone bone = _bones[i];
|
|
OVRBone bindPoseBone = _bindPoses[i] ?? (_bindPoses[i] = new OVRBone());
|
|
bindPoseBone.Id = bone.Id;
|
|
bindPoseBone.ParentBoneIndex = bone.ParentBoneIndex;
|
|
|
|
Transform trans = bindPoseBone.Transform
|
|
? bindPoseBone.Transform
|
|
: (bindPoseBone.Transform =
|
|
new GameObject(BoneLabelFromBoneId(_skeletonType, bindPoseBone.Id)).transform);
|
|
trans.localPosition = bone.Transform.localPosition;
|
|
trans.localRotation = bone.Transform.localRotation;
|
|
}
|
|
|
|
for (int i = 0; i < _bindPoses.Count; ++i)
|
|
{
|
|
if (!IsValidBone((BoneId)_bindPoses[i].ParentBoneIndex) ||
|
|
IsBodySkeleton(_skeletonType)) // Body bones are always in tracking space
|
|
{
|
|
_bindPoses[i].Transform.SetParent(_bindPosesGO.transform, false);
|
|
}
|
|
else
|
|
{
|
|
_bindPoses[i].Transform.SetParent(_bindPoses[_bindPoses[i].ParentBoneIndex].Transform, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitializeCapsules()
|
|
{
|
|
bool flipX = IsHandSkeleton(_skeletonType);
|
|
|
|
if (_enablePhysicsCapsules)
|
|
{
|
|
if (!_capsulesGO)
|
|
{
|
|
_capsulesGO = new GameObject("Capsules");
|
|
_capsulesGO.transform.SetParent(transform, false);
|
|
_capsulesGO.transform.localPosition = Vector3.zero;
|
|
_capsulesGO.transform.localRotation = Quaternion.identity;
|
|
}
|
|
|
|
if (_capsules == null || _capsules.Count != _skeleton.NumBoneCapsules)
|
|
{
|
|
_capsules = new List<OVRBoneCapsule>(new OVRBoneCapsule[_skeleton.NumBoneCapsules]);
|
|
Capsules = _capsules.AsReadOnly();
|
|
}
|
|
|
|
for (int i = 0; i < _capsules.Count; ++i)
|
|
{
|
|
OVRBone bone = _bones[_skeleton.BoneCapsules[i].BoneIndex];
|
|
OVRBoneCapsule capsule = _capsules[i] ?? (_capsules[i] = new OVRBoneCapsule());
|
|
capsule.BoneIndex = _skeleton.BoneCapsules[i].BoneIndex;
|
|
|
|
if (capsule.CapsuleRigidbody == null)
|
|
{
|
|
capsule.CapsuleRigidbody =
|
|
new GameObject(BoneLabelFromBoneId(_skeletonType, bone.Id) + "_CapsuleRigidbody")
|
|
.AddComponent<Rigidbody>();
|
|
capsule.CapsuleRigidbody.mass = 1.0f;
|
|
capsule.CapsuleRigidbody.isKinematic = true;
|
|
capsule.CapsuleRigidbody.useGravity = false;
|
|
capsule.CapsuleRigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
|
|
}
|
|
|
|
GameObject rbGO = capsule.CapsuleRigidbody.gameObject;
|
|
rbGO.transform.SetParent(_capsulesGO.transform, false);
|
|
rbGO.transform.position = bone.Transform.position;
|
|
rbGO.transform.rotation = bone.Transform.rotation;
|
|
|
|
if (capsule.CapsuleCollider == null)
|
|
{
|
|
capsule.CapsuleCollider =
|
|
new GameObject(BoneLabelFromBoneId(_skeletonType, bone.Id) + "_CapsuleCollider")
|
|
.AddComponent<CapsuleCollider>();
|
|
capsule.CapsuleCollider.isTrigger = false;
|
|
}
|
|
|
|
var p0 = flipX
|
|
? _skeleton.BoneCapsules[i].StartPoint.FromFlippedXVector3f()
|
|
: _skeleton.BoneCapsules[i].StartPoint.FromFlippedZVector3f();
|
|
var p1 = flipX
|
|
? _skeleton.BoneCapsules[i].EndPoint.FromFlippedXVector3f()
|
|
: _skeleton.BoneCapsules[i].EndPoint.FromFlippedZVector3f();
|
|
var delta = p1 - p0;
|
|
var mag = delta.magnitude;
|
|
var rot = Quaternion.FromToRotation(Vector3.right, delta);
|
|
capsule.CapsuleCollider.radius = _skeleton.BoneCapsules[i].Radius;
|
|
capsule.CapsuleCollider.height = mag + _skeleton.BoneCapsules[i].Radius * 2.0f;
|
|
capsule.CapsuleCollider.direction = 0;
|
|
capsule.CapsuleCollider.center = Vector3.right * mag * 0.5f;
|
|
|
|
GameObject ccGO = capsule.CapsuleCollider.gameObject;
|
|
ccGO.transform.SetParent(rbGO.transform, false);
|
|
ccGO.transform.localPosition = p0;
|
|
ccGO.transform.localRotation = rot;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
UpdateSkeleton();
|
|
}
|
|
|
|
protected void UpdateSkeleton()
|
|
{
|
|
if (ShouldInitialize())
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
if (!IsInitialized || _dataProvider == null)
|
|
{
|
|
IsDataValid = false;
|
|
IsDataHighConfidence = false;
|
|
return;
|
|
}
|
|
|
|
var data = _dataProvider.GetSkeletonPoseData();
|
|
|
|
IsDataValid = data.IsDataValid;
|
|
|
|
if (!data.IsDataValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SkeletonChangedCount != data.SkeletonChangedCount)
|
|
{
|
|
SkeletonChangedCount = data.SkeletonChangedCount;
|
|
IsInitialized = false;
|
|
Initialize();
|
|
}
|
|
|
|
IsDataHighConfidence = data.IsDataHighConfidence;
|
|
|
|
if (_updateRootPose)
|
|
{
|
|
transform.localPosition = data.RootPose.Position.FromFlippedZVector3f();
|
|
transform.localRotation = data.RootPose.Orientation.FromFlippedZQuatf();
|
|
}
|
|
|
|
if (_updateRootScale)
|
|
{
|
|
transform.localScale = new Vector3(data.RootScale, data.RootScale, data.RootScale);
|
|
}
|
|
|
|
for (var i = 0; i < _bones.Count; ++i)
|
|
{
|
|
var boneTransform = _bones[i].Transform;
|
|
if (boneTransform == null) continue;
|
|
|
|
if (IsBodySkeleton(_skeletonType))
|
|
{
|
|
boneTransform.localPosition = data.BoneTranslations[i].FromFlippedZVector3f();
|
|
boneTransform.localRotation = data.BoneRotations[i].FromFlippedZQuatf();
|
|
}
|
|
else if (IsHandSkeleton(_skeletonType))
|
|
{
|
|
boneTransform.localRotation = data.BoneRotations[i].FromFlippedXQuatf();
|
|
|
|
if (_bones[i].Id == BoneId.Hand_WristRoot)
|
|
{
|
|
boneTransform.localRotation *= wristFixupRotation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boneTransform.localRotation = data.BoneRotations[i].FromFlippedZQuatf();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (!IsInitialized || _dataProvider == null)
|
|
{
|
|
IsDataValid = false;
|
|
IsDataHighConfidence = false;
|
|
|
|
return;
|
|
}
|
|
|
|
Update();
|
|
|
|
if (_enablePhysicsCapsules)
|
|
{
|
|
var data = _dataProvider.GetSkeletonPoseData();
|
|
|
|
IsDataValid = data.IsDataValid;
|
|
IsDataHighConfidence = data.IsDataHighConfidence;
|
|
|
|
for (int i = 0; i < _capsules.Count; ++i)
|
|
{
|
|
OVRBoneCapsule capsule = _capsules[i];
|
|
var capsuleGO = capsule.CapsuleRigidbody.gameObject;
|
|
|
|
if (data.IsDataValid && data.IsDataHighConfidence)
|
|
{
|
|
Transform bone = _bones[(int)capsule.BoneIndex].Transform;
|
|
|
|
if (capsuleGO.activeSelf)
|
|
{
|
|
capsule.CapsuleRigidbody.MovePosition(bone.position);
|
|
capsule.CapsuleRigidbody.MoveRotation(bone.rotation);
|
|
}
|
|
else
|
|
{
|
|
capsuleGO.SetActive(true);
|
|
capsule.CapsuleRigidbody.position = bone.position;
|
|
capsule.CapsuleRigidbody.rotation = bone.rotation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (capsuleGO.activeSelf)
|
|
{
|
|
capsuleGO.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public BoneId GetCurrentStartBoneId()
|
|
{
|
|
switch (_skeletonType)
|
|
{
|
|
case SkeletonType.HandLeft:
|
|
case SkeletonType.HandRight:
|
|
return BoneId.Hand_Start;
|
|
case SkeletonType.Body:
|
|
return BoneId.Body_Start;
|
|
case SkeletonType.None:
|
|
default:
|
|
return BoneId.Invalid;
|
|
}
|
|
}
|
|
|
|
public BoneId GetCurrentEndBoneId()
|
|
{
|
|
switch (_skeletonType)
|
|
{
|
|
case SkeletonType.HandLeft:
|
|
case SkeletonType.HandRight:
|
|
return BoneId.Hand_End;
|
|
case SkeletonType.Body:
|
|
return BoneId.Body_End;
|
|
case SkeletonType.None:
|
|
default:
|
|
return BoneId.Invalid;
|
|
}
|
|
}
|
|
|
|
private BoneId GetCurrentMaxSkinnableBoneId()
|
|
{
|
|
switch (_skeletonType)
|
|
{
|
|
case SkeletonType.HandLeft:
|
|
case SkeletonType.HandRight:
|
|
return BoneId.Hand_MaxSkinnable;
|
|
case SkeletonType.Body:
|
|
return BoneId.Body_End;
|
|
case SkeletonType.None:
|
|
default:
|
|
return BoneId.Invalid;
|
|
}
|
|
}
|
|
|
|
public int GetCurrentNumBones()
|
|
{
|
|
switch (_skeletonType)
|
|
{
|
|
case SkeletonType.HandLeft:
|
|
case SkeletonType.HandRight:
|
|
case SkeletonType.Body:
|
|
return GetCurrentEndBoneId() - GetCurrentStartBoneId();
|
|
case SkeletonType.None:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public int GetCurrentNumSkinnableBones()
|
|
{
|
|
switch (_skeletonType)
|
|
{
|
|
case SkeletonType.HandLeft:
|
|
case SkeletonType.HandRight:
|
|
case SkeletonType.Body:
|
|
return GetCurrentMaxSkinnableBoneId() - GetCurrentStartBoneId();
|
|
case SkeletonType.None:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// force aliased enum values to the more appropriate value
|
|
public static string BoneLabelFromBoneId(OVRSkeleton.SkeletonType skeletonType, BoneId boneId)
|
|
{
|
|
if (skeletonType == OVRSkeleton.SkeletonType.Body)
|
|
{
|
|
switch (boneId)
|
|
{
|
|
case BoneId.Body_Root:
|
|
return "Body_Root";
|
|
case BoneId.Body_Hips:
|
|
return "Body_Hips";
|
|
case BoneId.Body_SpineLower:
|
|
return "Body_SpineLower";
|
|
case BoneId.Body_SpineMiddle:
|
|
return "Body_SpineMiddle";
|
|
case BoneId.Body_SpineUpper:
|
|
return "Body_SpineUpper";
|
|
case BoneId.Body_Chest:
|
|
return "Body_Chest";
|
|
case BoneId.Body_Neck:
|
|
return "Body_Neck";
|
|
case BoneId.Body_Head:
|
|
return "Body_Head";
|
|
case BoneId.Body_LeftShoulder:
|
|
return "Body_LeftShoulder";
|
|
case BoneId.Body_LeftScapula:
|
|
return "Body_LeftScapula";
|
|
case BoneId.Body_LeftArmUpper:
|
|
return "Body_LeftArmUpper";
|
|
case BoneId.Body_LeftArmLower:
|
|
return "Body_LeftArmLower";
|
|
case BoneId.Body_LeftHandWristTwist:
|
|
return "Body_LeftHandWristTwist";
|
|
case BoneId.Body_RightShoulder:
|
|
return "Body_RightShoulder";
|
|
case BoneId.Body_RightScapula:
|
|
return "Body_RightScapula";
|
|
case BoneId.Body_RightArmUpper:
|
|
return "Body_RightArmUpper";
|
|
case BoneId.Body_RightArmLower:
|
|
return "Body_RightArmLower";
|
|
case BoneId.Body_RightHandWristTwist:
|
|
return "Body_RightHandWristTwist";
|
|
case BoneId.Body_LeftHandPalm:
|
|
return "Body_LeftHandPalm";
|
|
case BoneId.Body_LeftHandWrist:
|
|
return "Body_LeftHandWrist";
|
|
case BoneId.Body_LeftHandThumbMetacarpal:
|
|
return "Body_LeftHandThumbMetacarpal";
|
|
case BoneId.Body_LeftHandThumbProximal:
|
|
return "Body_LeftHandThumbProximal";
|
|
case BoneId.Body_LeftHandThumbDistal:
|
|
return "Body_LeftHandThumbDistal";
|
|
case BoneId.Body_LeftHandThumbTip:
|
|
return "Body_LeftHandThumbTip";
|
|
case BoneId.Body_LeftHandIndexMetacarpal:
|
|
return "Body_LeftHandIndexMetacarpal";
|
|
case BoneId.Body_LeftHandIndexProximal:
|
|
return "Body_LeftHandIndexProximal";
|
|
case BoneId.Body_LeftHandIndexIntermediate:
|
|
return "Body_LeftHandIndexIntermediate";
|
|
case BoneId.Body_LeftHandIndexDistal:
|
|
return "Body_LeftHandIndexDistal";
|
|
case BoneId.Body_LeftHandIndexTip:
|
|
return "Body_LeftHandIndexTip";
|
|
case BoneId.Body_LeftHandMiddleMetacarpal:
|
|
return "Body_LeftHandMiddleMetacarpal";
|
|
case BoneId.Body_LeftHandMiddleProximal:
|
|
return "Body_LeftHandMiddleProximal";
|
|
case BoneId.Body_LeftHandMiddleIntermediate:
|
|
return "Body_LeftHandMiddleIntermediate";
|
|
case BoneId.Body_LeftHandMiddleDistal:
|
|
return "Body_LeftHandMiddleDistal";
|
|
case BoneId.Body_LeftHandMiddleTip:
|
|
return "Body_LeftHandMiddleTip";
|
|
case BoneId.Body_LeftHandRingMetacarpal:
|
|
return "Body_LeftHandRingMetacarpal";
|
|
case BoneId.Body_LeftHandRingProximal:
|
|
return "Body_LeftHandRingProximal";
|
|
case BoneId.Body_LeftHandRingIntermediate:
|
|
return "Body_LeftHandRingIntermediate";
|
|
case BoneId.Body_LeftHandRingDistal:
|
|
return "Body_LeftHandRingDistal";
|
|
case BoneId.Body_LeftHandRingTip:
|
|
return "Body_LeftHandRingTip";
|
|
case BoneId.Body_LeftHandLittleMetacarpal:
|
|
return "Body_LeftHandLittleMetacarpal";
|
|
case BoneId.Body_LeftHandLittleProximal:
|
|
return "Body_LeftHandLittleProximal";
|
|
case BoneId.Body_LeftHandLittleIntermediate:
|
|
return "Body_LeftHandLittleIntermediate";
|
|
case BoneId.Body_LeftHandLittleDistal:
|
|
return "Body_LeftHandLittleDistal";
|
|
case BoneId.Body_LeftHandLittleTip:
|
|
return "Body_LeftHandLittleTip";
|
|
case BoneId.Body_RightHandPalm:
|
|
return "Body_RightHandPalm";
|
|
case BoneId.Body_RightHandWrist:
|
|
return "Body_RightHandWrist";
|
|
case BoneId.Body_RightHandThumbMetacarpal:
|
|
return "Body_RightHandThumbMetacarpal";
|
|
case BoneId.Body_RightHandThumbProximal:
|
|
return "Body_RightHandThumbProximal";
|
|
case BoneId.Body_RightHandThumbDistal:
|
|
return "Body_RightHandThumbDistal";
|
|
case BoneId.Body_RightHandThumbTip:
|
|
return "Body_RightHandThumbTip";
|
|
case BoneId.Body_RightHandIndexMetacarpal:
|
|
return "Body_RightHandIndexMetacarpal";
|
|
case BoneId.Body_RightHandIndexProximal:
|
|
return "Body_RightHandIndexProximal";
|
|
case BoneId.Body_RightHandIndexIntermediate:
|
|
return "Body_RightHandIndexIntermediate";
|
|
case BoneId.Body_RightHandIndexDistal:
|
|
return "Body_RightHandIndexDistal";
|
|
case BoneId.Body_RightHandIndexTip:
|
|
return "Body_RightHandIndexTip";
|
|
case BoneId.Body_RightHandMiddleMetacarpal:
|
|
return "Body_RightHandMiddleMetacarpal";
|
|
case BoneId.Body_RightHandMiddleProximal:
|
|
return "Body_RightHandMiddleProximal";
|
|
case BoneId.Body_RightHandMiddleIntermediate:
|
|
return "Body_RightHandMiddleIntermediate";
|
|
case BoneId.Body_RightHandMiddleDistal:
|
|
return "Body_RightHandMiddleDistal";
|
|
case BoneId.Body_RightHandMiddleTip:
|
|
return "Body_RightHandMiddleTip";
|
|
case BoneId.Body_RightHandRingMetacarpal:
|
|
return "Body_RightHandRingMetacarpal";
|
|
case BoneId.Body_RightHandRingProximal:
|
|
return "Body_RightHandRingProximal";
|
|
case BoneId.Body_RightHandRingIntermediate:
|
|
return "Body_RightHandRingIntermediate";
|
|
case BoneId.Body_RightHandRingDistal:
|
|
return "Body_RightHandRingDistal";
|
|
case BoneId.Body_RightHandRingTip:
|
|
return "Body_RightHandRingTip";
|
|
case BoneId.Body_RightHandLittleMetacarpal:
|
|
return "Body_RightHandLittleMetacarpal";
|
|
case BoneId.Body_RightHandLittleProximal:
|
|
return "Body_RightHandLittleProximal";
|
|
case BoneId.Body_RightHandLittleIntermediate:
|
|
return "Body_RightHandLittleIntermediate";
|
|
case BoneId.Body_RightHandLittleDistal:
|
|
return "Body_RightHandLittleDistal";
|
|
case BoneId.Body_RightHandLittleTip:
|
|
return "Body_RightHandLittleTip";
|
|
default:
|
|
return "Body_Unknown";
|
|
}
|
|
}
|
|
else if (IsHandSkeleton(skeletonType))
|
|
{
|
|
switch (boneId)
|
|
{
|
|
case OVRSkeleton.BoneId.Hand_WristRoot:
|
|
return "Hand_WristRoot";
|
|
case OVRSkeleton.BoneId.Hand_ForearmStub:
|
|
return "Hand_ForearmStub";
|
|
case OVRSkeleton.BoneId.Hand_Thumb0:
|
|
return "Hand_Thumb0";
|
|
case OVRSkeleton.BoneId.Hand_Thumb1:
|
|
return "Hand_Thumb1";
|
|
case OVRSkeleton.BoneId.Hand_Thumb2:
|
|
return "Hand_Thumb2";
|
|
case OVRSkeleton.BoneId.Hand_Thumb3:
|
|
return "Hand_Thumb3";
|
|
case OVRSkeleton.BoneId.Hand_Index1:
|
|
return "Hand_Index1";
|
|
case OVRSkeleton.BoneId.Hand_Index2:
|
|
return "Hand_Index2";
|
|
case OVRSkeleton.BoneId.Hand_Index3:
|
|
return "Hand_Index3";
|
|
case OVRSkeleton.BoneId.Hand_Middle1:
|
|
return "Hand_Middle1";
|
|
case OVRSkeleton.BoneId.Hand_Middle2:
|
|
return "Hand_Middle2";
|
|
case OVRSkeleton.BoneId.Hand_Middle3:
|
|
return "Hand_Middle3";
|
|
case OVRSkeleton.BoneId.Hand_Ring1:
|
|
return "Hand_Ring1";
|
|
case OVRSkeleton.BoneId.Hand_Ring2:
|
|
return "Hand_Ring2";
|
|
case OVRSkeleton.BoneId.Hand_Ring3:
|
|
return "Hand_Ring3";
|
|
case OVRSkeleton.BoneId.Hand_Pinky0:
|
|
return "Hand_Pinky0";
|
|
case OVRSkeleton.BoneId.Hand_Pinky1:
|
|
return "Hand_Pinky1";
|
|
case OVRSkeleton.BoneId.Hand_Pinky2:
|
|
return "Hand_Pinky2";
|
|
case OVRSkeleton.BoneId.Hand_Pinky3:
|
|
return "Hand_Pinky3";
|
|
case OVRSkeleton.BoneId.Hand_ThumbTip:
|
|
return "Hand_ThumbTip";
|
|
case OVRSkeleton.BoneId.Hand_IndexTip:
|
|
return "Hand_IndexTip";
|
|
case OVRSkeleton.BoneId.Hand_MiddleTip:
|
|
return "Hand_MiddleTip";
|
|
case OVRSkeleton.BoneId.Hand_RingTip:
|
|
return "Hand_RingTip";
|
|
case OVRSkeleton.BoneId.Hand_PinkyTip:
|
|
return "Hand_PinkyTip";
|
|
default:
|
|
return "Hand_Unknown";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return "Skeleton_Unknown";
|
|
}
|
|
}
|
|
|
|
internal static bool IsBodySkeleton(SkeletonType type) => type == SkeletonType.Body;
|
|
private static bool IsHandSkeleton(SkeletonType type) =>
|
|
type == SkeletonType.HandLeft || type == SkeletonType.HandRight;
|
|
}
|
|
|
|
public class OVRBone
|
|
{
|
|
public OVRSkeleton.BoneId Id { get; set; }
|
|
public short ParentBoneIndex { get; set; }
|
|
public Transform Transform { get; set; }
|
|
|
|
public OVRBone()
|
|
{
|
|
}
|
|
|
|
public OVRBone(OVRSkeleton.BoneId id, short parentBoneIndex, Transform trans)
|
|
{
|
|
Id = id;
|
|
ParentBoneIndex = parentBoneIndex;
|
|
Transform = trans;
|
|
}
|
|
}
|
|
|
|
public class OVRBoneCapsule
|
|
{
|
|
public short BoneIndex { get; set; }
|
|
public Rigidbody CapsuleRigidbody { get; set; }
|
|
public CapsuleCollider CapsuleCollider { get; set; }
|
|
|
|
public OVRBoneCapsule()
|
|
{
|
|
}
|
|
|
|
public OVRBoneCapsule(short boneIndex, Rigidbody capsuleRigidBody, CapsuleCollider capsuleCollider)
|
|
{
|
|
BoneIndex = boneIndex;
|
|
CapsuleRigidbody = capsuleRigidBody;
|
|
CapsuleCollider = capsuleCollider;
|
|
}
|
|
}
|