FreeFBIKEffector.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. using System;
  2. using RootMotion;
  3. using RootMotion.FinalIK;
  4. using UnityEngine;
  5. [Serializable]
  6. public class FreeFBIKEffector
  7. {
  8. public FreeFBIKEffector(FullBodyBipedIK ik_ctrl, Transform bone, Transform ik_target, bool is_upper)
  9. {
  10. this.ik = ik_ctrl;
  11. this.Bone = bone;
  12. this.Target = ik_target;
  13. this.IsUpperBody = is_upper;
  14. IKSolverFullBodyBiped solver = this.ik.solver;
  15. solver.OnStoreDefaultLocalState = (IKSolver.UpdateDelegate)Delegate.Combine(solver.OnStoreDefaultLocalState, new IKSolver.UpdateDelegate(this.OnStoreDefaultLocalState));
  16. IKSolverFullBodyBiped solver2 = this.ik.solver;
  17. solver2.OnFixTransforms = (IKSolver.UpdateDelegate)Delegate.Combine(solver2.OnFixTransforms, new IKSolver.UpdateDelegate(this.OnFixTransforms));
  18. this.boneRotationRelativeToRoot = Quaternion.Inverse(this.ik.references.root.rotation) * this.Bone.rotation;
  19. }
  20. public Vector3 TargetPos
  21. {
  22. get
  23. {
  24. return this.Target.position;
  25. }
  26. }
  27. private Transform m_leftNearBone
  28. {
  29. get
  30. {
  31. return (!this.IsUpperBody) ? this.ik.references.leftThigh : this.ik.references.leftUpperArm;
  32. }
  33. }
  34. private Transform m_rightNearBone
  35. {
  36. get
  37. {
  38. return (!this.IsUpperBody) ? this.ik.references.rightThigh : this.ik.references.rightUpperArm;
  39. }
  40. }
  41. private IKEffector m_leftNearEffector
  42. {
  43. get
  44. {
  45. return (!this.IsUpperBody) ? this.ik.solver.leftThighEffector : this.ik.solver.leftShoulderEffector;
  46. }
  47. }
  48. private IKEffector m_rightNearEffector
  49. {
  50. get
  51. {
  52. return (!this.IsUpperBody) ? this.ik.solver.rightThighEffector : this.ik.solver.rightShoulderEffector;
  53. }
  54. }
  55. private Transform m_leftFarBone
  56. {
  57. get
  58. {
  59. return (!this.IsUpperBody) ? this.ik.references.leftUpperArm : this.ik.references.leftThigh;
  60. }
  61. }
  62. private Transform m_rightFarBone
  63. {
  64. get
  65. {
  66. return (!this.IsUpperBody) ? this.ik.references.rightUpperArm : this.ik.references.rightThigh;
  67. }
  68. }
  69. private IKEffector m_leftFarEffector
  70. {
  71. get
  72. {
  73. return (!this.IsUpperBody) ? this.ik.solver.leftShoulderEffector : this.ik.solver.leftThighEffector;
  74. }
  75. }
  76. private IKEffector m_rightFarEffector
  77. {
  78. get
  79. {
  80. return (!this.IsUpperBody) ? this.ik.solver.rightShoulderEffector : this.ik.solver.rightThighEffector;
  81. }
  82. }
  83. private void OnStoreDefaultLocalState()
  84. {
  85. if (this.positionWeight <= 0f)
  86. {
  87. return;
  88. }
  89. foreach (FreeFBIKEffector.BendBone bendBone in this.bendBones)
  90. {
  91. if (bendBone != null)
  92. {
  93. bendBone.StoreDefaultLocalState();
  94. }
  95. }
  96. this.ccdDefaultLocalRotations = new Quaternion[this.CCDBones.Length];
  97. for (int j = 0; j < this.CCDBones.Length; j++)
  98. {
  99. if (this.CCDBones[j] != null)
  100. {
  101. this.ccdDefaultLocalRotations[j] = this.CCDBones[j].localRotation;
  102. }
  103. }
  104. this.boneLocalPosition = this.Bone.localPosition;
  105. this.boneLocalRotation = this.Bone.localRotation;
  106. this.stretchLocalPositions = new Vector3[this.stretchBones.Length];
  107. this.stretchLocalRotations = new Quaternion[this.stretchBones.Length];
  108. for (int k = 0; k < this.stretchBones.Length; k++)
  109. {
  110. if (this.stretchBones[k] != null)
  111. {
  112. this.stretchLocalPositions[k] = this.stretchBones[k].localPosition;
  113. this.stretchLocalRotations[k] = this.stretchBones[k].localRotation;
  114. }
  115. }
  116. this.chestLocalPositions = new Vector3[this.chestBones.Length];
  117. this.chestLocalRotations = new Quaternion[this.chestBones.Length];
  118. for (int l = 0; l < this.chestBones.Length; l++)
  119. {
  120. if (this.chestBones[l] != null)
  121. {
  122. this.chestLocalPositions[l] = this.chestBones[l].localPosition;
  123. this.chestLocalRotations[l] = this.chestBones[l].localRotation;
  124. }
  125. }
  126. this.bendBonesCount = this.bendBones.Length;
  127. this.ccdBonesCount = this.CCDBones.Length;
  128. this.stretchBonesCount = this.stretchBones.Length;
  129. this.chestBonesCount = this.chestBones.Length;
  130. }
  131. private void OnFixTransforms()
  132. {
  133. if (this.positionWeight <= 0f)
  134. {
  135. return;
  136. }
  137. foreach (FreeFBIKEffector.BendBone bendBone in this.bendBones)
  138. {
  139. if (bendBone != null)
  140. {
  141. bendBone.FixTransforms();
  142. }
  143. }
  144. for (int j = 0; j < this.CCDBones.Length; j++)
  145. {
  146. if (this.CCDBones[j] != null)
  147. {
  148. this.CCDBones[j].localRotation = this.ccdDefaultLocalRotations[j];
  149. }
  150. }
  151. this.Bone.localPosition = this.boneLocalPosition;
  152. this.Bone.localRotation = this.boneLocalRotation;
  153. for (int k = 0; k < this.stretchBones.Length; k++)
  154. {
  155. if (this.stretchBones[k] != null)
  156. {
  157. this.stretchBones[k].localPosition = this.stretchLocalPositions[k];
  158. this.stretchBones[k].localRotation = this.stretchLocalRotations[k];
  159. }
  160. }
  161. for (int l = 0; l < this.chestBones.Length; l++)
  162. {
  163. if (this.chestBones[l] != null)
  164. {
  165. this.chestBones[l].localPosition = this.chestLocalPositions[l];
  166. this.chestBones[l].localRotation = this.chestLocalRotations[l];
  167. }
  168. }
  169. }
  170. public void OnPreRead()
  171. {
  172. if (this.positionWeight <= 0f)
  173. {
  174. return;
  175. }
  176. if (this.bendBonesCount != this.bendBones.Length || this.ccdBonesCount != this.CCDBones.Length || this.stretchBonesCount != this.stretchBones.Length || this.chestBonesCount != this.chestBones.Length)
  177. {
  178. this.OnStoreDefaultLocalState();
  179. }
  180. this.ChestDirection();
  181. this.SpineBend();
  182. this.CCDPass();
  183. this.offset = this.TargetPos - this.Bone.position;
  184. this.nearBoneDist = Vector3.Distance(this.m_leftNearBone.position, this.m_rightNearBone.position);
  185. this.leftNearBoneDist = Vector3.Distance(this.Bone.position, this.m_leftNearBone.position);
  186. this.rightNearBoneDist = Vector3.Distance(this.Bone.position, this.m_rightNearBone.position);
  187. this.boneToBody = this.ik.solver.rootNode.position - this.Bone.position;
  188. this.boneToLeftFarBone = this.m_leftFarBone.position - this.Bone.position;
  189. this.boneToRightFarBone = this.m_rightFarBone.position - this.Bone.position;
  190. this.leftNearBonePos = this.m_leftNearBone.position + this.offset * this.bodyWeight;
  191. this.rightNearBonePos = this.m_rightNearBone.position + this.offset * this.bodyWeight;
  192. this.chestRotation = Quaternion.LookRotation(this.Bone.position - this.m_leftNearBone.position, this.m_rightNearBone.position - this.m_leftNearBone.position);
  193. }
  194. private void SpineBend()
  195. {
  196. float num = this.bendWeight * this.ik.solver.IKPositionWeight;
  197. if (num <= 0f)
  198. {
  199. return;
  200. }
  201. if (this.bendBones.Length == 0)
  202. {
  203. return;
  204. }
  205. Quaternion quaternion = this.Target.rotation * Quaternion.Inverse(this.ik.references.root.rotation * this.boneRotationRelativeToRoot);
  206. quaternion = QuaTools.ClampRotation(quaternion, this.bodyClampWeight, 2);
  207. float num2 = 1f / (float)this.bendBones.Length;
  208. for (int i = 0; i < this.bendBones.Length; i++)
  209. {
  210. if (this.bendBones[i].transform != null)
  211. {
  212. this.bendBones[i].transform.rotation = Quaternion.Lerp(Quaternion.identity, quaternion, num2 * this.bendBones[i].weight * num) * this.bendBones[i].transform.rotation;
  213. }
  214. }
  215. }
  216. private void CCDPass()
  217. {
  218. float num = this.CCDWeight * this.ik.solver.IKPositionWeight;
  219. if (num <= 0f)
  220. {
  221. return;
  222. }
  223. for (int i = this.CCDBones.Length - 1; i > -1; i--)
  224. {
  225. Quaternion quaternion = Quaternion.FromToRotation(this.Bone.position - this.CCDBones[i].position, this.TargetPos - this.CCDBones[i].position) * this.CCDBones[i].rotation;
  226. float num2 = Mathf.Lerp((float)((this.CCDBones.Length - i) / this.CCDBones.Length), 1f, this.roll);
  227. float num3 = Quaternion.Angle(Quaternion.identity, quaternion);
  228. num3 = Mathf.Lerp(0f, num3, (this.damper - num3) / this.damper);
  229. this.CCDBones[i].rotation = Quaternion.RotateTowards(this.CCDBones[i].rotation, quaternion, num3 * num * num2);
  230. }
  231. }
  232. public void Iterate()
  233. {
  234. if (this.positionWeight <= 0f)
  235. {
  236. return;
  237. }
  238. this.leftNearBonePos = this.TargetPos + (this.leftNearBonePos - this.TargetPos).normalized * this.leftNearBoneDist;
  239. this.rightNearBonePos = this.TargetPos + (this.rightNearBonePos - this.TargetPos).normalized * this.rightNearBoneDist;
  240. this.Solve(ref this.leftNearBonePos, ref this.rightNearBonePos, this.nearBoneDist);
  241. this.LerpSolverPosition(this.m_leftNearEffector, this.leftNearBonePos, this.positionWeight * this.ik.solver.IKPositionWeight, this.m_leftNearEffector.positionOffset);
  242. this.LerpSolverPosition(this.m_rightNearEffector, this.rightNearBonePos, this.positionWeight * this.ik.solver.IKPositionWeight, this.m_rightNearEffector.positionOffset);
  243. Quaternion to = Quaternion.LookRotation(this.TargetPos - this.leftNearBonePos, this.rightNearBonePos - this.leftNearBonePos);
  244. Quaternion quaternion = QuaTools.FromToRotation(this.chestRotation, to);
  245. Vector3 b = quaternion * this.boneToBody;
  246. this.LerpSolverPosition(this.ik.solver.bodyEffector, this.TargetPos + b, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.bodyEffector.positionOffset - this.ik.solver.pullBodyOffset);
  247. Quaternion rotation = Quaternion.Lerp(Quaternion.identity, quaternion, this.farBoneWeight);
  248. Vector3 b2 = rotation * this.boneToLeftFarBone;
  249. Vector3 b3 = rotation * this.boneToRightFarBone;
  250. this.LerpSolverPosition(this.m_leftFarEffector, this.TargetPos + b2, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.bodyEffector.positionOffset - this.ik.solver.pullBodyOffset + this.ik.solver.leftThighEffector.positionOffset);
  251. this.LerpSolverPosition(this.m_rightFarEffector, this.TargetPos + b3, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.bodyEffector.positionOffset - this.ik.solver.pullBodyOffset + this.m_rightFarEffector.positionOffset);
  252. }
  253. private void ChestDirection()
  254. {
  255. float num = this.chestDirectionWeight * this.ik.solver.IKPositionWeight;
  256. if (num <= 0f)
  257. {
  258. return;
  259. }
  260. bool flag = false;
  261. this.chestDirection = V3Tools.ClampDirection(this.chestDirection, this.ik.references.root.forward, 0.45f, 2, out flag);
  262. if (this.chestDirection == Vector3.zero)
  263. {
  264. return;
  265. }
  266. Quaternion quaternion = Quaternion.FromToRotation(this.ik.references.root.forward, this.chestDirection);
  267. quaternion = Quaternion.Lerp(Quaternion.identity, quaternion, num * (1f / (float)this.chestBones.Length));
  268. foreach (Transform transform in this.chestBones)
  269. {
  270. transform.rotation = quaternion * transform.rotation;
  271. }
  272. }
  273. private void PostStretching()
  274. {
  275. float num = this.postStretchWeight * this.ik.solver.IKPositionWeight;
  276. if (num > 0f)
  277. {
  278. Vector3 a = Vector3.ClampMagnitude(this.TargetPos - this.Bone.position, this.maxStretch);
  279. a *= num;
  280. this.stretchDamper = Mathf.Max(this.stretchDamper, 0f);
  281. if (this.stretchDamper > 0f)
  282. {
  283. a /= (1f + a.magnitude) * (1f + this.stretchDamper);
  284. }
  285. for (int i = 0; i < this.stretchBones.Length; i++)
  286. {
  287. if (this.stretchBones[i] != null)
  288. {
  289. this.stretchBones[i].position += a / (float)this.stretchBones.Length;
  290. }
  291. }
  292. }
  293. if (this.fixBone && this.ik.solver.IKPositionWeight > 0f)
  294. {
  295. this.Bone.position = this.TargetPos;
  296. }
  297. }
  298. private void LerpSolverPosition(IKEffector effector, Vector3 position, float weight, Vector3 offset)
  299. {
  300. if (effector.target)
  301. {
  302. Vector3 position2 = effector.target.position;
  303. effector.target.position = Vector3.Lerp(effector.target.position, position + offset, weight);
  304. }
  305. else
  306. {
  307. effector.GetNode(this.ik.solver).solverPosition = Vector3.Lerp(effector.bone.position, position + offset, weight);
  308. }
  309. }
  310. private void Solve(ref Vector3 pos1, ref Vector3 pos2, float nominalDistance)
  311. {
  312. Vector3 a = pos2 - pos1;
  313. float magnitude = a.magnitude;
  314. if (magnitude == nominalDistance)
  315. {
  316. return;
  317. }
  318. if (magnitude == 0f)
  319. {
  320. return;
  321. }
  322. float num = 1f;
  323. num *= 1f - nominalDistance / magnitude;
  324. Vector3 b = a * num * 0.5f;
  325. pos1 += b;
  326. pos2 -= b;
  327. }
  328. [Tooltip("Reference to the FBBIK component.")]
  329. public FullBodyBipedIK ik;
  330. [LargeHeader("Position")]
  331. [Tooltip("Master weight for positioning the head.")]
  332. [Range(0f, 1f)]
  333. public float positionWeight = 1f;
  334. [Tooltip("The weight of moving the body along with the head")]
  335. [Range(0f, 1f)]
  336. public float bodyWeight = 1f;
  337. [Tooltip("The weight of moving the thighs along with the head")]
  338. [Range(0f, 1f)]
  339. public float farBoneWeight = 1f;
  340. [LargeHeader("Rotation")]
  341. [Tooltip("Clamping the rotation of the body")]
  342. [Range(0f, 1f)]
  343. public float bodyClampWeight = 0.5f;
  344. [Tooltip("Clamping the rotation of the head")]
  345. [Range(0f, 1f)]
  346. public float boneClampWeight = 0.5f;
  347. [Tooltip("The master weight of bending/twisting the spine to the rotation of the head effector. This is similar to CCD, but uses the rotation of the head effector not the position.")]
  348. [Range(0f, 1f)]
  349. public float bendWeight = 1f;
  350. [Tooltip("The bones to use for bending.")]
  351. public FreeFBIKEffector.BendBone[] bendBones = new FreeFBIKEffector.BendBone[0];
  352. [LargeHeader("CCD")]
  353. [Tooltip("Optional. The master weight of the CCD (Cyclic Coordinate Descent) IK effect that bends the spine towards the head effector before FBBIK solves.")]
  354. [Range(0f, 1f)]
  355. public float CCDWeight = 1f;
  356. [Tooltip("The weight of rolling the bones in towards the target")]
  357. [Range(0f, 1f)]
  358. public float roll;
  359. [Tooltip("Smoothing the CCD effect.")]
  360. [Range(0f, 1000f)]
  361. public float damper = 500f;
  362. [Tooltip("Bones to use for the CCD pass. Assign spine and/or neck bones.")]
  363. public Transform[] CCDBones = new Transform[0];
  364. [LargeHeader("Stretching")]
  365. [Tooltip("Stretching the spine/neck to help reach the target. This is useful for making sure the head stays locked relative to the VR headset. NB! Stretching is done after FBBIK has solved so if you have the hand effectors pinned and spine bones included in the 'Stretch Bones', the hands might become offset from their target positions.")]
  366. [Range(0f, 1f)]
  367. public float postStretchWeight = 1f;
  368. [Tooltip("Stretch magnitude limit.")]
  369. public float maxStretch = 0.1f;
  370. [Tooltip("If > 0, dampers the stretching effect.")]
  371. public float stretchDamper;
  372. [Tooltip("If true, will fix head position to this Transform no matter what. Good for making sure the head will not budge away from the VR headset")]
  373. public bool fixBone;
  374. [Tooltip("Bones to use for stretching. The more bones you add, the less noticable the effect.")]
  375. public Transform[] stretchBones = new Transform[0];
  376. [LargeHeader("Chest Direction")]
  377. public Vector3 chestDirection = Vector3.forward;
  378. [Range(0f, 1f)]
  379. public float chestDirectionWeight = 1f;
  380. public Transform[] chestBones = new Transform[0];
  381. private Vector3 offset;
  382. private Vector3 boneToBody;
  383. private Vector3 nearCenterToBone;
  384. private Vector3 boneToLeftFarBone;
  385. private Vector3 boneToRightFarBone;
  386. private Vector3 leftNearBonePos;
  387. private Vector3 rightNearBonePos;
  388. private float nearBoneDist;
  389. private float leftNearBoneDist;
  390. private float rightNearBoneDist;
  391. private Quaternion chestRotation;
  392. private Quaternion boneRotationRelativeToRoot;
  393. private Quaternion[] ccdDefaultLocalRotations = new Quaternion[0];
  394. private Vector3 boneLocalPosition;
  395. private Quaternion boneLocalRotation;
  396. private Vector3[] stretchLocalPositions = new Vector3[0];
  397. private Quaternion[] stretchLocalRotations = new Quaternion[0];
  398. private Vector3[] chestLocalPositions = new Vector3[0];
  399. private Quaternion[] chestLocalRotations = new Quaternion[0];
  400. private int bendBonesCount;
  401. private int ccdBonesCount;
  402. private int stretchBonesCount;
  403. private int chestBonesCount;
  404. public readonly bool IsUpperBody;
  405. public readonly Transform Target;
  406. public readonly Transform Bone;
  407. [Serializable]
  408. public class BendBone
  409. {
  410. public BendBone()
  411. {
  412. }
  413. public BendBone(Transform transform, float weight)
  414. {
  415. this.transform = transform;
  416. this.weight = weight;
  417. }
  418. public void StoreDefaultLocalState()
  419. {
  420. this.defaultLocalRotation = this.transform.localRotation;
  421. }
  422. public void FixTransforms()
  423. {
  424. this.transform.localRotation = this.defaultLocalRotation;
  425. }
  426. [Tooltip("Assign spine and/or neck bones.")]
  427. public Transform transform;
  428. [Tooltip("The weight of rotating this bone.")]
  429. [Range(0f, 1f)]
  430. public float weight = 0.5f;
  431. private Quaternion defaultLocalRotation = Quaternion.identity;
  432. }
  433. }