IKSolverFullBodyBiped.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// Full body biped IK effector types.
  7. /// </summary>
  8. [System.Serializable]
  9. public enum FullBodyBipedEffector {
  10. Body,
  11. LeftShoulder,
  12. RightShoulder,
  13. LeftThigh,
  14. RightThigh,
  15. LeftHand,
  16. RightHand,
  17. LeftFoot,
  18. RightFoot
  19. }
  20. /// <summary>
  21. /// Full body biped IK chain types.
  22. /// </summary>
  23. [System.Serializable]
  24. public enum FullBodyBipedChain {
  25. LeftArm,
  26. RightArm,
  27. LeftLeg,
  28. RightLeg
  29. }
  30. /// <summary>
  31. /// FBIK solver specialized to biped characters.
  32. /// </summary>
  33. [System.Serializable]
  34. public class IKSolverFullBodyBiped : IKSolverFullBody {
  35. #region Main Interface
  36. /// <summary>
  37. /// The central root node (body).
  38. /// </summary>
  39. public Transform rootNode;
  40. /// <summary>
  41. /// The stiffness of spine constraints.
  42. /// </summary>
  43. [Range(0f, 1f)]
  44. public float spineStiffness = 0.5f;
  45. /// <summary>
  46. /// Weight of hand effectors pulling the body vertically (relative to root rotation).
  47. /// </summary>
  48. [Range(-1f, 1f)]
  49. public float pullBodyVertical = 0.5f;
  50. /// <summary>
  51. /// Weight of hand effectors pulling the body horizontally (relative to root rotation).
  52. /// </summary>
  53. [Range(-1f, 1f)]
  54. public float pullBodyHorizontal = 0f;
  55. /// <summary>
  56. /// Gets the body effector.
  57. /// </summary>
  58. public IKEffector bodyEffector { get { return GetEffector(FullBodyBipedEffector.Body); }}
  59. /// <summary>
  60. /// Gets the left shoulder effector.
  61. /// </summary>
  62. public IKEffector leftShoulderEffector { get { return GetEffector(FullBodyBipedEffector.LeftShoulder); }}
  63. /// <summary>
  64. /// Gets the right shoulder effector.
  65. /// </summary>
  66. public IKEffector rightShoulderEffector { get { return GetEffector(FullBodyBipedEffector.RightShoulder); }}
  67. /// <summary>
  68. /// Gets the left thigh effector.
  69. /// </summary>
  70. public IKEffector leftThighEffector { get { return GetEffector(FullBodyBipedEffector.LeftThigh); }}
  71. /// <summary>
  72. /// Gets the right thigh effector.
  73. /// </summary>
  74. public IKEffector rightThighEffector { get { return GetEffector(FullBodyBipedEffector.RightThigh); }}
  75. /// <summary>
  76. /// Gets the left hand effector.
  77. /// </summary>
  78. public IKEffector leftHandEffector { get { return GetEffector(FullBodyBipedEffector.LeftHand); }}
  79. /// <summary>
  80. /// Gets the right hand effector.
  81. /// </summary>
  82. public IKEffector rightHandEffector { get { return GetEffector(FullBodyBipedEffector.RightHand); }}
  83. /// <summary>
  84. /// Gets the left foot effector.
  85. /// </summary>
  86. public IKEffector leftFootEffector { get { return GetEffector(FullBodyBipedEffector.LeftFoot); }}
  87. /// <summary>
  88. /// Gets the right foot effector.
  89. /// </summary>
  90. public IKEffector rightFootEffector { get { return GetEffector(FullBodyBipedEffector.RightFoot); }}
  91. /// <summary>
  92. /// Gets the left arm chain.
  93. /// </summary>
  94. public FBIKChain leftArmChain { get { return chain[1]; }}
  95. /// <summary>
  96. /// Gets the right arm chain.
  97. /// </summary>
  98. public FBIKChain rightArmChain { get { return chain[2]; }}
  99. /// <summary>
  100. /// Gets the left leg chain.
  101. /// </summary>
  102. public FBIKChain leftLegChain { get { return chain[3]; }}
  103. /// <summary>
  104. /// Gets the right leg chain.
  105. /// </summary>
  106. public FBIKChain rightLegChain { get { return chain[4]; }}
  107. /// <summary>
  108. /// Gets the left arm IK mapping.
  109. /// </summary>
  110. public IKMappingLimb leftArmMapping { get { return limbMappings[0]; }}
  111. /// <summary>
  112. /// Gets the right arm IK mapping.
  113. /// </summary>
  114. public IKMappingLimb rightArmMapping { get { return limbMappings[1]; }}
  115. /// <summary>
  116. /// Gets the left leg IK mapping.
  117. /// </summary>
  118. public IKMappingLimb leftLegMapping { get { return limbMappings[2]; }}
  119. /// <summary>
  120. /// Gets the right leg IK mapping.
  121. /// </summary>
  122. public IKMappingLimb rightLegMapping { get { return limbMappings[3]; }}
  123. /// <summary>
  124. /// Gets the head IK mapping.
  125. /// </summary>
  126. public IKMappingBone headMapping { get { return boneMappings[0]; }}
  127. /// <summary>
  128. /// Sets chain weights for the specified chain.
  129. /// </summary>
  130. public void SetChainWeights(FullBodyBipedChain c, float pull, float reach = 0f) {
  131. GetChain(c).pull = pull;
  132. GetChain(c).reach = reach;
  133. }
  134. /// <summary>
  135. /// Sets effector weights for the specified effector.
  136. /// </summary>
  137. public void SetEffectorWeights(FullBodyBipedEffector effector, float positionWeight, float rotationWeight) {
  138. GetEffector(effector).positionWeight = Mathf.Clamp(positionWeight, 0f, 1f);
  139. GetEffector(effector).rotationWeight = Mathf.Clamp(rotationWeight, 0f, 1f);
  140. }
  141. /// <summary>
  142. /// Gets the chain of a limb.
  143. /// </summary>
  144. public FBIKChain GetChain(FullBodyBipedChain c) {
  145. switch(c) {
  146. case FullBodyBipedChain.LeftArm: return chain[1];
  147. case FullBodyBipedChain.RightArm: return chain[2];
  148. case FullBodyBipedChain.LeftLeg: return chain[3];
  149. case FullBodyBipedChain.RightLeg: return chain[4];
  150. }
  151. return null;
  152. }
  153. /// <summary>
  154. /// Gets the chain of the specified effector.
  155. /// </summary>
  156. public FBIKChain GetChain(FullBodyBipedEffector effector) {
  157. switch(effector) {
  158. case FullBodyBipedEffector.Body: return chain[0];
  159. case FullBodyBipedEffector.LeftShoulder: return chain[1];
  160. case FullBodyBipedEffector.RightShoulder: return chain[2];
  161. case FullBodyBipedEffector.LeftThigh: return chain[3];
  162. case FullBodyBipedEffector.RightThigh: return chain[4];
  163. case FullBodyBipedEffector.LeftHand: return chain[1];
  164. case FullBodyBipedEffector.RightHand: return chain[2];
  165. case FullBodyBipedEffector.LeftFoot: return chain[3];
  166. case FullBodyBipedEffector.RightFoot: return chain[4];
  167. }
  168. return null;
  169. }
  170. /// <summary>
  171. /// Gets the effector of type.
  172. /// </summary>
  173. public IKEffector GetEffector(FullBodyBipedEffector effector) {
  174. switch(effector) {
  175. case FullBodyBipedEffector.Body: return effectors[0];
  176. case FullBodyBipedEffector.LeftShoulder: return effectors[1];
  177. case FullBodyBipedEffector.RightShoulder: return effectors[2];
  178. case FullBodyBipedEffector.LeftThigh: return effectors[3];
  179. case FullBodyBipedEffector.RightThigh: return effectors[4];
  180. case FullBodyBipedEffector.LeftHand: return effectors[5];
  181. case FullBodyBipedEffector.RightHand: return effectors[6];
  182. case FullBodyBipedEffector.LeftFoot: return effectors[7];
  183. case FullBodyBipedEffector.RightFoot: return effectors[8];
  184. }
  185. return null;
  186. }
  187. /// <summary>
  188. /// Gets the effector of type.
  189. /// </summary>
  190. public IKEffector GetEndEffector(FullBodyBipedChain c) {
  191. switch(c) {
  192. case FullBodyBipedChain.LeftArm: return effectors[5];
  193. case FullBodyBipedChain.RightArm: return effectors[6];
  194. case FullBodyBipedChain.LeftLeg: return effectors[7];
  195. case FullBodyBipedChain.RightLeg: return effectors[8];
  196. }
  197. return null;
  198. }
  199. /// <summary>
  200. /// Gets the limb mapping for the limb.
  201. /// </summary>
  202. public IKMappingLimb GetLimbMapping(FullBodyBipedChain chain) {
  203. switch(chain) {
  204. case FullBodyBipedChain.LeftArm: return limbMappings[0];
  205. case FullBodyBipedChain.RightArm: return limbMappings[1];
  206. case FullBodyBipedChain.LeftLeg: return limbMappings[2];
  207. case FullBodyBipedChain.RightLeg: return limbMappings[3];
  208. }
  209. return null;
  210. }
  211. /// <summary>
  212. /// Gets the limb mapping for the effector type.
  213. /// </summary>
  214. public IKMappingLimb GetLimbMapping(FullBodyBipedEffector effector) {
  215. switch(effector) {
  216. case FullBodyBipedEffector.LeftShoulder: return limbMappings[0];
  217. case FullBodyBipedEffector.RightShoulder: return limbMappings[1];
  218. case FullBodyBipedEffector.LeftThigh: return limbMappings[2];
  219. case FullBodyBipedEffector.RightThigh: return limbMappings[3];
  220. case FullBodyBipedEffector.LeftHand: return limbMappings[0];
  221. case FullBodyBipedEffector.RightHand: return limbMappings[1];
  222. case FullBodyBipedEffector.LeftFoot: return limbMappings[2];
  223. case FullBodyBipedEffector.RightFoot: return limbMappings[3];
  224. default: return null;
  225. }
  226. }
  227. /// <summary>
  228. /// Gets the spine mapping.
  229. /// </summary>
  230. public IKMappingSpine GetSpineMapping() {
  231. return spineMapping;
  232. }
  233. /// <summary>
  234. /// Gets the head mapping.
  235. /// </summary>
  236. public IKMappingBone GetHeadMapping() {
  237. return boneMappings[0];
  238. }
  239. /// <summary>
  240. /// Gets the bend constraint of a limb.
  241. /// </summary>
  242. public IKConstraintBend GetBendConstraint(FullBodyBipedChain limb) {
  243. switch(limb) {
  244. case FullBodyBipedChain.LeftArm: return chain[1].bendConstraint;
  245. case FullBodyBipedChain.RightArm: return chain[2].bendConstraint;
  246. case FullBodyBipedChain.LeftLeg: return chain[3].bendConstraint;
  247. case FullBodyBipedChain.RightLeg: return chain[4].bendConstraint;
  248. }
  249. return null;
  250. }
  251. public override bool IsValid(ref string message) {
  252. if (!base.IsValid(ref message)) return false;
  253. if (rootNode == null) {
  254. message = "Root Node bone is null. FBBIK will not initiate.";
  255. return false;
  256. }
  257. if (chain.Length != 5 ||
  258. chain[0].nodes.Length != 1 ||
  259. chain[1].nodes.Length != 3 ||
  260. chain[2].nodes.Length != 3 ||
  261. chain[3].nodes.Length != 3 ||
  262. chain[4].nodes.Length != 3 ||
  263. effectors.Length != 9 ||
  264. limbMappings.Length != 4
  265. ) {
  266. message = "Invalid FBBIK setup. Please right-click on the component header and select 'Reinitiate'.";
  267. return false;
  268. }
  269. return true;
  270. }
  271. /// <summary>
  272. /// Sets up the solver to BipedReferences and reinitiates (if in runtime).
  273. /// </summary>
  274. /// <param name="references">Biped references.</param>
  275. /// <param name="rootNode">Root node (optional). if null, will try to detect the root node bone automatically. </param>
  276. public void SetToReferences(BipedReferences references, Transform rootNode = null) {
  277. root = references.root;
  278. if (rootNode == null) rootNode = DetectRootNodeBone(references);
  279. this.rootNode = rootNode;
  280. // Root Node
  281. if (chain == null || chain.Length != 5) chain = new FBIKChain[5];
  282. for (int i = 0; i < chain.Length; i++) {
  283. if (chain[i] == null) {
  284. chain[i] = new FBIKChain();
  285. }
  286. }
  287. chain[0].pin = 0f;
  288. chain[0].SetNodes(rootNode);
  289. chain[0].children = new int[4] { 1, 2, 3, 4 };
  290. // Left Arm
  291. chain[1].SetNodes(references.leftUpperArm, references.leftForearm, references.leftHand);
  292. // Right Arm
  293. chain[2].SetNodes(references.rightUpperArm, references.rightForearm, references.rightHand);
  294. // Left Leg
  295. chain[3].SetNodes(references.leftThigh, references.leftCalf, references.leftFoot);
  296. // Right Leg
  297. chain[4].SetNodes(references.rightThigh, references.rightCalf, references.rightFoot);
  298. // Effectors
  299. if (effectors.Length != 9) effectors = new IKEffector[9] {
  300. new IKEffector(), new IKEffector(), new IKEffector(), new IKEffector(), new IKEffector(), new IKEffector(), new IKEffector(), new IKEffector(), new IKEffector()
  301. };
  302. effectors[0].bone = rootNode;
  303. effectors[0].childBones = new Transform[2] { references.leftThigh, references.rightThigh };
  304. effectors[1].bone = references.leftUpperArm;
  305. effectors[2].bone = references.rightUpperArm;
  306. effectors[3].bone = references.leftThigh;
  307. effectors[4].bone = references.rightThigh;
  308. effectors[5].bone = references.leftHand;
  309. effectors[6].bone = references.rightHand;
  310. effectors[7].bone = references.leftFoot;
  311. effectors[8].bone = references.rightFoot;
  312. effectors[5].planeBone1 = references.leftUpperArm;
  313. effectors[5].planeBone2 = references.rightUpperArm;
  314. effectors[5].planeBone3 = rootNode;
  315. effectors[6].planeBone1 = references.rightUpperArm;
  316. effectors[6].planeBone2 = references.leftUpperArm;
  317. effectors[6].planeBone3 = rootNode;
  318. effectors[7].planeBone1 = references.leftThigh;
  319. effectors[7].planeBone2 = references.rightThigh;
  320. effectors[7].planeBone3 = rootNode;
  321. effectors[8].planeBone1 = references.rightThigh;
  322. effectors[8].planeBone2 = references.leftThigh;
  323. effectors[8].planeBone3 = rootNode;
  324. // Child Constraints
  325. chain[0].childConstraints = new FBIKChain.ChildConstraint[4] {
  326. new FBIKChain.ChildConstraint(references.leftUpperArm, references.rightThigh, 0f, 1f),
  327. new FBIKChain.ChildConstraint(references.rightUpperArm, references.leftThigh, 0f, 1f),
  328. new FBIKChain.ChildConstraint(references.leftUpperArm, references.rightUpperArm),
  329. new FBIKChain.ChildConstraint(references.leftThigh, references.rightThigh)
  330. };
  331. // IKMappingSpine
  332. Transform[] spineBones = new Transform[references.spine.Length + 1];
  333. spineBones[0] = references.pelvis;
  334. for (int i = 0; i < references.spine.Length; i++) {
  335. spineBones[i + 1] = references.spine[i];
  336. }
  337. if (spineMapping == null) {
  338. spineMapping = new IKMappingSpine();
  339. spineMapping.iterations = 3;
  340. }
  341. spineMapping.SetBones(spineBones, references.leftUpperArm, references.rightUpperArm, references.leftThigh, references.rightThigh);
  342. // IKMappingBone
  343. int boneMappingsCount = references.head != null? 1: 0;
  344. if (boneMappings.Length != boneMappingsCount) {
  345. boneMappings = new IKMappingBone[boneMappingsCount];
  346. for (int i = 0; i < boneMappings.Length; i++) {
  347. boneMappings[i] = new IKMappingBone();
  348. }
  349. if (boneMappingsCount == 1) boneMappings[0].maintainRotationWeight = 0f;
  350. }
  351. if (boneMappings.Length > 0) boneMappings[0].bone = references.head;
  352. // IKMappingLimb
  353. if (limbMappings.Length != 4) {
  354. limbMappings = new IKMappingLimb[4] {
  355. new IKMappingLimb(), new IKMappingLimb(), new IKMappingLimb(), new IKMappingLimb()
  356. };
  357. limbMappings[2].maintainRotationWeight = 1f;
  358. limbMappings[3].maintainRotationWeight = 1f;
  359. }
  360. limbMappings[0].SetBones(references.leftUpperArm, references.leftForearm, references.leftHand, GetLeftClavicle(references));
  361. limbMappings[1].SetBones(references.rightUpperArm, references.rightForearm, references.rightHand, GetRightClavicle(references));
  362. limbMappings[2].SetBones(references.leftThigh, references.leftCalf, references.leftFoot);
  363. limbMappings[3].SetBones(references.rightThigh, references.rightCalf, references.rightFoot);
  364. if (Application.isPlaying) Initiate(references.root);
  365. }
  366. /*
  367. * Tries to guess which bone should be the root node
  368. * */
  369. public static Transform DetectRootNodeBone(BipedReferences references) {
  370. if (!references.isFilled) return null;
  371. if (references.spine.Length < 1) return null;
  372. int spineLength = references.spine.Length;
  373. if (spineLength == 1) return references.spine[0];
  374. Vector3 hip = Vector3.Lerp(references.leftThigh.position, references.rightThigh.position, 0.5f);
  375. Vector3 neck = Vector3.Lerp(references.leftUpperArm.position, references.rightUpperArm.position, 0.5f);
  376. Vector3 toNeck = neck - hip;
  377. float toNeckMag = toNeck.magnitude;
  378. if (references.spine.Length < 2) return references.spine[0];
  379. int rootNodeBone = 0;
  380. for (int i = 1; i < spineLength; i++) {
  381. Vector3 hipToBone = references.spine[i].position - hip;
  382. Vector3 projection = Vector3.Project(hipToBone, toNeck);
  383. float dot = Vector3.Dot(projection.normalized, toNeck.normalized);
  384. if (dot > 0) {
  385. float mag = projection.magnitude / toNeckMag;
  386. if (mag < 0.5f) rootNodeBone = i;
  387. }
  388. }
  389. return references.spine[rootNodeBone];
  390. }
  391. /// <summary>
  392. /// Sets the bend directions of the limbs to the local axes specified by BipedLimbOrientations.
  393. /// </summary>
  394. public void SetLimbOrientations(BipedLimbOrientations o) {
  395. SetLimbOrientation(FullBodyBipedChain.LeftArm, o.leftArm);
  396. SetLimbOrientation(FullBodyBipedChain.RightArm, o.rightArm);
  397. SetLimbOrientation(FullBodyBipedChain.LeftLeg, o.leftLeg);
  398. SetLimbOrientation(FullBodyBipedChain.RightLeg, o.rightLeg);
  399. }
  400. #endregion Main Interface
  401. // Offset applied to the body effector by PullBody
  402. public Vector3 pullBodyOffset { get; private set; }
  403. /*
  404. * Sets the bend direction of a limb to the local axes specified by the LimbOrientation.
  405. * */
  406. private void SetLimbOrientation(FullBodyBipedChain chain, BipedLimbOrientations.LimbOrientation limbOrientation) {
  407. bool inverse = chain == FullBodyBipedChain.LeftArm || chain == FullBodyBipedChain.RightArm;
  408. if (inverse) {
  409. GetBendConstraint(chain).SetLimbOrientation(-limbOrientation.upperBoneForwardAxis, -limbOrientation.lowerBoneForwardAxis, -limbOrientation.lastBoneLeftAxis);
  410. GetLimbMapping(chain).SetLimbOrientation(-limbOrientation.upperBoneForwardAxis, -limbOrientation.lowerBoneForwardAxis);
  411. } else {
  412. GetBendConstraint(chain).SetLimbOrientation(limbOrientation.upperBoneForwardAxis, limbOrientation.lowerBoneForwardAxis, limbOrientation.lastBoneLeftAxis);
  413. GetLimbMapping(chain).SetLimbOrientation(limbOrientation.upperBoneForwardAxis, limbOrientation.lowerBoneForwardAxis);
  414. }
  415. }
  416. private static Transform GetLeftClavicle(BipedReferences references) {
  417. if (references.leftUpperArm == null) return null;
  418. if (!Contains(references.spine, references.leftUpperArm.parent)) return references.leftUpperArm.parent;
  419. return null;
  420. }
  421. private static Transform GetRightClavicle(BipedReferences references) {
  422. if (references.rightUpperArm == null) return null;
  423. if (!Contains(references.spine, references.rightUpperArm.parent)) return references.rightUpperArm.parent;
  424. return null;
  425. }
  426. private static bool Contains(Transform[] array, Transform transform) {
  427. foreach (Transform t in array) if (t == transform) return true;
  428. return false;
  429. }
  430. protected override void ReadPose() {
  431. // Set effectors to their targets
  432. for (int i = 0; i < effectors.Length; i++) effectors[i].SetToTarget();
  433. // Pulling the body with the hands
  434. PullBody();
  435. // Spine stiffness
  436. float s = Mathf.Clamp(1f - spineStiffness, 0f, 1f);
  437. chain[0].childConstraints[0].pushElasticity = s;
  438. chain[0].childConstraints[1].pushElasticity = s;
  439. base.ReadPose();
  440. }
  441. /*
  442. * Pulling the body with the hands
  443. * */
  444. private void PullBody() {
  445. if (iterations < 1) return;
  446. // Getting the body positionOffset
  447. if (pullBodyVertical != 0f || pullBodyHorizontal != 0f) {
  448. Vector3 offset = GetBodyOffset();
  449. pullBodyOffset = V3Tools.ExtractVertical(offset, root.up, pullBodyVertical) + V3Tools.ExtractHorizontal(offset, root.up, pullBodyHorizontal);
  450. bodyEffector.positionOffset += pullBodyOffset;
  451. }
  452. }
  453. /*
  454. * Get pull offset of the body effector.
  455. * */
  456. private Vector3 GetBodyOffset() {
  457. Vector3 offset = Vector3.zero + GetHandBodyPull(leftHandEffector, leftArmChain, Vector3.zero) * Mathf.Clamp(leftHandEffector.positionWeight, 0f, 1f);
  458. return offset + GetHandBodyPull(rightHandEffector, rightArmChain, offset) * Mathf.Clamp(rightHandEffector.positionWeight, 0f, 1f);
  459. }
  460. /*
  461. * Get pull offset of a hand
  462. * */
  463. private Vector3 GetHandBodyPull(IKEffector effector, FBIKChain arm, Vector3 offset) {
  464. // Get the vector from shoulder to hand effector
  465. Vector3 direction = effector.position - (arm.nodes[0].transform.position + offset);
  466. float armLength = arm.nodes[0].length + arm.nodes[1].length;
  467. // Find delta of effector distance and arm length
  468. float dirMag = direction.magnitude;
  469. if (dirMag < armLength) return Vector3.zero;
  470. float x = dirMag - armLength;
  471. return (direction / dirMag) * x;
  472. }
  473. private Vector3 offset;
  474. protected override void ApplyBendConstraints() {
  475. if (iterations > 0) {
  476. chain[1].bendConstraint.rotationOffset = leftHandEffector.planeRotationOffset;
  477. chain[2].bendConstraint.rotationOffset = rightHandEffector.planeRotationOffset;
  478. chain[3].bendConstraint.rotationOffset = leftFootEffector.planeRotationOffset;
  479. chain[4].bendConstraint.rotationOffset = rightFootEffector.planeRotationOffset;
  480. } else {
  481. offset = Vector3.Lerp(effectors[0].positionOffset, effectors[0].position - (effectors[0].bone.position + effectors[0].positionOffset), effectors[0].positionWeight);
  482. for (int i = 0; i < 5; i++) {
  483. effectors[i].GetNode(this).solverPosition += offset;
  484. }
  485. }
  486. base.ApplyBendConstraints();
  487. }
  488. protected override void WritePose() {
  489. if (iterations == 0) {
  490. spineMapping.spineBones[0].position += offset;
  491. }
  492. base.WritePose();
  493. }
  494. }
  495. }