SteamVR_Skeleton_Poser.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. using System;
  3. using System.Collections;
  4. using UnityEngine;
  5. using Valve.VR;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. namespace Valve.VR
  9. {
  10. public class SteamVR_Skeleton_Poser : MonoBehaviour
  11. {
  12. #region Editor Storage
  13. public bool poseEditorExpanded = true;
  14. public bool blendEditorExpanded = true;
  15. public string[] poseNames;
  16. #endregion
  17. public GameObject previewLeftHandPrefab;
  18. public GameObject previewRightHandPrefab;
  19. public SteamVR_Skeleton_Pose skeletonMainPose;
  20. public List<SteamVR_Skeleton_Pose> skeletonAdditionalPoses = new List<SteamVR_Skeleton_Pose>();
  21. [SerializeField]
  22. protected bool showLeftPreview = false;
  23. [SerializeField]
  24. protected bool showRightPreview = true; //show the right hand by default
  25. [SerializeField]
  26. protected GameObject previewLeftInstance;
  27. [SerializeField]
  28. protected GameObject previewRightInstance;
  29. [SerializeField]
  30. protected int previewPoseSelection = 0;
  31. public int blendPoseCount { get { return blendPoses.Length; } }
  32. public List<PoseBlendingBehaviour> blendingBehaviours = new List<PoseBlendingBehaviour>();
  33. public SteamVR_Skeleton_PoseSnapshot blendedSnapshotL;
  34. public SteamVR_Skeleton_PoseSnapshot blendedSnapshotR;
  35. private SkeletonBlendablePose[] blendPoses;
  36. private int boneCount;
  37. private bool poseUpdatedThisFrame;
  38. public float scale;
  39. protected void Awake()
  40. {
  41. if (previewLeftInstance != null)
  42. DestroyImmediate(previewLeftInstance);
  43. if (previewRightInstance != null)
  44. DestroyImmediate(previewRightInstance);
  45. blendPoses = new SkeletonBlendablePose[skeletonAdditionalPoses.Count + 1];
  46. for (int i = 0; i < blendPoseCount; i++)
  47. {
  48. blendPoses[i] = new SkeletonBlendablePose(GetPoseByIndex(i));
  49. blendPoses[i].PoseToSnapshots();
  50. }
  51. boneCount = skeletonMainPose.leftHand.bonePositions.Length;
  52. // NOTE: Is there a better way to get the bone count? idk
  53. blendedSnapshotL = new SteamVR_Skeleton_PoseSnapshot(boneCount, SteamVR_Input_Sources.LeftHand);
  54. blendedSnapshotR = new SteamVR_Skeleton_PoseSnapshot(boneCount, SteamVR_Input_Sources.RightHand);
  55. }
  56. /// <summary>
  57. /// Set the blending value of a blendingBehaviour. Works best on Manual type behaviours.
  58. /// </summary>
  59. public void SetBlendingBehaviourValue(string behaviourName, float value)
  60. {
  61. PoseBlendingBehaviour behaviour = blendingBehaviours.Find(b => b.name == behaviourName);
  62. if(behaviour == null)
  63. {
  64. Debug.LogError("[SteamVR] Blending Behaviour: " + behaviourName + " not found on Skeleton Poser: " + gameObject.name);
  65. return;
  66. }
  67. if(behaviour.type != PoseBlendingBehaviour.BlenderTypes.Manual)
  68. {
  69. Debug.LogWarning("[SteamVR] Blending Behaviour: " + behaviourName + " is not a manual behaviour. Its value will likely be overriden.");
  70. }
  71. behaviour.value = value;
  72. }
  73. /// <summary>
  74. /// Get the blending value of a blendingBehaviour.
  75. /// </summary>
  76. public float GetBlendingBehaviourValue(string behaviourName)
  77. {
  78. PoseBlendingBehaviour behaviour = blendingBehaviours.Find(b => b.name == behaviourName);
  79. if (behaviour == null)
  80. {
  81. Debug.LogError("[SteamVR] Blending Behaviour: " + behaviourName + " not found on Skeleton Poser: " + gameObject.name);
  82. return 0;
  83. }
  84. return behaviour.value;
  85. }
  86. /// <summary>
  87. /// Enable or disable a blending behaviour.
  88. /// </summary>
  89. public void SetBlendingBehaviourEnabled(string behaviourName, bool value)
  90. {
  91. PoseBlendingBehaviour behaviour = blendingBehaviours.Find(b => b.name == behaviourName);
  92. if (behaviour == null)
  93. {
  94. Debug.LogError("[SteamVR] Blending Behaviour: " + behaviourName + " not found on Skeleton Poser: " + gameObject.name);
  95. return;
  96. }
  97. behaviour.enabled = value;
  98. }
  99. /// <summary>
  100. /// Check if a blending behaviour is enabled.
  101. /// </summary>
  102. /// <param name="behaviourName"></param>
  103. /// <returns></returns>
  104. public bool GetBlendingBehaviourEnabled(string behaviourName)
  105. {
  106. PoseBlendingBehaviour behaviour = blendingBehaviours.Find(b => b.name == behaviourName);
  107. if (behaviour == null)
  108. {
  109. Debug.LogError("[SteamVR] Blending Behaviour: " + behaviourName + " not found on Skeleton Poser: " + gameObject.name);
  110. return false;
  111. }
  112. return behaviour.enabled;
  113. }
  114. /// <summary>
  115. /// Get a blending behaviour by name.
  116. /// </summary>
  117. public PoseBlendingBehaviour GetBlendingBehaviour(string behaviourName)
  118. {
  119. PoseBlendingBehaviour behaviour = blendingBehaviours.Find(b => b.name == behaviourName);
  120. if (behaviour == null)
  121. {
  122. Debug.LogError("[SteamVR] Blending Behaviour: " + behaviourName + " not found on Skeleton Poser: " + gameObject.name);
  123. return null;
  124. }
  125. return behaviour;
  126. }
  127. public SteamVR_Skeleton_Pose GetPoseByIndex(int index)
  128. {
  129. if (index == 0) { return skeletonMainPose; }
  130. else { return skeletonAdditionalPoses[index - 1]; }
  131. }
  132. private SteamVR_Skeleton_PoseSnapshot GetHandSnapshot(SteamVR_Input_Sources inputSource)
  133. {
  134. if (inputSource == SteamVR_Input_Sources.LeftHand)
  135. return blendedSnapshotL;
  136. else
  137. return blendedSnapshotR;
  138. }
  139. /// <summary>
  140. /// Retrieve the final animated pose, to be applied to a hand skeleton
  141. /// </summary>
  142. /// <param name="forAction">The skeleton action you want to blend between</param>
  143. /// <param name="handType">If this is for the left or right hand</param>
  144. public SteamVR_Skeleton_PoseSnapshot GetBlendedPose(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources handType)
  145. {
  146. UpdatePose(skeletonAction, handType);
  147. return GetHandSnapshot(handType);
  148. }
  149. /// <summary>
  150. /// Retrieve the final animated pose, to be applied to a hand skeleton
  151. /// </summary>
  152. /// <param name="skeletonBehaviour">The skeleton behaviour you want to get the action/input source from to blend between</param>
  153. public SteamVR_Skeleton_PoseSnapshot GetBlendedPose(SteamVR_Behaviour_Skeleton skeletonBehaviour)
  154. {
  155. return GetBlendedPose(skeletonBehaviour.skeletonAction, skeletonBehaviour.inputSource);
  156. }
  157. /// <summary>
  158. /// 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.
  159. /// </summary>
  160. public void UpdatePose(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources inputSource)
  161. {
  162. // only allow this function to run once per frame
  163. if (poseUpdatedThisFrame) return;
  164. poseUpdatedThisFrame = true;
  165. // always do additive animation on main pose
  166. blendPoses[0].UpdateAdditiveAnimation(skeletonAction, inputSource);
  167. //copy from main pose as a base
  168. SteamVR_Skeleton_PoseSnapshot snap = GetHandSnapshot(inputSource);
  169. snap.CopyFrom(blendPoses[0].GetHandSnapshot(inputSource));
  170. ApplyBlenderBehaviours(skeletonAction, inputSource, snap);
  171. if (inputSource == SteamVR_Input_Sources.RightHand)
  172. blendedSnapshotR = snap;
  173. if (inputSource == SteamVR_Input_Sources.LeftHand)
  174. blendedSnapshotL = snap;
  175. }
  176. protected void ApplyBlenderBehaviours(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources inputSource, SteamVR_Skeleton_PoseSnapshot snapshot)
  177. {
  178. // apply blending for each behaviour
  179. for (int behaviourIndex = 0; behaviourIndex < blendingBehaviours.Count; behaviourIndex++)
  180. {
  181. blendingBehaviours[behaviourIndex].Update(Time.deltaTime, inputSource);
  182. // if disabled or very low influence, skip for perf
  183. if (blendingBehaviours[behaviourIndex].enabled && blendingBehaviours[behaviourIndex].influence * blendingBehaviours[behaviourIndex].value > 0.01f)
  184. {
  185. if (blendingBehaviours[behaviourIndex].pose != 0)
  186. {
  187. // update additive animation only as needed
  188. blendPoses[blendingBehaviours[behaviourIndex].pose].UpdateAdditiveAnimation(skeletonAction, inputSource);
  189. }
  190. blendingBehaviours[behaviourIndex].ApplyBlending(snapshot, blendPoses, inputSource);
  191. }
  192. }
  193. }
  194. protected void LateUpdate()
  195. {
  196. // let the pose be updated again the next frame
  197. poseUpdatedThisFrame = false;
  198. }
  199. /// <summary>Weighted average of n vector3s</summary>
  200. protected Vector3 BlendVectors(Vector3[] vectors, float[] weights)
  201. {
  202. Vector3 blendedVector = Vector3.zero;
  203. for (int i = 0; i < vectors.Length; i++)
  204. {
  205. blendedVector += vectors[i] * weights[i];
  206. }
  207. return blendedVector;
  208. }
  209. /// <summary>Weighted average of n quaternions</summary>
  210. protected Quaternion BlendQuaternions(Quaternion[] quaternions, float[] weights)
  211. {
  212. Quaternion outquat = Quaternion.identity;
  213. for (int i = 0; i < quaternions.Length; i++)
  214. {
  215. outquat *= Quaternion.Slerp(Quaternion.identity, quaternions[i], weights[i]);
  216. }
  217. return outquat;
  218. }
  219. /// <summary>
  220. /// A SkeletonBlendablePose holds a reference to a Skeleton_Pose scriptableObject, and also contains some helper functions.
  221. /// Also handles pose-specific animation like additive finger motion.
  222. /// </summary>
  223. public class SkeletonBlendablePose
  224. {
  225. public SteamVR_Skeleton_Pose pose;
  226. public SteamVR_Skeleton_PoseSnapshot snapshotR;
  227. public SteamVR_Skeleton_PoseSnapshot snapshotL;
  228. /// <summary>
  229. /// Get the snapshot of this pose with effects such as additive finger animation applied.
  230. /// </summary>
  231. public SteamVR_Skeleton_PoseSnapshot GetHandSnapshot(SteamVR_Input_Sources inputSource)
  232. {
  233. if (inputSource == SteamVR_Input_Sources.LeftHand)
  234. {
  235. return snapshotL;
  236. }
  237. else
  238. {
  239. return snapshotR;
  240. }
  241. }
  242. public void UpdateAdditiveAnimation(SteamVR_Action_Skeleton skeletonAction, SteamVR_Input_Sources inputSource)
  243. {
  244. SteamVR_Skeleton_PoseSnapshot snapshot = GetHandSnapshot(inputSource);
  245. SteamVR_Skeleton_Pose_Hand poseHand = pose.GetHand(inputSource);
  246. for (int boneIndex = 0; boneIndex < snapshotL.bonePositions.Length; boneIndex++)
  247. {
  248. int fingerIndex = SteamVR_Skeleton_JointIndexes.GetFingerForBone(boneIndex);
  249. SteamVR_Skeleton_FingerExtensionTypes extensionType = poseHand.GetMovementTypeForBone(boneIndex);
  250. if (extensionType == SteamVR_Skeleton_FingerExtensionTypes.Free)
  251. {
  252. snapshot.bonePositions[boneIndex] = skeletonAction.bonePositions[boneIndex];
  253. snapshot.boneRotations[boneIndex] = skeletonAction.boneRotations[boneIndex];
  254. }
  255. if (extensionType == SteamVR_Skeleton_FingerExtensionTypes.Extend)
  256. {
  257. // lerp to open pose by fingercurl
  258. snapshot.bonePositions[boneIndex] = Vector3.Lerp(poseHand.bonePositions[boneIndex], skeletonAction.bonePositions[boneIndex], 1 - skeletonAction.fingerCurls[fingerIndex]);
  259. snapshot.boneRotations[boneIndex] = Quaternion.Lerp(poseHand.boneRotations[boneIndex], skeletonAction.boneRotations[boneIndex], 1 - skeletonAction.fingerCurls[fingerIndex]);
  260. }
  261. if (extensionType == SteamVR_Skeleton_FingerExtensionTypes.Contract)
  262. {
  263. // lerp to closed pose by fingercurl
  264. snapshot.bonePositions[boneIndex] = Vector3.Lerp(poseHand.bonePositions[boneIndex], skeletonAction.bonePositions[boneIndex], skeletonAction.fingerCurls[fingerIndex]);
  265. snapshot.boneRotations[boneIndex] = Quaternion.Lerp(poseHand.boneRotations[boneIndex], skeletonAction.boneRotations[boneIndex], skeletonAction.fingerCurls[fingerIndex]);
  266. }
  267. }
  268. }
  269. /// <summary>
  270. /// Init based on an existing Skeleton_Pose
  271. /// </summary>
  272. public SkeletonBlendablePose(SteamVR_Skeleton_Pose p)
  273. {
  274. pose = p;
  275. snapshotR = new SteamVR_Skeleton_PoseSnapshot(p.rightHand.bonePositions.Length, SteamVR_Input_Sources.RightHand);
  276. snapshotL = new SteamVR_Skeleton_PoseSnapshot(p.leftHand.bonePositions.Length, SteamVR_Input_Sources.LeftHand);
  277. }
  278. /// <summary>
  279. /// Copy the base pose into the snapshots.
  280. /// </summary>
  281. public void PoseToSnapshots()
  282. {
  283. snapshotR.position = pose.rightHand.position;
  284. snapshotR.rotation = pose.rightHand.rotation;
  285. pose.rightHand.bonePositions.CopyTo(snapshotR.bonePositions, 0);
  286. pose.rightHand.boneRotations.CopyTo(snapshotR.boneRotations, 0);
  287. snapshotL.position = pose.leftHand.position;
  288. snapshotL.rotation = pose.leftHand.rotation;
  289. pose.leftHand.bonePositions.CopyTo(snapshotL.bonePositions, 0);
  290. pose.leftHand.boneRotations.CopyTo(snapshotL.boneRotations, 0);
  291. }
  292. public SkeletonBlendablePose() { }
  293. }
  294. /// <summary>
  295. /// A filter applied to the base pose. Blends to a secondary pose by a certain weight. Can be masked per-finger
  296. /// </summary>
  297. [System.Serializable]
  298. public class PoseBlendingBehaviour
  299. {
  300. public string name;
  301. public bool enabled = true;
  302. public float influence = 1;
  303. public int pose = 1;
  304. public float value = 0;
  305. public SteamVR_Action_Single action_single;
  306. public SteamVR_Action_Boolean action_bool;
  307. public float smoothingSpeed = 0;
  308. public BlenderTypes type;
  309. public bool useMask;
  310. public SteamVR_Skeleton_HandMask mask = new SteamVR_Skeleton_HandMask();
  311. public bool previewEnabled;
  312. /// <summary>
  313. /// Performs smoothing based on deltaTime parameter.
  314. /// </summary>
  315. public void Update(float deltaTime, SteamVR_Input_Sources inputSource)
  316. {
  317. if (type == BlenderTypes.AnalogAction)
  318. {
  319. if (smoothingSpeed == 0)
  320. value = action_single.GetAxis(inputSource);
  321. else
  322. value = Mathf.Lerp(value, action_single.GetAxis(inputSource), deltaTime * smoothingSpeed);
  323. }
  324. if (type == BlenderTypes.BooleanAction)
  325. {
  326. if (smoothingSpeed == 0)
  327. value = action_bool.GetState(inputSource) ? 1 : 0;
  328. else
  329. value = Mathf.Lerp(value, action_bool.GetState(inputSource) ? 1 : 0, deltaTime * smoothingSpeed);
  330. }
  331. }
  332. /// <summary>
  333. /// Apply blending to this behaviour's pose to an existing snapshot.
  334. /// </summary>
  335. /// <param name="snapshot">Snapshot to modify</param>
  336. /// <param name="blendPoses">List of blend poses to get the target pose</param>
  337. /// <param name="inputSource">Which hand to receive input from</param>
  338. public void ApplyBlending(SteamVR_Skeleton_PoseSnapshot snapshot, SkeletonBlendablePose[] blendPoses, SteamVR_Input_Sources inputSource)
  339. {
  340. SteamVR_Skeleton_PoseSnapshot targetSnapshot = blendPoses[pose].GetHandSnapshot(inputSource);
  341. if (mask.GetFinger(0) || useMask == false)
  342. {
  343. snapshot.position = Vector3.Lerp(snapshot.position, targetSnapshot.position, influence * value);
  344. snapshot.rotation = Quaternion.Slerp(snapshot.rotation, targetSnapshot.rotation, influence * value);
  345. }
  346. for (int boneIndex = 0; boneIndex < snapshot.bonePositions.Length; boneIndex++)
  347. {
  348. // verify the current finger is enabled in the mask, or if no mask is used.
  349. if (mask.GetFinger(SteamVR_Skeleton_JointIndexes.GetFingerForBone(boneIndex) + 1) || useMask == false)
  350. {
  351. snapshot.bonePositions[boneIndex] = Vector3.Lerp(snapshot.bonePositions[boneIndex], targetSnapshot.bonePositions[boneIndex], influence * value);
  352. snapshot.boneRotations[boneIndex] = Quaternion.Slerp(snapshot.boneRotations[boneIndex], targetSnapshot.boneRotations[boneIndex], influence * value);
  353. }
  354. }
  355. }
  356. public PoseBlendingBehaviour()
  357. {
  358. enabled = true;
  359. influence = 1;
  360. }
  361. public enum BlenderTypes
  362. {
  363. Manual, AnalogAction, BooleanAction
  364. }
  365. }
  366. }
  367. /// <summary>
  368. /// PoseSnapshots hold a skeleton pose for one hand, as well as storing which hand they contain.
  369. /// They have several functions for combining BlendablePoses.
  370. /// </summary>
  371. public class SteamVR_Skeleton_PoseSnapshot
  372. {
  373. public SteamVR_Input_Sources inputSource;
  374. public Vector3 position;
  375. public Quaternion rotation;
  376. public Vector3[] bonePositions;
  377. public Quaternion[] boneRotations;
  378. public SteamVR_Skeleton_PoseSnapshot(int boneCount, SteamVR_Input_Sources source)
  379. {
  380. inputSource = source;
  381. bonePositions = new Vector3[boneCount];
  382. boneRotations = new Quaternion[boneCount];
  383. position = Vector3.zero;
  384. rotation = Quaternion.identity;
  385. }
  386. /// <summary>
  387. /// Perform a deep copy from one poseSnapshot to another.
  388. /// </summary>
  389. public void CopyFrom(SteamVR_Skeleton_PoseSnapshot source)
  390. {
  391. inputSource = source.inputSource;
  392. position = source.position;
  393. rotation = source.rotation;
  394. for (int i = 0; i < bonePositions.Length; i++)
  395. {
  396. bonePositions[i] = source.bonePositions[i];
  397. boneRotations[i] = source.boneRotations[i];
  398. }
  399. }
  400. }
  401. /// <summary>
  402. /// Simple mask for fingers
  403. /// </summary>
  404. [System.Serializable]
  405. public class SteamVR_Skeleton_HandMask
  406. {
  407. public bool palm;
  408. public bool thumb;
  409. public bool index;
  410. public bool middle;
  411. public bool ring;
  412. public bool pinky;
  413. public bool[] values = new bool[6];
  414. public void SetFinger(int i, bool value)
  415. {
  416. values[i] = value;
  417. Apply();
  418. }
  419. public bool GetFinger(int i)
  420. {
  421. return values[i];
  422. }
  423. public SteamVR_Skeleton_HandMask()
  424. {
  425. values = new bool[6];
  426. Reset();
  427. }
  428. /// <summary>
  429. /// All elements on
  430. /// </summary>
  431. public void Reset()
  432. {
  433. values = new bool[6];
  434. for (int i = 0; i < 6; i++)
  435. {
  436. values[i] = true;
  437. }
  438. Apply();
  439. }
  440. protected void Apply()
  441. {
  442. palm = values[0];
  443. thumb = values[1];
  444. index = values[2];
  445. middle = values[3];
  446. ring = values[4];
  447. pinky = values[5];
  448. }
  449. public static readonly SteamVR_Skeleton_HandMask fullMask = new SteamVR_Skeleton_HandMask();
  450. };
  451. }