AvatarUtility.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. using System.Reflection;
  6. namespace RootMotion
  7. {
  8. public class TQ
  9. {
  10. public TQ(Vector3 translation, Quaternion rotation)
  11. {
  12. t = translation;
  13. q = rotation;
  14. }
  15. public Vector3 t;
  16. public Quaternion q;
  17. }
  18. /*
  19. Written with the kind help of the one commonly known as Mecanim-Dev.
  20. */
  21. public class AvatarUtility
  22. {
  23. public static Quaternion GetPostRotation(Avatar avatar, AvatarIKGoal avatarIKGoal)
  24. {
  25. int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
  26. if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
  27. MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
  28. if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
  29. return (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
  30. }
  31. /// <summary>
  32. /// Get IK position and rotation for foot/hand bone position/rotation.
  33. /// </summary>
  34. public static TQ GetIKGoalTQ(Avatar avatar, float humanScale, AvatarIKGoal avatarIKGoal, TQ bodyPositionRotation, TQ boneTQ)
  35. {
  36. int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
  37. if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
  38. MethodInfo methodGetAxisLength = typeof(Avatar).GetMethod("GetAxisLength", BindingFlags.Instance | BindingFlags.NonPublic);
  39. if (methodGetAxisLength == null) throw new InvalidOperationException("Cannot find GetAxisLength method.");
  40. MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
  41. if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
  42. Quaternion postRotation = (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
  43. var goalTQ = new TQ(boneTQ.t, boneTQ.q * postRotation);
  44. if (avatarIKGoal == AvatarIKGoal.LeftFoot || avatarIKGoal == AvatarIKGoal.RightFoot)
  45. {
  46. // Here you could use animator.leftFeetBottomHeight or animator.rightFeetBottomHeight rather than GetAxisLenght
  47. // Both are equivalent but GetAxisLength is the generic way and work for all human bone
  48. float axislength = (float)methodGetAxisLength.Invoke(avatar, new object[] { humanId });
  49. Vector3 footBottom = new Vector3(axislength, 0, 0);
  50. goalTQ.t += (goalTQ.q * footBottom);
  51. }
  52. // IK goal are in avatar body local space
  53. Quaternion invRootQ = Quaternion.Inverse(bodyPositionRotation.q);
  54. goalTQ.t = invRootQ * (goalTQ.t - bodyPositionRotation.t);
  55. goalTQ.q = invRootQ * goalTQ.q;
  56. goalTQ.t /= humanScale;
  57. goalTQ.q = Quaternion.LookRotation(goalTQ.q * Vector3.forward, goalTQ.q * Vector3.up);
  58. return goalTQ;
  59. }
  60. public static HumanBodyBones HumanIDFromAvatarIKGoal(AvatarIKGoal avatarIKGoal)
  61. {
  62. HumanBodyBones humanId = HumanBodyBones.LastBone;
  63. switch (avatarIKGoal)
  64. {
  65. case AvatarIKGoal.LeftFoot: humanId = HumanBodyBones.LeftFoot; break;
  66. case AvatarIKGoal.RightFoot: humanId = HumanBodyBones.RightFoot; break;
  67. case AvatarIKGoal.LeftHand: humanId = HumanBodyBones.LeftHand; break;
  68. case AvatarIKGoal.RightHand: humanId = HumanBodyBones.RightHand; break;
  69. }
  70. return humanId;
  71. }
  72. }
  73. }