Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections;
- using System.Collections.Generic;
- using System;
- using UnityEngine;
- using Oculus.Avatar;
- // Smoothly tween between the "real" hand pose that comes out of the
- // Oculus Avatar SDK and a custom grip pose.
- //
- // ## Why?
- // The SDK doesn't natively do this so we have to do it manually here.
- // We can't just naively lerp from the current hand pose to our target because
- // ovr doesn't update bones unless the C layer says they changed - so there's
- // no consistent FROM state, and thus no real control over how far through
- // the lerp you want to be
- // (i.e. you can't say "lerp 50% please" because the next frame the from
- // state will have moved to 50% so the request for 50% is now really for 75%
- // and so you'll always tend towards the TO state no matter how much you
- // actually want to lerp.)
- //
- // ## Overview
- // Roughtly we:
- // - Make a copy of the hand skeleton
- // - When the OvrAvatar updates, update our copy to match.
- // - Use our copy as the FROM version in a lerp TO our custom pose
- // - Set the rendered skeleton to whatever our lerped versions are
- //
- // ## TODO / Known Issues
- // - Not tested with OvrAvatarSkinnedMeshRenderPBSComponent
- // - Multiple target poses or some sort of blend tree would be nice.
- // - Limit tweens to specific fingers to allow it to play nice with other hand
- // poses (e.g. index finger trigger should only effect index finger).
- //
- // ## Contact
- // Any questions or bug reports - email me!
- // Chris McLaughlin - chris@vitei.com
- public class OVRAvatarHandPoseOverrider : MonoBehaviour {
- [Range(0,1)]
- public float m_tweenAmount;
- public OvrAvatar m_avatar;
- public enum Handedness { Right, Left }
- public Handedness m_hand;
- public Transform m_gripPose;
- OvrAvatarHand m_ovrHand;
- OvrAvatarRenderComponent m_ovrHandRenderer;
- Transform[] m_bonesTruePose;
- IntPtr m_ovrRenderPart = IntPtr.Zero;
- [System.Serializable]
- public class BoneSet {
- public Transform m_rendered;
- public Transform m_base;
- public Transform m_target;
- }
- List<BoneSet> m_boneSets;
- #if UNITY_EDITOR
- Transform m_lastGripPose;
- private void OnValidate() {
- // if the game is runnnign and the user changes the pose in editor,
- // then we want to make sure to update everything so that's what
- // they see!
- if(Application.isPlaying && m_lastGripPose != m_gripPose) {
- SetupBoneSets();
- }
- m_lastGripPose = m_gripPose;
- }
- #endif
- protected virtual void Start() {
- // Get all the relevant components that we're gonna need later on
- this.Fetch(out m_avatar);
- m_ovrHand = (m_hand == Handedness.Left) ? m_avatar.HandLeft : m_avatar.HandRight;
- }
- protected virtual void Update() {
- // we need a renderpart before we can do anything else. This doesn't
- // exist until the SDK tries to render the hand, so we have to keep
- // testing here in update until we can get it.
- if (m_ovrRenderPart == IntPtr.Zero){
- GetNativeRenderPartPtr();
- return;
- }
- // we need the bonesets setup otherwise there's nothing to actually do
- // the work on. Again we need the rendered data before we can do
- // this setup.
- if (m_boneSets == null) {
- SetupBoneSets();
- return;
- }
- // Use the renderpart to look into the avatar sdk adn find out what bones have changed.
- // Update our TRUE bones to whatever the sdk says they should be
- // We can't use the meshrenderer's bones as the FROM in our lerp
- // because the SDK doesn't update them unless _it_ thinks that they
- // changed.
- ulong dirtyJoints = CAPI.ovrAvatarSkinnedMeshRender_GetDirtyJoints(m_ovrRenderPart);
- for (UInt32 i = 0; i < m_bonesTruePose.Length; i++) {
- UInt64 dirtyMask = (ulong)1 << (int)i;
- // We need to make sure that we fully update the initial position of
- // Skinned mesh renderers, then, thereafter, we can only update dirty joints
- if ((dirtyMask & dirtyJoints) != 0) {
- //This joint is dirty and needs to be updated
- Transform targetBone = m_ovrHandRenderer.bones[i];
- if (m_bonesTruePose != null && m_bonesTruePose[i] != null) {
- m_bonesTruePose[i].localPosition = targetBone.localPosition;
- m_bonesTruePose[i].localRotation = targetBone.localRotation;
- m_bonesTruePose[i].localScale = targetBone.localScale;
- }
- }
- }
- // Loop through all our bones based on how much the trigger is pulled and put them into the right positions!
- for (int i = 0; i < m_boneSets.Count; i++) {
- // Not lerping POSITION or SCALE because hand bones don't need to, but if you want to do that for some reason, then add it here!
- // You could use a SLERP here, but I didn't see any difference in action, so went with the cheaper LERP instead.
- m_boneSets[i].m_rendered.localRotation = Quaternion.Lerp(m_boneSets[i].m_base.localRotation, m_boneSets[i].m_target.localRotation, m_tweenAmount);
- }
- }
- //-------------------------------------------------------
- // Dive into the avatar sdk and pull out a reference to what I guess is
- // the thing that renders a hand. we can then use it to look up how
- // dirty it is later on.
- void GetNativeRenderPartPtr() {
- if (m_avatar.sdkAvatar != IntPtr.Zero) { // null check, otherwise the next line will hard crash the game.
- UInt32 componentCount = CAPI.ovrAvatarComponent_Count(m_avatar.sdkAvatar);
- for (UInt32 i = 0; i < componentCount; i++) {
- IntPtr ptr = CAPI.ovrAvatarComponent_Get_Native(m_avatar.sdkAvatar, i);
- ovrAvatarComponent component = (ovrAvatarComponent)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, typeof(ovrAvatarComponent));
- if (component.name == (m_hand == Handedness.Left ? "hand_left" : "hand_right")) {
- m_ovrRenderPart = OvrAvatar.GetRenderPart(component, 0); //magic 0
- }
- }
- }
- }
- // Make a copy of all the bones in the OvrAvatarSkinnedMeshRenderComponent, these will represent the position as the avatar sdk thinks is should be
- void CreateTrueBoneArray() {
- Transform[] bones = m_ovrHandRenderer.bones;
- m_bonesTruePose = new Transform[bones.Length];
- for (int i = 0; i < bones.Length; i++) {
- m_bonesTruePose[i] = new GameObject(bones[i].name + " (true pose)").transform;
- }
- List<Transform> bonesAsList = new List<Transform>(bones);
- for (int i = 0; i < bones.Length; i++) {
- int parentIdx = bonesAsList.IndexOf(bones[i].parent);
- if (parentIdx >= 0) {
- m_bonesTruePose[i].parent = m_bonesTruePose[parentIdx];
- }
- else {
- m_bonesTruePose[i].parent = bones[i].parent;
- }
- m_bonesTruePose[i].transform.localPosition = bones[i].transform.localPosition;
- m_bonesTruePose[i].transform.localRotation = bones[i].transform.localRotation;
- m_bonesTruePose[i].transform.localScale = bones[i].transform.localScale;
- }
- }
- // Make sets of corresponding bones that we'll lerp between.
- void SetupBoneSets() {
- if(m_ovrHand == null) {
- return;
- }
- // Get the skinnedmeshrenderer because it has all the bone data we need
- m_ovrHandRenderer = m_ovrHand.GetComponentInChildren<OvrAvatarRenderComponent>();
- if (m_ovrHandRenderer == null) {
- return;
- }
- // Make our copy of the bones to use as the TRUTH
- CreateTrueBoneArray();
- // Make our bonesets that allow us to lerp between TRUTH and m_gripPose;
- m_boneSets = new List<BoneSet>();
- for (int i = 0; i < m_ovrHandRenderer.bones.Length; i++) {
- BoneSet bs = new BoneSet();
- bs.m_rendered = m_ovrHandRenderer.bones[i];
- bs.m_base = m_bonesTruePose[i];
- bs.m_target = m_gripPose.FindChildRecursive(m_ovrHandRenderer.bones[i].name);
- // don't add any that have incomplete data as it will cause errors later on ehwn we're interpolating
- if (bs.m_rendered != null && bs.m_base != null && bs.m_target != null) {
- m_boneSets.Add(bs);
- }
- }
- }
- }
Add Comment
Please, Sign In to add comment