Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

479 lines
15 KiB

/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus Utilities SDK License Version 1.31 (the "License"); you may not use
the Utilities 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/utilities-1.31
Unless required by applicable law or agreed to in writing, the Utilities 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;
using System.Collections.Generic;
using UnityEngine;
[DefaultExecutionOrder(-80)]
public class OVRSkeleton : MonoBehaviour
{
public interface IOVRSkeletonDataProvider
{
SkeletonType GetSkeletonType();
SkeletonPoseData GetSkeletonPoseData();
}
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 enum SkeletonType
{
None = OVRPlugin.SkeletonType.None,
HandLeft = OVRPlugin.SkeletonType.HandLeft,
HandRight = OVRPlugin.SkeletonType.HandRight,
}
public enum BoneId
{
Invalid = OVRPlugin.BoneId.Invalid,
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,
// add new bones here
Max = OVRPlugin.BoneId.Max
}
[SerializeField]
private SkeletonType _skeletonType = SkeletonType.None;
[SerializeField]
private IOVRSkeletonDataProvider _dataProvider;
[SerializeField]
private bool _updateRootPose = false;
[SerializeField]
private bool _updateRootScale = false;
[SerializeField]
private bool _enablePhysicsCapsules = false;
private GameObject _bonesGO;
private GameObject _bindPosesGO;
private GameObject _capsulesGO;
protected List<OVRBone> _bones;
private List<OVRBone> _bindPoses;
private List<OVRBoneCapsule> _capsules;
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; }
private void Awake()
{
if (_dataProvider == null)
{
_dataProvider = GetComponent<IOVRSkeletonDataProvider>();
}
_bones = new List<OVRBone>();
Bones = _bones.AsReadOnly();
_bindPoses = new List<OVRBone>();
BindPoses = _bindPoses.AsReadOnly();
_capsules = new List<OVRBoneCapsule>();
Capsules = _capsules.AsReadOnly();
}
private void Start()
{
if (_skeletonType != SkeletonType.None)
{
Initialize();
}
}
private void Initialize()
{
var skeleton = new OVRPlugin.Skeleton();
if (OVRPlugin.GetSkeleton((OVRPlugin.SkeletonType)_skeletonType, out skeleton))
{
InitializeBones(skeleton);
InitializeBindPose(skeleton);
InitializeCapsules(skeleton);
IsInitialized = true;
}
}
virtual protected void InitializeBones(OVRPlugin.Skeleton skeleton)
{
_bones = new List<OVRBone>(new OVRBone[skeleton.NumBones]);
Bones = _bones.AsReadOnly();
if (!_bonesGO)
{
_bonesGO = new GameObject("Bones");
_bonesGO.transform.SetParent(transform, false);
_bonesGO.transform.localPosition = Vector3.zero;
_bonesGO.transform.localRotation = Quaternion.identity;
}
// pre-populate bones list before attempting to apply bone hierarchy
for (int i = 0; i < skeleton.NumBones; ++i)
{
BoneId id = (OVRSkeleton.BoneId)skeleton.Bones[i].Id;
short parentIdx = skeleton.Bones[i].ParentBoneIndex;
Vector3 pos = skeleton.Bones[i].Pose.Position.FromFlippedXVector3f();
Quaternion rot = skeleton.Bones[i].Pose.Orientation.FromFlippedXQuatf();
var boneGO = new GameObject(id.ToString());
boneGO.transform.localPosition = pos;
boneGO.transform.localRotation = rot;
_bones[i] = new OVRBone(id, parentIdx, boneGO.transform);
}
for (int i = 0; i < skeleton.NumBones; ++i)
{
if (((OVRPlugin.BoneId)skeleton.Bones[i].ParentBoneIndex) == OVRPlugin.BoneId.Invalid)
{
_bones[i].Transform.SetParent(_bonesGO.transform, false);
}
else
{
_bones[i].Transform.SetParent(_bones[_bones[i].ParentBoneIndex].Transform, false);
}
}
}
private void InitializeBindPose(OVRPlugin.Skeleton skeleton)
{
_bindPoses = new List<OVRBone>(new OVRBone[skeleton.NumBones]);
BindPoses = _bindPoses.AsReadOnly();
if (!_bindPosesGO)
{
_bindPosesGO = new GameObject("BindPoses");
_bindPosesGO.transform.SetParent(transform, false);
_bindPosesGO.transform.localPosition = Vector3.zero;
_bindPosesGO.transform.localRotation = Quaternion.identity;
}
for (int i = 0; i < skeleton.NumBones; ++i)
{
BoneId id = (OVRSkeleton.BoneId)skeleton.Bones[i].Id;
short parentIdx = skeleton.Bones[i].ParentBoneIndex;
var bindPoseGO = new GameObject(id.ToString());
OVRBone bone = _bones[i];
if (bone.Transform != null)
{
bindPoseGO.transform.localPosition = bone.Transform.localPosition;
bindPoseGO.transform.localRotation = bone.Transform.localRotation;
}
_bindPoses[i] = new OVRBone(id, parentIdx, bindPoseGO.transform);
}
for (int i = 0; i < skeleton.NumBones; ++i)
{
if (((OVRPlugin.BoneId)skeleton.Bones[i].ParentBoneIndex) == OVRPlugin.BoneId.Invalid)
{
_bindPoses[i].Transform.SetParent(_bindPosesGO.transform, false);
}
else
{
_bindPoses[i].Transform.SetParent(_bindPoses[_bones[i].ParentBoneIndex].Transform, false);
}
}
}
private void InitializeCapsules(OVRPlugin.Skeleton skeleton)
{
if (_enablePhysicsCapsules)
{
_capsules = new List<OVRBoneCapsule>(new OVRBoneCapsule[skeleton.NumBoneCapsules]);
Capsules = _capsules.AsReadOnly();
if (!_capsulesGO)
{
_capsulesGO = new GameObject("Capsules");
_capsulesGO.transform.SetParent(transform, false);
_capsulesGO.transform.localPosition = Vector3.zero;
_capsulesGO.transform.localRotation = Quaternion.identity;
}
_capsules = new List<OVRBoneCapsule>(new OVRBoneCapsule[skeleton.NumBoneCapsules]);
Capsules = _capsules.AsReadOnly();
for (int i = 0; i < skeleton.NumBoneCapsules; ++i)
{
var capsule = skeleton.BoneCapsules[i];
Transform bone = Bones[capsule.BoneIndex].Transform;
var capsuleRigidBodyGO = new GameObject((_bones[capsule.BoneIndex].Id).ToString() + "_CapsuleRigidBody");
capsuleRigidBodyGO.transform.SetParent(_capsulesGO.transform, false);
capsuleRigidBodyGO.transform.position = bone.position;
capsuleRigidBodyGO.transform.rotation = bone.rotation;
var capsuleRigidBody = capsuleRigidBodyGO.AddComponent<Rigidbody>();
capsuleRigidBody.mass = 1.0f;
capsuleRigidBody.isKinematic = true;
capsuleRigidBody.useGravity = false;
#if UNITY_2018_3_OR_NEWER
capsuleRigidBody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
#else
capsuleRigidBody.collisionDetectionMode = CollisionDetectionMode.Continuous;
#endif
var capsuleColliderGO = new GameObject((_bones[capsule.BoneIndex].Id).ToString() + "_CapsuleCollider");
capsuleColliderGO.transform.SetParent(capsuleRigidBodyGO.transform, false);
var capsuleCollider = capsuleColliderGO.AddComponent<CapsuleCollider>();
var p0 = capsule.Points[0].FromFlippedXVector3f();
var p1 = capsule.Points[1].FromFlippedXVector3f();
var delta = p1 - p0;
var mag = delta.magnitude;
var rot = Quaternion.FromToRotation(Vector3.right, delta);
capsuleCollider.radius = capsule.Radius;
capsuleCollider.height = mag + capsule.Radius * 2.0f;
capsuleCollider.isTrigger = false;
capsuleCollider.direction = 0;
capsuleColliderGO.transform.localPosition = p0;
capsuleColliderGO.transform.localRotation = rot;
capsuleCollider.center = Vector3.right * mag * 0.5f;
_capsules[i] = new OVRBoneCapsule(capsule.BoneIndex, capsuleRigidBody, capsuleCollider);
}
}
}
private void Update()
{
if (!IsInitialized || _dataProvider == null)
{
IsDataValid = false;
IsDataHighConfidence = false;
return;
}
var data = _dataProvider.GetSkeletonPoseData();
IsDataValid = data.IsDataValid;
if (data.IsDataValid)
{
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)
{
if (_bones[i].Transform != null)
{
_bones[i].Transform.localRotation = data.BoneRotations[i].FromFlippedXQuatf();
if (_bones[i].Id == BoneId.Hand_WristRoot)
{
_bones[i].Transform.localRotation *= wristFixupRotation;
}
}
}
}
}
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.None:
default:
return BoneId.Invalid;
}
}
public BoneId GetCurrentEndBoneId()
{
switch (_skeletonType)
{
case SkeletonType.HandLeft:
case SkeletonType.HandRight:
return BoneId.Hand_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.None:
default:
return BoneId.Invalid;
}
}
public int GetCurrentNumBones()
{
switch (_skeletonType)
{
case SkeletonType.HandLeft:
case SkeletonType.HandRight:
return GetCurrentEndBoneId() - GetCurrentStartBoneId();
case SkeletonType.None:
default:
return 0;
}
}
public int GetCurrentNumSkinnableBones()
{
switch (_skeletonType)
{
case SkeletonType.HandLeft:
case SkeletonType.HandRight:
return GetCurrentMaxSkinnableBoneId() - GetCurrentStartBoneId();
case SkeletonType.None:
default:
return 0;
}
}
}
public class OVRBone
{
public OVRSkeleton.BoneId Id { get; private set; }
public short ParentBoneIndex { get; private set; }
public Transform Transform { get; private set; }
public OVRBone(OVRSkeleton.BoneId id, short parentBoneIndex, Transform trans)
{
Id = id;
ParentBoneIndex = parentBoneIndex;
Transform = trans;
}
}
public class OVRBoneCapsule
{
public short BoneIndex { get; private set; }
public Rigidbody CapsuleRigidbody { get; private set; }
public CapsuleCollider CapsuleCollider { get; private set; }
public OVRBoneCapsule(short boneIndex, Rigidbody capsuleRigidBody, CapsuleCollider capsuleCollider)
{
BoneIndex = boneIndex;
CapsuleRigidbody = capsuleRigidBody;
CapsuleCollider = capsuleCollider;
}
}