BipedReferences.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. namespace RootMotion {
  5. /// <summary>
  6. /// Contains references to bones common to all biped characters.
  7. /// </summary>
  8. [System.Serializable]
  9. public class BipedReferences {
  10. #region Main Interface
  11. /// <summary>
  12. /// The root transform is the parent of all the biped's bones and should be located at ground level.
  13. /// </summary>
  14. public Transform root;
  15. /// <summary>
  16. /// The pelvis (hip) bone.
  17. /// </summary>
  18. public Transform pelvis;
  19. /// <summary>
  20. /// The first bone of the left leg.
  21. /// </summary>
  22. public Transform leftThigh;
  23. /// <summary>
  24. /// The second bone of the left leg.
  25. /// </summary>
  26. public Transform leftCalf;
  27. /// <summary>
  28. /// The third bone of the left leg.
  29. /// </summary>
  30. public Transform leftFoot;
  31. /// <summary>
  32. /// The first bone of the right leg.
  33. /// </summary>
  34. public Transform rightThigh;
  35. /// <summary>
  36. /// The second bone of the right leg.
  37. /// </summary>
  38. public Transform rightCalf;
  39. /// <summary>
  40. /// The third bone of the right leg.
  41. /// </summary>
  42. public Transform rightFoot;
  43. /// <summary>
  44. /// The first bone of the left arm.
  45. /// </summary>
  46. public Transform leftUpperArm;
  47. /// <summary>
  48. /// The second bone of the left arm.
  49. /// </summary>
  50. public Transform leftForearm;
  51. /// <summary>
  52. /// The third bone of the left arm.
  53. /// </summary>
  54. public Transform leftHand;
  55. /// <summary>
  56. /// The first bone of the right arm.
  57. /// </summary>
  58. public Transform rightUpperArm;
  59. /// <summary>
  60. /// The second bone of the right arm.
  61. /// </summary>
  62. public Transform rightForearm;
  63. /// <summary>
  64. /// The third bone of the right arm.
  65. /// </summary>
  66. public Transform rightHand;
  67. /// <summary>
  68. /// The head.
  69. /// </summary>
  70. public Transform head;
  71. /// <summary>
  72. /// The spine hierarchy. Should not contain any bone deeper in the hierarchy than the arms (neck or head).
  73. /// </summary>
  74. public Transform[] spine = new Transform[0];
  75. /// <summary>
  76. /// The eyes.
  77. /// </summary>
  78. public Transform[] eyes = new Transform[0];
  79. /// <summary>
  80. /// Check for null references.
  81. /// </summary>
  82. public virtual bool isFilled {
  83. get {
  84. if (root == null) return false;
  85. if (pelvis == null) return false;
  86. if (leftThigh == null || leftCalf == null || leftFoot == null) return false;
  87. if (rightThigh == null || rightCalf == null || rightFoot == null) return false;
  88. if (leftUpperArm == null || leftForearm == null || leftHand == null) return false;
  89. if (rightUpperArm == null || rightForearm == null || rightHand == null) return false;
  90. foreach (Transform s in spine) if (s == null) return false;
  91. foreach (Transform eye in eyes) if (eye == null) return false;
  92. return true;
  93. }
  94. }
  95. /// <summary>
  96. /// Gets a value indicating whether this <see cref="BipedReferences"/> is empty.
  97. /// </summary>
  98. public bool isEmpty {
  99. get {
  100. return IsEmpty(true);
  101. }
  102. }
  103. /// <summary>
  104. /// Gets a value indicating whether this <see cref="BipedReferences"/> is empty. If includeRoot is false, returns true(is empty) even if root Transform has been assigned.
  105. /// </summary>
  106. public virtual bool IsEmpty(bool includeRoot) {
  107. if (includeRoot && root != null) return false;
  108. if (pelvis != null || head != null) return false;
  109. if (leftThigh != null || leftCalf != null || leftFoot != null) return false;
  110. if (rightThigh != null || rightCalf != null || rightFoot != null) return false;
  111. if (leftUpperArm != null || leftForearm != null || leftHand != null) return false;
  112. if (rightUpperArm != null || rightForearm != null || rightHand != null) return false;
  113. foreach (Transform s in spine) if (s != null) return false;
  114. foreach (Transform eye in eyes) if (eye != null) return false;
  115. return true;
  116. }
  117. /// <summary>
  118. /// Returns true if the References contain the specified Transform
  119. /// </summary>
  120. public virtual bool Contains(Transform t, bool ignoreRoot = false) {
  121. if (!ignoreRoot && root == t) return true;
  122. if (pelvis == t) return true;
  123. if (leftThigh == t) return true;
  124. if (leftCalf == t) return true;
  125. if (leftFoot == t) return true;
  126. if (rightThigh == t) return true;
  127. if (rightCalf == t) return true;
  128. if (rightFoot == t) return true;
  129. if (leftUpperArm == t) return true;
  130. if (leftForearm == t) return true;
  131. if (leftHand == t) return true;
  132. if (rightUpperArm == t) return true;
  133. if (rightForearm == t) return true;
  134. if (rightHand == t) return true;
  135. if (head == t) return true;
  136. foreach (Transform s in spine) if (s == t) return true;
  137. foreach (Transform e in eyes) if (e == t) return true;
  138. return false;
  139. }
  140. /// <summary>
  141. /// Params for automatic biped recognition. (Using a struct here because I might need to add more parameters in the future).
  142. /// </summary>
  143. public struct AutoDetectParams {
  144. /// <summary>
  145. /// Should the immediate parent of the legs be included in the spine?.
  146. /// </summary>
  147. public bool legsParentInSpine;
  148. public bool includeEyes;
  149. public AutoDetectParams(bool legsParentInSpine, bool includeEyes) {
  150. this.legsParentInSpine = legsParentInSpine;
  151. this.includeEyes = includeEyes;
  152. }
  153. public static AutoDetectParams Default {
  154. get {
  155. return new AutoDetectParams(true, true);
  156. }
  157. }
  158. }
  159. /// <summary>
  160. /// Automatically detects biped bones. Returns true if a valid biped has been referenced.
  161. /// </summary>
  162. public static bool AutoDetectReferences(ref BipedReferences references, Transform root, AutoDetectParams autoDetectParams) {
  163. if (references == null) references = new BipedReferences();
  164. references.root = root;
  165. // If that failed try the Animator
  166. var animator = root.GetComponent<Animator>();
  167. if (animator != null && animator.isHuman) {
  168. AssignHumanoidReferences(ref references, animator, autoDetectParams);
  169. return true; // Assume humanoids are always valid
  170. }
  171. // Try with naming and hierarchy first
  172. DetectReferencesByNaming(ref references, root, autoDetectParams);
  173. Warning.logged = false;
  174. if (!references.isFilled) {
  175. Warning.Log("BipedReferences contains one or more missing Transforms.", root, true);
  176. return false;
  177. }
  178. string message = "";
  179. if (SetupError(references, ref message)) {
  180. Warning.Log(message, references.root, true);
  181. return false;
  182. }
  183. if (SetupWarning(references, ref message)) {
  184. Warning.Log(message, references.root, true);
  185. }
  186. return true;
  187. }
  188. /// <summary>
  189. /// Detects the references based on naming and hierarchy.
  190. /// </summary>
  191. public static void DetectReferencesByNaming(ref BipedReferences references, Transform root, AutoDetectParams autoDetectParams) {
  192. if (references == null) references = new BipedReferences();
  193. Transform[] children = root.GetComponentsInChildren<Transform>();
  194. // Find limbs
  195. DetectLimb(BipedNaming.BoneType.Arm, BipedNaming.BoneSide.Left, ref references.leftUpperArm, ref references.leftForearm, ref references.leftHand, children);
  196. DetectLimb(BipedNaming.BoneType.Arm, BipedNaming.BoneSide.Right, ref references.rightUpperArm, ref references.rightForearm, ref references.rightHand, children);
  197. DetectLimb(BipedNaming.BoneType.Leg, BipedNaming.BoneSide.Left, ref references.leftThigh, ref references.leftCalf, ref references.leftFoot, children);
  198. DetectLimb(BipedNaming.BoneType.Leg, BipedNaming.BoneSide.Right, ref references.rightThigh, ref references.rightCalf, ref references.rightFoot, children);
  199. // Find head bone
  200. references.head = BipedNaming.GetBone(children, BipedNaming.BoneType.Head);
  201. // Find Pelvis
  202. references.pelvis = BipedNaming.GetNamingMatch(children, BipedNaming.pelvis);
  203. // If pelvis is not an ancestor of a leg, it is not a valid pelvis
  204. if (references.pelvis == null || !Hierarchy.IsAncestor(references.leftThigh, references.pelvis)) {
  205. if (references.leftThigh != null) references.pelvis = references.leftThigh.parent;
  206. }
  207. // Find spine and head bones
  208. if (references.leftUpperArm != null && references.rightUpperArm != null && references.pelvis != null && references.leftThigh != null) {
  209. Transform neck = Hierarchy.GetFirstCommonAncestor(references.leftUpperArm, references.rightUpperArm);
  210. if (neck != null) {
  211. Transform[] inverseSpine = new Transform[1] { neck };
  212. Hierarchy.AddAncestors(inverseSpine[0], references.pelvis, ref inverseSpine);
  213. references.spine = new Transform[0];
  214. for (int i = inverseSpine.Length - 1; i > -1; i--) {
  215. if (AddBoneToSpine(inverseSpine[i], ref references, autoDetectParams)) {
  216. Array.Resize(ref references.spine, references.spine.Length + 1);
  217. references.spine[references.spine.Length - 1] = inverseSpine[i];
  218. }
  219. }
  220. // Head
  221. if (references.head == null) {
  222. for (int i = 0; i < neck.childCount; i++) {
  223. Transform child = neck.GetChild(i);
  224. if (!Hierarchy.ContainsChild(child, references.leftUpperArm) && !Hierarchy.ContainsChild(child, references.rightUpperArm)) {
  225. references.head = child;
  226. break;
  227. }
  228. }
  229. }
  230. }
  231. }
  232. // Find eye bones
  233. Transform[] eyes = BipedNaming.GetBonesOfType(BipedNaming.BoneType.Eye, children);
  234. references.eyes = new Transform[0];
  235. if (autoDetectParams.includeEyes) {
  236. for (int i = 0; i < eyes.Length; i++) {
  237. if (AddBoneToEyes(eyes[i], ref references, autoDetectParams)) {
  238. Array.Resize(ref references.eyes, references.eyes.Length + 1);
  239. references.eyes[references.eyes.Length - 1] = eyes[i];
  240. }
  241. }
  242. }
  243. }
  244. /// <summary>
  245. /// Fills in BipedReferences using Animator.GetBoneTransform().
  246. /// </summary>
  247. public static void AssignHumanoidReferences(ref BipedReferences references, Animator animator, AutoDetectParams autoDetectParams) {
  248. if (references == null) references = new BipedReferences();
  249. if (animator == null || !animator.isHuman) return;
  250. references.spine = new Transform[0];
  251. references.eyes = new Transform[0];
  252. references.head = animator.GetBoneTransform(HumanBodyBones.Head);
  253. references.leftThigh = animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
  254. references.leftCalf = animator.GetBoneTransform(HumanBodyBones.LeftLowerLeg);
  255. references.leftFoot = animator.GetBoneTransform(HumanBodyBones.LeftFoot);
  256. references.rightThigh = animator.GetBoneTransform(HumanBodyBones.RightUpperLeg);
  257. references.rightCalf = animator.GetBoneTransform(HumanBodyBones.RightLowerLeg);
  258. references.rightFoot = animator.GetBoneTransform(HumanBodyBones.RightFoot);
  259. references.leftUpperArm = animator.GetBoneTransform(HumanBodyBones.LeftUpperArm);
  260. references.leftForearm = animator.GetBoneTransform(HumanBodyBones.LeftLowerArm);
  261. references.leftHand = animator.GetBoneTransform(HumanBodyBones.LeftHand);
  262. references.rightUpperArm = animator.GetBoneTransform(HumanBodyBones.RightUpperArm);
  263. references.rightForearm = animator.GetBoneTransform(HumanBodyBones.RightLowerArm);
  264. references.rightHand = animator.GetBoneTransform(HumanBodyBones.RightHand);
  265. references.pelvis = animator.GetBoneTransform(HumanBodyBones.Hips);
  266. AddBoneToHierarchy(ref references.spine, animator.GetBoneTransform(HumanBodyBones.Spine));
  267. AddBoneToHierarchy(ref references.spine, animator.GetBoneTransform(HumanBodyBones.Chest));
  268. // Make sure the neck bone is not above the arms
  269. if (references.leftUpperArm != null) {
  270. if (!IsNeckBone(animator.GetBoneTransform(HumanBodyBones.Neck), references.leftUpperArm)) AddBoneToHierarchy(ref references.spine, animator.GetBoneTransform(HumanBodyBones.Neck));
  271. }
  272. if (autoDetectParams.includeEyes) {
  273. AddBoneToHierarchy(ref references.eyes, animator.GetBoneTransform(HumanBodyBones.LeftEye));
  274. AddBoneToHierarchy(ref references.eyes, animator.GetBoneTransform(HumanBodyBones.RightEye));
  275. }
  276. }
  277. /// <summary>
  278. /// Checks the setup for definite problems.
  279. /// </summary>
  280. public static bool SetupError(BipedReferences references, ref string errorMessage) {
  281. if (!references.isFilled) {
  282. errorMessage = "BipedReferences contains one or more missing Transforms.";
  283. return true;
  284. }
  285. if (LimbError(references.leftThigh, references.leftCalf, references.leftFoot, ref errorMessage)) return true;
  286. if (LimbError(references.rightThigh, references.rightCalf, references.rightFoot, ref errorMessage)) return true;
  287. if (LimbError(references.leftUpperArm, references.leftForearm, references.leftHand, ref errorMessage)) return true;
  288. if (LimbError(references.rightUpperArm, references.rightForearm, references.rightHand, ref errorMessage)) return true;
  289. if (SpineError(references, ref errorMessage)) return true;
  290. if (EyesError(references, ref errorMessage)) return true;
  291. return false;
  292. }
  293. /// <summary>
  294. /// Checks the setup for possible problems.
  295. /// </summary>
  296. public static bool SetupWarning(BipedReferences references, ref string warningMessage) {
  297. if (LimbWarning(references.leftThigh, references.leftCalf, references.leftFoot, ref warningMessage)) return true;
  298. if (LimbWarning(references.rightThigh, references.rightCalf, references.rightFoot, ref warningMessage)) return true;
  299. if (LimbWarning(references.leftUpperArm, references.leftForearm, references.leftHand, ref warningMessage)) return true;
  300. if (LimbWarning(references.rightUpperArm, references.rightForearm, references.rightHand, ref warningMessage)) return true;
  301. if (SpineWarning(references, ref warningMessage)) return true;
  302. if (EyesWarning(references, ref warningMessage)) return true;
  303. if (RootHeightWarning(references, ref warningMessage)) return true;
  304. if (FacingAxisWarning(references, ref warningMessage)) return true;
  305. return false;
  306. }
  307. #endregion Main Interface
  308. // Determines whether a Transform is above the arms
  309. private static bool IsNeckBone(Transform bone, Transform leftUpperArm) {
  310. if (leftUpperArm.parent != null && leftUpperArm.parent == bone) return false;
  311. if (Hierarchy.IsAncestor(leftUpperArm, bone)) return false;
  312. return true;
  313. }
  314. // Determines whether a bone is valid for being added into the eyes array
  315. private static bool AddBoneToEyes(Transform bone, ref BipedReferences references, AutoDetectParams autoDetectParams) {
  316. if (references.head != null) {
  317. if (!Hierarchy.IsAncestor(bone, references.head)) return false;
  318. }
  319. if (bone.GetComponent<SkinnedMeshRenderer>() != null) return false;
  320. return true;
  321. }
  322. // Determines whether a bone is valid for being added into the spine
  323. private static bool AddBoneToSpine(Transform bone, ref BipedReferences references, AutoDetectParams autoDetectParams) {
  324. if (bone == references.root) return false;
  325. bool isLegsParent = bone == references.leftThigh.parent;
  326. if (isLegsParent && !autoDetectParams.legsParentInSpine) return false;
  327. if (references.pelvis != null) {
  328. if (bone == references.pelvis) return false;
  329. if (Hierarchy.IsAncestor(references.pelvis, bone)) return false;
  330. }
  331. return true;
  332. }
  333. // Tries to guess the limb bones based on naming
  334. private static void DetectLimb(BipedNaming.BoneType boneType, BipedNaming.BoneSide boneSide, ref Transform firstBone, ref Transform secondBone, ref Transform lastBone, Transform[] transforms) {
  335. Transform[] limb = BipedNaming.GetBonesOfTypeAndSide(boneType, boneSide, transforms);
  336. if (limb.Length < 3) {
  337. //Warning.Log("Unable to detect biped bones by bone names. Please manually assign bone references.", firstBone, true);
  338. return;
  339. }
  340. // Standard biped characters
  341. if (limb.Length == 3) {
  342. firstBone = limb[0];
  343. secondBone = limb[1];
  344. lastBone = limb[2];
  345. }
  346. // For Bootcamp soldier type of characters with more than 3 limb bones
  347. if (limb.Length > 3) {
  348. firstBone = limb[0];
  349. secondBone = limb[2];
  350. lastBone = limb[limb.Length - 1];
  351. }
  352. }
  353. // Adds transform to hierarchy if not null
  354. private static void AddBoneToHierarchy(ref Transform[] bones, Transform transform) {
  355. if (transform == null) return;
  356. Array.Resize(ref bones, bones.Length + 1);
  357. bones[bones.Length - 1] = transform;
  358. }
  359. // Check if the limb is properly set up
  360. private static bool LimbError(Transform bone1, Transform bone2, Transform bone3, ref string errorMessage) {
  361. if (bone1 == null) {
  362. errorMessage = "Bone 1 of a BipedReferences limb is null.";
  363. return true;
  364. }
  365. if (bone2 == null) {
  366. errorMessage = "Bone 2 of a BipedReferences limb is null.";
  367. return true;
  368. }
  369. if (bone3 == null) {
  370. errorMessage = "Bone 3 of a BipedReferences limb is null.";
  371. return true;
  372. }
  373. Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(new Transform[3] { bone1, bone2, bone3 });
  374. if (duplicate != null) {
  375. errorMessage = duplicate.name + " is represented multiple times in the same BipedReferences limb.";
  376. return true;
  377. }
  378. if (bone2.position == bone1.position) {
  379. errorMessage = "Second bone's position equals first bone's position in the biped's limb.";
  380. return true;
  381. }
  382. if (bone3.position == bone2.position) {
  383. errorMessage = "Third bone's position equals second bone's position in the biped's limb.";
  384. return true;
  385. }
  386. if (!Hierarchy.HierarchyIsValid(new Transform[3] { bone1, bone2, bone3 })) {
  387. errorMessage = "BipedReferences limb hierarchy is invalid. Bone transforms in a limb do not belong to the same ancestry. Please make sure the bones are parented to each other. " +
  388. "Bones: " + bone1.name + ", " + bone2.name + ", " + bone3.name;
  389. return true;
  390. }
  391. return false;
  392. }
  393. // Check if the limb is properly set up
  394. private static bool LimbWarning(Transform bone1, Transform bone2, Transform bone3, ref string warningMessage) {
  395. Vector3 cross = Vector3.Cross(bone2.position - bone1.position, bone3.position - bone1.position);
  396. if (cross == Vector3.zero) {
  397. warningMessage = "BipedReferences limb is completely stretched out in the initial pose. IK solver can not calculate the default bend plane for the limb. " +
  398. "Please make sure you character's limbs are at least slightly bent in the initial pose. " +
  399. "First bone: " + bone1.name + ", second bone: " + bone2.name + ".";
  400. return true;
  401. }
  402. return false;
  403. }
  404. // Check if spine is properly set up
  405. private static bool SpineError(BipedReferences references, ref string errorMessage) {
  406. // No spine might be a valid setup in some cases
  407. if (references.spine.Length == 0) return false;
  408. for (int i = 0; i < references.spine.Length; i++) {
  409. if (references.spine[i] == null) {
  410. errorMessage = "BipedReferences spine bone at index " + i + " is null.";
  411. return true;
  412. }
  413. }
  414. Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(references.spine);
  415. if (duplicate != null) {
  416. errorMessage = duplicate.name + " is represented multiple times in BipedReferences spine.";
  417. return true;
  418. }
  419. if (!Hierarchy.HierarchyIsValid(references.spine)) {
  420. errorMessage = "BipedReferences spine hierarchy is invalid. Bone transforms in the spine do not belong to the same ancestry. Please make sure the bones are parented to each other.";
  421. return true;
  422. }
  423. for (int i = 0; i < references.spine.Length; i++) {
  424. bool matchesParentPosition = false;
  425. if (i == 0 && references.spine[i].position == references.pelvis.position) matchesParentPosition = true;
  426. if (i != 0 && references.spine.Length > 1 && references.spine[i].position == references.spine[i - 1].position) matchesParentPosition = true;
  427. if (matchesParentPosition) {
  428. errorMessage = "Biped's spine bone nr " + i + " position is the same as it's parent spine/pelvis bone's position. Please remove this bone from the spine.";
  429. return true;
  430. }
  431. }
  432. return false;
  433. }
  434. // Check if spine is properly set up
  435. private static bool SpineWarning(BipedReferences references, ref string warningMessage) {
  436. // Maybe need to add something here in the future
  437. return false;
  438. }
  439. // Check if eyes are properly set up
  440. private static bool EyesError(BipedReferences references, ref string errorMessage) {
  441. // No eyes might be a valid setup
  442. if (references.eyes.Length == 0) return false;
  443. for (int i = 0; i < references.eyes.Length; i++) {
  444. if (references.eyes[i] == null) {
  445. errorMessage = "BipedReferences eye bone at index " + i + " is null.";
  446. return true;
  447. }
  448. }
  449. Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(references.eyes);
  450. if (duplicate != null) {
  451. errorMessage = duplicate.name + " is represented multiple times in BipedReferences eyes.";
  452. return true;
  453. }
  454. return false;
  455. }
  456. // Check if eyes are properly set up
  457. private static bool EyesWarning(BipedReferences references, ref string warningMessage) {
  458. // Maybe need to add something here in the future
  459. return false;
  460. }
  461. // Check if BipedIK transform position is at the character's feet
  462. private static bool RootHeightWarning(BipedReferences references, ref string warningMessage) {
  463. if (references.head == null) return false;
  464. float headHeight = GetVerticalOffset(references.head.position, references.leftFoot.position, references.root.rotation);
  465. float rootHeight = GetVerticalOffset(references.root.position, references.leftFoot.position, references.root.rotation);
  466. if (rootHeight / headHeight > 0.2f) {
  467. warningMessage = "Biped's root Transform's position should be at ground level relative to the character (at the character's feet not at it's pelvis).";
  468. return true;
  469. }
  470. return false;
  471. }
  472. // Check if the character is facing the correct axis
  473. private static bool FacingAxisWarning(BipedReferences references, ref string warningMessage) {
  474. Vector3 handsLeftToRight = references.rightHand.position - references.leftHand.position;
  475. Vector3 feetLeftToRight = references.rightFoot.position - references.leftFoot.position;
  476. float dotHands = Vector3.Dot(handsLeftToRight.normalized, references.root.right);
  477. float dotFeet = Vector3.Dot(feetLeftToRight.normalized, references.root.right);
  478. if (dotHands < 0 || dotFeet < 0) {
  479. warningMessage = "Biped does not seem to be facing it's forward axis. " +
  480. "Please make sure that in the initial pose the character is facing towards the positive Z axis of the Biped root gameobject.";
  481. return true;
  482. }
  483. return false;
  484. }
  485. // Gets vertical offset relative to a rotation
  486. private static float GetVerticalOffset(Vector3 p1, Vector3 p2, Quaternion rotation) {
  487. Vector3 v = Quaternion.Inverse(rotation) * (p1 - p2);
  488. return v.y;
  489. }
  490. }
  491. }