123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- //======= Copyright (c) Valve Corporation, All rights reserved. ===============
- using System;
- using System.Collections;
- using UnityEngine;
- using Valve.VR;
- using System.Collections.Generic;
- using System.Linq;
- namespace Valve.VR
- {
- public class SteamVR_Skeleton_Poser : MonoBehaviour
- {
- #region Editor Storage
- public bool poseEditorExpanded = true;
- public bool blendEditorExpanded = true;
- public string[] poseNames;
- #endregion
- public GameObject overridePreviewLeftHandPrefab;
- public GameObject overridePreviewRightHandPrefab;
- public SteamVR_Skeleton_Pose skeletonMainPose;
- public List<SteamVR_Skeleton_Pose> skeletonAdditionalPoses = new List<SteamVR_Skeleton_Pose>();
- [SerializeField]
- protected bool showLeftPreview = false;
- [SerializeField]
- protected bool showRightPreview = true; //show the right hand by default
- [SerializeField]
- protected GameObject previewLeftInstance;
- [SerializeField]
- protected GameObject previewRightInstance;
- [SerializeField]
- protected int previewPoseSelection = 0;
- public int blendPoseCount { get { return blendPoses.Length; } }
- public List<PoseBlendingBehaviour> blendingBehaviours = new List<PoseBlendingBehaviour>();
- public SteamVR_Skeleton_PoseSnapshot blendedSnapshotL;
- public SteamVR_Skeleton_PoseSnapshot blendedSnapshotR;
- private SkeletonBlendablePose[] blendPoses;
- private int boneCount;
- private bool poseUpdatedThisFrame;
- public float scale;
- protected void Awake()
- {
- if (previewLeftInstance != null)
- DestroyImmediate(previewLeftInstance);
- if (previewRightInstance != null)
- DestroyImmediate(previewRightInstance);
- blendPoses = new SkeletonBlendablePose[skeletonAdditionalPoses.Count + 1];
- for (int i = 0; i < blendPoseCount; i++)
- {
- blendPoses[i] = new SkeletonBlendablePose(GetPoseByIndex(i));
- blendPoses[i].PoseToSnapshots();
- }
- boneCount = skeletonMainPose.leftHand.bonePositions.Length;
- // NOTE: Is there a better way to get the bone count? idk
- blendedSnapshotL = new SteamVR_Skeleton_PoseSnapshot(boneCount, SteamVR_Input_Sources.LeftHand);
- blendedSnapshotR = new SteamVR_Skeleton_PoseSnapshot(boneCount, SteamVR_Input_Sources.RightHand);
- }
- /// <summary>
- /// Set the blending value of a blendingBehaviour. Works best on Manual type behaviours.
- /// </summary>
- public void SetBlendingBehaviourValue(string behaviourName, float value)
- {
- PoseBlendingBehaviour behaviour = FindBlendingBehaviour(behaviourName);
- if (behaviour != null)
- {
- behaviour.value = value;
- if (behaviour.type != PoseBlendingBehaviour.BlenderTypes.Manual)
- {
- Debug.LogWarning("[SteamVR] Blending Behaviour: " + behaviourName + " is not a manual behaviour. Its value will likely be overriden.", this);
- }
- }
- }
- /// <summary>
- /// Get the blending value of a blendingBehaviour.
- /// </summary>
- public float GetBlendingBehaviourValue(string behaviourName)
- {
- PoseBlendingBehaviour behaviour = FindBlendingBehaviour(behaviourName);
- if (behaviour != null)
- {
- return behaviour.value;
- }
- return 0;
- }
- /// <summary>
- /// Enable or disable a blending behaviour.
- /// </summary>
- public void SetBlendingBehaviourEnabled(string behaviourName, bool value)
- {
- PoseBlendingBehaviour behaviour = FindBlendingBehaviour(behaviourName);
- if (behaviour != null)
- {
- behaviour.enabled = value;
- }
- }
- /// <summary>
- /// Check if a blending behaviour is enabled.
- /// </summary>
- /// <param name="behaviourName"></param>
- /// <returns></returns>
- public bool GetBlendingBehaviourEnabled(string behaviourName)
- {
- PoseBlendingBehaviour behaviour = FindBlendingBehaviour(behaviourName);
- if (behaviour != null)
- {
- return behaviour.enabled;
- }
- return false;
- }
- /// <summary>
- /// Get a blending behaviour by name.
- /// </summary>
- public PoseBlendingBehaviour GetBlendingBehaviour(string behaviourName)
- {
- return FindBlendingBehaviour(behaviourName);
- }
- protected PoseBlendingBehaviour FindBlendingBehaviour(string behaviourName, bool throwErrors = true)
- {
- PoseBlendingBehaviour behaviour = blendingBehaviours.Find(b => b.name == behaviourName);
- if (behaviour == null)
- {
- if (throwErrors)
- Debug.LogError("[SteamVR] Blending Behaviour: " + behaviourName + " not found on Skeleton Poser: " + gameObject.name, this);
- return null;
- }
- return behaviour;
- }
- public SteamVR_Skeleton_Pose GetPoseByIndex(int index)
- {
- if (index == 0) { return skeletonMainPose; }
- else { return skeletonAdditionalPoses[index - 1]; }
- }
- private SteamVR_Skeleton_PoseSnapshot GetHandSnapshot(SteamVR_Input_Sources inputSource)
- {
- if (inputSource == SteamVR_Input_Sources.LeftHand)
- return blendedSnapshotL;
- else
- return blendedSnapshotR;
- }
- /// <summary>
- /// Retrieve the final animated pose, to be applied to a hand skeleton
- /// </summary>
- /// <param name="forAction">The skeleton action you want to blend between</param>
- /// <param name="handType">If this is for the left or right hand</param>
- public SteamVR_Skeleton_PoseSnapshot GetBlendedPose(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources handType)
- {
- UpdatePose(skeletonAction, handType);
- return GetHandSnapshot(handType);
- }
- /// <summary>
- /// Retrieve the final animated pose, to be applied to a hand skeleton
- /// </summary>
- /// <param name="skeletonBehaviour">The skeleton behaviour you want to get the action/input source from to blend between</param>
- public SteamVR_Skeleton_PoseSnapshot GetBlendedPose(SteamVR_Behaviour_Skeleton skeletonBehaviour)
- {
- return GetBlendedPose(skeletonBehaviour.skeletonAction, skeletonBehaviour.inputSource);
- }
- /// <summary>
- /// Updates all pose animation and blending. Can be called from different places without performance concerns, as it will only let itself run once per frame.
- /// </summary>
- public void UpdatePose(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources inputSource)
- {
- // only allow this function to run once per frame
- if (poseUpdatedThisFrame) return;
- poseUpdatedThisFrame = true;
- if (skeletonAction.activeBinding)
- {
- // always do additive animation on main pose
- blendPoses[0].UpdateAdditiveAnimation(skeletonAction, inputSource);
- }
- //copy from main pose as a base
- SteamVR_Skeleton_PoseSnapshot snap = GetHandSnapshot(inputSource);
- snap.CopyFrom(blendPoses[0].GetHandSnapshot(inputSource));
- ApplyBlenderBehaviours(skeletonAction, inputSource, snap);
- if (inputSource == SteamVR_Input_Sources.RightHand)
- blendedSnapshotR = snap;
- if (inputSource == SteamVR_Input_Sources.LeftHand)
- blendedSnapshotL = snap;
- }
- protected void ApplyBlenderBehaviours(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources inputSource, SteamVR_Skeleton_PoseSnapshot snapshot)
- {
- // apply blending for each behaviour
- for (int behaviourIndex = 0; behaviourIndex < blendingBehaviours.Count; behaviourIndex++)
- {
- blendingBehaviours[behaviourIndex].Update(Time.deltaTime, inputSource);
- // if disabled or very low influence, skip for perf
- if (blendingBehaviours[behaviourIndex].enabled && blendingBehaviours[behaviourIndex].influence * blendingBehaviours[behaviourIndex].value > 0.01f)
- {
- if (blendingBehaviours[behaviourIndex].pose != 0 && skeletonAction.activeBinding)
- {
- // update additive animation only as needed
- blendPoses[blendingBehaviours[behaviourIndex].pose].UpdateAdditiveAnimation(skeletonAction, inputSource);
- }
- blendingBehaviours[behaviourIndex].ApplyBlending(snapshot, blendPoses, inputSource);
- }
- }
- }
- protected void LateUpdate()
- {
- // let the pose be updated again the next frame
- poseUpdatedThisFrame = false;
- }
- /// <summary>Weighted average of n vector3s</summary>
- protected Vector3 BlendVectors(Vector3[] vectors, float[] weights)
- {
- Vector3 blendedVector = Vector3.zero;
- for (int i = 0; i < vectors.Length; i++)
- {
- blendedVector += vectors[i] * weights[i];
- }
- return blendedVector;
- }
- /// <summary>Weighted average of n quaternions</summary>
- protected Quaternion BlendQuaternions(Quaternion[] quaternions, float[] weights)
- {
- Quaternion outquat = Quaternion.identity;
- for (int i = 0; i < quaternions.Length; i++)
- {
- outquat *= Quaternion.Slerp(Quaternion.identity, quaternions[i], weights[i]);
- }
- return outquat;
- }
- /// <summary>
- /// A SkeletonBlendablePose holds a reference to a Skeleton_Pose scriptableObject, and also contains some helper functions.
- /// Also handles pose-specific animation like additive finger motion.
- /// </summary>
- public class SkeletonBlendablePose
- {
- public SteamVR_Skeleton_Pose pose;
- public SteamVR_Skeleton_PoseSnapshot snapshotR;
- public SteamVR_Skeleton_PoseSnapshot snapshotL;
- /// <summary>
- /// Get the snapshot of this pose with effects such as additive finger animation applied.
- /// </summary>
- public SteamVR_Skeleton_PoseSnapshot GetHandSnapshot(SteamVR_Input_Sources inputSource)
- {
- if (inputSource == SteamVR_Input_Sources.LeftHand)
- {
- return snapshotL;
- }
- else
- {
- return snapshotR;
- }
- }
- public void UpdateAdditiveAnimation(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources inputSource)
- {
- if (skeletonAction.GetSkeletalTrackingLevel() == EVRSkeletalTrackingLevel.VRSkeletalTracking_Estimated)
- {
- //do not apply additive animation on low fidelity controllers, eg. Vive Wands and Touch
- return;
- }
- SteamVR_Skeleton_PoseSnapshot snapshot = GetHandSnapshot(inputSource);
- SteamVR_Skeleton_Pose_Hand poseHand = pose.GetHand(inputSource);
- for (int boneIndex = 0; boneIndex < snapshotL.bonePositions.Length; boneIndex++)
- {
- int fingerIndex = SteamVR_Skeleton_JointIndexes.GetFingerForBone(boneIndex);
- SteamVR_Skeleton_FingerExtensionTypes extensionType = poseHand.GetMovementTypeForBone(boneIndex);
- if (extensionType == SteamVR_Skeleton_FingerExtensionTypes.Free)
- {
- snapshot.bonePositions[boneIndex] = skeletonAction.bonePositions[boneIndex];
- snapshot.boneRotations[boneIndex] = skeletonAction.boneRotations[boneIndex];
- }
- if (extensionType == SteamVR_Skeleton_FingerExtensionTypes.Extend)
- {
- // lerp to open pose by fingercurl
- snapshot.bonePositions[boneIndex] = Vector3.Lerp(poseHand.bonePositions[boneIndex], skeletonAction.bonePositions[boneIndex], 1 - skeletonAction.fingerCurls[fingerIndex]);
- snapshot.boneRotations[boneIndex] = Quaternion.Lerp(poseHand.boneRotations[boneIndex], skeletonAction.boneRotations[boneIndex], 1 - skeletonAction.fingerCurls[fingerIndex]);
- }
- if (extensionType == SteamVR_Skeleton_FingerExtensionTypes.Contract)
- {
- // lerp to closed pose by fingercurl
- snapshot.bonePositions[boneIndex] = Vector3.Lerp(poseHand.bonePositions[boneIndex], skeletonAction.bonePositions[boneIndex], skeletonAction.fingerCurls[fingerIndex]);
- snapshot.boneRotations[boneIndex] = Quaternion.Lerp(poseHand.boneRotations[boneIndex], skeletonAction.boneRotations[boneIndex], skeletonAction.fingerCurls[fingerIndex]);
- }
- }
- }
- /// <summary>
- /// Init based on an existing Skeleton_Pose
- /// </summary>
- public SkeletonBlendablePose(SteamVR_Skeleton_Pose p)
- {
- pose = p;
- snapshotR = new SteamVR_Skeleton_PoseSnapshot(p.rightHand.bonePositions.Length, SteamVR_Input_Sources.RightHand);
- snapshotL = new SteamVR_Skeleton_PoseSnapshot(p.leftHand.bonePositions.Length, SteamVR_Input_Sources.LeftHand);
- }
- /// <summary>
- /// Copy the base pose into the snapshots.
- /// </summary>
- public void PoseToSnapshots()
- {
- snapshotR.position = pose.rightHand.position;
- snapshotR.rotation = pose.rightHand.rotation;
- pose.rightHand.bonePositions.CopyTo(snapshotR.bonePositions, 0);
- pose.rightHand.boneRotations.CopyTo(snapshotR.boneRotations, 0);
- snapshotL.position = pose.leftHand.position;
- snapshotL.rotation = pose.leftHand.rotation;
- pose.leftHand.bonePositions.CopyTo(snapshotL.bonePositions, 0);
- pose.leftHand.boneRotations.CopyTo(snapshotL.boneRotations, 0);
- }
- public SkeletonBlendablePose() { }
- }
- /// <summary>
- /// A filter applied to the base pose. Blends to a secondary pose by a certain weight. Can be masked per-finger
- /// </summary>
- [System.Serializable]
- public class PoseBlendingBehaviour
- {
- public string name;
- public bool enabled = true;
- public float influence = 1;
- public int pose = 1;
- public float value = 0;
- public SteamVR_Action_Single action_single;
- public SteamVR_Action_Boolean action_bool;
- public float smoothingSpeed = 0;
- public BlenderTypes type;
- public bool useMask;
- public SteamVR_Skeleton_HandMask mask = new SteamVR_Skeleton_HandMask();
- public bool previewEnabled;
- /// <summary>
- /// Performs smoothing based on deltaTime parameter.
- /// </summary>
- public void Update(float deltaTime, SteamVR_Input_Sources inputSource)
- {
- if (type == BlenderTypes.AnalogAction)
- {
- if (smoothingSpeed == 0)
- value = action_single.GetAxis(inputSource);
- else
- value = Mathf.Lerp(value, action_single.GetAxis(inputSource), deltaTime * smoothingSpeed);
- }
- if (type == BlenderTypes.BooleanAction)
- {
- if (smoothingSpeed == 0)
- value = action_bool.GetState(inputSource) ? 1 : 0;
- else
- value = Mathf.Lerp(value, action_bool.GetState(inputSource) ? 1 : 0, deltaTime * smoothingSpeed);
- }
- }
- /// <summary>
- /// Apply blending to this behaviour's pose to an existing snapshot.
- /// </summary>
- /// <param name="snapshot">Snapshot to modify</param>
- /// <param name="blendPoses">List of blend poses to get the target pose</param>
- /// <param name="inputSource">Which hand to receive input from</param>
- public void ApplyBlending(SteamVR_Skeleton_PoseSnapshot snapshot, SkeletonBlendablePose[] blendPoses, SteamVR_Input_Sources inputSource)
- {
- SteamVR_Skeleton_PoseSnapshot targetSnapshot = blendPoses[pose].GetHandSnapshot(inputSource);
- if (mask.GetFinger(0) || useMask == false)
- {
- snapshot.position = Vector3.Lerp(snapshot.position, targetSnapshot.position, influence * value);
- snapshot.rotation = Quaternion.Slerp(snapshot.rotation, targetSnapshot.rotation, influence * value);
- }
- for (int boneIndex = 0; boneIndex < snapshot.bonePositions.Length; boneIndex++)
- {
- // verify the current finger is enabled in the mask, or if no mask is used.
- if (mask.GetFinger(SteamVR_Skeleton_JointIndexes.GetFingerForBone(boneIndex) + 1) || useMask == false)
- {
- snapshot.bonePositions[boneIndex] = Vector3.Lerp(snapshot.bonePositions[boneIndex], targetSnapshot.bonePositions[boneIndex], influence * value);
- snapshot.boneRotations[boneIndex] = Quaternion.Slerp(snapshot.boneRotations[boneIndex], targetSnapshot.boneRotations[boneIndex], influence * value);
- }
- }
- }
- public PoseBlendingBehaviour()
- {
- enabled = true;
- influence = 1;
- }
- public enum BlenderTypes
- {
- Manual, AnalogAction, BooleanAction
- }
- }
- //this is broken
- public Vector3 GetTargetHandPosition(SteamVR_Behaviour_Skeleton hand, Transform origin)
- {
- Vector3 oldOrigin = origin.position;
- Quaternion oldHand = hand.transform.rotation;
- hand.transform.rotation = GetBlendedPose(hand).rotation;
- origin.position = hand.transform.TransformPoint(GetBlendedPose(hand).position);
- Vector3 offset = origin.InverseTransformPoint(hand.transform.position);
- origin.position = oldOrigin;
- hand.transform.rotation = oldHand;
- return origin.TransformPoint(offset);
- }
- public Quaternion GetTargetHandRotation(SteamVR_Behaviour_Skeleton hand, Transform origin)
- {
- Quaternion oldOrigin = origin.rotation;
- origin.rotation = hand.transform.rotation * GetBlendedPose(hand).rotation;
- Quaternion offsetRot = Quaternion.Inverse(origin.rotation) * hand.transform.rotation;
- origin.rotation = oldOrigin;
- return origin.rotation * offsetRot;
- }
- }
- /// <summary>
- /// PoseSnapshots hold a skeleton pose for one hand, as well as storing which hand they contain.
- /// They have several functions for combining BlendablePoses.
- /// </summary>
- public class SteamVR_Skeleton_PoseSnapshot
- {
- public SteamVR_Input_Sources inputSource;
- public Vector3 position;
- public Quaternion rotation;
- public Vector3[] bonePositions;
- public Quaternion[] boneRotations;
- public SteamVR_Skeleton_PoseSnapshot(int boneCount, SteamVR_Input_Sources source)
- {
- inputSource = source;
- bonePositions = new Vector3[boneCount];
- boneRotations = new Quaternion[boneCount];
- position = Vector3.zero;
- rotation = Quaternion.identity;
- }
- /// <summary>
- /// Perform a deep copy from one poseSnapshot to another.
- /// </summary>
- public void CopyFrom(SteamVR_Skeleton_PoseSnapshot source)
- {
- inputSource = source.inputSource;
- position = source.position;
- rotation = source.rotation;
- for (int i = 0; i < bonePositions.Length; i++)
- {
- bonePositions[i] = source.bonePositions[i];
- boneRotations[i] = source.boneRotations[i];
- }
- }
- }
- /// <summary>
- /// Simple mask for fingers
- /// </summary>
- [System.Serializable]
- public class SteamVR_Skeleton_HandMask
- {
- public bool palm;
- public bool thumb;
- public bool index;
- public bool middle;
- public bool ring;
- public bool pinky;
- public bool[] values = new bool[6];
- public void SetFinger(int i, bool value)
- {
- values[i] = value;
- Apply();
- }
- public bool GetFinger(int i)
- {
- return values[i];
- }
- public SteamVR_Skeleton_HandMask()
- {
- values = new bool[6];
- Reset();
- }
- /// <summary>
- /// All elements on
- /// </summary>
- public void Reset()
- {
- values = new bool[6];
- for (int i = 0; i < 6; i++)
- {
- values[i] = true;
- }
- Apply();
- }
- protected void Apply()
- {
- palm = values[0];
- thumb = values[1];
- index = values[2];
- middle = values[3];
- ring = values[4];
- pinky = values[5];
- }
- public static readonly SteamVR_Skeleton_HandMask fullMask = new SteamVR_Skeleton_HandMask();
- };
- }
|