boneIKController.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import { Bone } from "./bone";
  2. import { Vector3, Quaternion, Matrix } from "../Maths/math.vector";
  3. import { AbstractMesh } from "../Meshes/abstractMesh";
  4. import { Nullable } from "../types";
  5. import { Space } from '../Maths/math.axis';
  6. /**
  7. * Class used to apply inverse kinematics to bones
  8. * @see https://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons#boneikcontroller
  9. */
  10. export class BoneIKController {
  11. private static _tmpVecs: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
  12. private static _tmpQuat = Quaternion.Identity();
  13. private static _tmpMats: Matrix[] = [Matrix.Identity(), Matrix.Identity()];
  14. /**
  15. * Gets or sets the target mesh
  16. */
  17. public targetMesh: AbstractMesh;
  18. /** Gets or sets the mesh used as pole */
  19. public poleTargetMesh: AbstractMesh;
  20. /**
  21. * Gets or sets the bone used as pole
  22. */
  23. public poleTargetBone: Nullable<Bone>;
  24. /**
  25. * Gets or sets the target position
  26. */
  27. public targetPosition = Vector3.Zero();
  28. /**
  29. * Gets or sets the pole target position
  30. */
  31. public poleTargetPosition = Vector3.Zero();
  32. /**
  33. * Gets or sets the pole target local offset
  34. */
  35. public poleTargetLocalOffset = Vector3.Zero();
  36. /**
  37. * Gets or sets the pole angle
  38. */
  39. public poleAngle = 0;
  40. /**
  41. * Gets or sets the mesh associated with the controller
  42. */
  43. public mesh: AbstractMesh;
  44. /**
  45. * The amount to slerp (spherical linear interpolation) to the target. Set this to a value between 0 and 1 (a value of 1 disables slerp)
  46. */
  47. public slerpAmount = 1;
  48. private _bone1Quat = Quaternion.Identity();
  49. private _bone1Mat = Matrix.Identity();
  50. private _bone2Ang = Math.PI;
  51. private _bone1: Nullable<Bone>;
  52. private _bone2: Bone;
  53. private _bone1Length: number;
  54. private _bone2Length: number;
  55. private _maxAngle = Math.PI;
  56. private _maxReach: number;
  57. private _rightHandedSystem = false;
  58. private _bendAxis = Vector3.Right();
  59. private _slerping = false;
  60. private _adjustRoll = 0;
  61. /**
  62. * Gets or sets maximum allowed angle
  63. */
  64. public get maxAngle(): number {
  65. return this._maxAngle;
  66. }
  67. public set maxAngle(value: number) {
  68. this._setMaxAngle(value);
  69. }
  70. /**
  71. * Creates a new BoneIKController
  72. * @param mesh defines the mesh to control
  73. * @param bone defines the bone to control
  74. * @param options defines options to set up the controller
  75. */
  76. constructor(mesh: AbstractMesh,
  77. bone: Bone,
  78. options?: {
  79. targetMesh?: AbstractMesh,
  80. poleTargetMesh?: AbstractMesh,
  81. poleTargetBone?: Bone,
  82. poleTargetLocalOffset?: Vector3,
  83. poleAngle?: number,
  84. bendAxis?: Vector3,
  85. maxAngle?: number,
  86. slerpAmount?: number
  87. }) {
  88. this._bone2 = bone;
  89. this._bone1 = bone.getParent();
  90. if (!this._bone1) {
  91. return;
  92. }
  93. this.mesh = mesh;
  94. var bonePos = bone.getPosition();
  95. if (bone.getAbsoluteTransform().determinant() > 0) {
  96. this._rightHandedSystem = true;
  97. this._bendAxis.x = 0;
  98. this._bendAxis.y = 0;
  99. this._bendAxis.z = -1;
  100. if (bonePos.x > bonePos.y && bonePos.x > bonePos.z) {
  101. this._adjustRoll = Math.PI * .5;
  102. this._bendAxis.z = 1;
  103. }
  104. }
  105. if (this._bone1.length) {
  106. var boneScale1 = this._bone1.getScale();
  107. var boneScale2 = this._bone2.getScale();
  108. this._bone1Length = this._bone1.length * boneScale1.y * this.mesh.scaling.y;
  109. this._bone2Length = this._bone2.length * boneScale2.y * this.mesh.scaling.y;
  110. } else if (this._bone1.children[0]) {
  111. mesh.computeWorldMatrix(true);
  112. var pos1 = this._bone2.children[0].getAbsolutePosition(mesh);
  113. var pos2 = this._bone2.getAbsolutePosition(mesh);
  114. var pos3 = this._bone1.getAbsolutePosition(mesh);
  115. this._bone1Length = Vector3.Distance(pos1, pos2);
  116. this._bone2Length = Vector3.Distance(pos2, pos3);
  117. }
  118. this._bone1.getRotationMatrixToRef(Space.WORLD, mesh, this._bone1Mat);
  119. this.maxAngle = Math.PI;
  120. if (options) {
  121. if (options.targetMesh) {
  122. this.targetMesh = options.targetMesh;
  123. this.targetMesh.computeWorldMatrix(true);
  124. }
  125. if (options.poleTargetMesh) {
  126. this.poleTargetMesh = options.poleTargetMesh;
  127. this.poleTargetMesh.computeWorldMatrix(true);
  128. } else if (options.poleTargetBone) {
  129. this.poleTargetBone = options.poleTargetBone;
  130. } else if (this._bone1.getParent()) {
  131. this.poleTargetBone = this._bone1.getParent();
  132. }
  133. if (options.poleTargetLocalOffset) {
  134. this.poleTargetLocalOffset.copyFrom(options.poleTargetLocalOffset);
  135. }
  136. if (options.poleAngle) {
  137. this.poleAngle = options.poleAngle;
  138. }
  139. if (options.bendAxis) {
  140. this._bendAxis.copyFrom(options.bendAxis);
  141. }
  142. if (options.maxAngle) {
  143. this.maxAngle = options.maxAngle;
  144. }
  145. if (options.slerpAmount) {
  146. this.slerpAmount = options.slerpAmount;
  147. }
  148. }
  149. }
  150. private _setMaxAngle(ang: number): void {
  151. if (ang < 0) {
  152. ang = 0;
  153. }
  154. if (ang > Math.PI || ang == undefined) {
  155. ang = Math.PI;
  156. }
  157. this._maxAngle = ang;
  158. var a = this._bone1Length;
  159. var b = this._bone2Length;
  160. this._maxReach = Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(ang));
  161. }
  162. /**
  163. * Force the controller to update the bones
  164. */
  165. public update(): void {
  166. var bone1 = this._bone1;
  167. if (!bone1) {
  168. return;
  169. }
  170. var target = this.targetPosition;
  171. var poleTarget = this.poleTargetPosition;
  172. var mat1 = BoneIKController._tmpMats[0];
  173. var mat2 = BoneIKController._tmpMats[1];
  174. if (this.targetMesh) {
  175. target.copyFrom(this.targetMesh.getAbsolutePosition());
  176. }
  177. if (this.poleTargetBone) {
  178. this.poleTargetBone.getAbsolutePositionFromLocalToRef(this.poleTargetLocalOffset, this.mesh, poleTarget);
  179. } else if (this.poleTargetMesh) {
  180. Vector3.TransformCoordinatesToRef(this.poleTargetLocalOffset, this.poleTargetMesh.getWorldMatrix(), poleTarget);
  181. }
  182. var bonePos = BoneIKController._tmpVecs[0];
  183. var zaxis = BoneIKController._tmpVecs[1];
  184. var xaxis = BoneIKController._tmpVecs[2];
  185. var yaxis = BoneIKController._tmpVecs[3];
  186. var upAxis = BoneIKController._tmpVecs[4];
  187. var _tmpQuat = BoneIKController._tmpQuat;
  188. bone1.getAbsolutePositionToRef(this.mesh, bonePos);
  189. poleTarget.subtractToRef(bonePos, upAxis);
  190. if (upAxis.x == 0 && upAxis.y == 0 && upAxis.z == 0) {
  191. upAxis.y = 1;
  192. } else {
  193. upAxis.normalize();
  194. }
  195. target.subtractToRef(bonePos, yaxis);
  196. yaxis.normalize();
  197. Vector3.CrossToRef(yaxis, upAxis, zaxis);
  198. zaxis.normalize();
  199. Vector3.CrossToRef(yaxis, zaxis, xaxis);
  200. xaxis.normalize();
  201. Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
  202. var a = this._bone1Length;
  203. var b = this._bone2Length;
  204. var c = Vector3.Distance(bonePos, target);
  205. if (this._maxReach > 0) {
  206. c = Math.min(this._maxReach, c);
  207. }
  208. var acosa = (b * b + c * c - a * a) / (2 * b * c);
  209. var acosb = (c * c + a * a - b * b) / (2 * c * a);
  210. if (acosa > 1) {
  211. acosa = 1;
  212. }
  213. if (acosb > 1) {
  214. acosb = 1;
  215. }
  216. if (acosa < -1) {
  217. acosa = -1;
  218. }
  219. if (acosb < -1) {
  220. acosb = -1;
  221. }
  222. var angA = Math.acos(acosa);
  223. var angB = Math.acos(acosb);
  224. var angC = -angA - angB;
  225. if (this._rightHandedSystem) {
  226. Matrix.RotationYawPitchRollToRef(0, 0, this._adjustRoll, mat2);
  227. mat2.multiplyToRef(mat1, mat1);
  228. Matrix.RotationAxisToRef(this._bendAxis, angB, mat2);
  229. mat2.multiplyToRef(mat1, mat1);
  230. } else {
  231. var _tmpVec = BoneIKController._tmpVecs[5];
  232. _tmpVec.copyFrom(this._bendAxis);
  233. _tmpVec.x *= -1;
  234. Matrix.RotationAxisToRef(_tmpVec, -angB, mat2);
  235. mat2.multiplyToRef(mat1, mat1);
  236. }
  237. if (this.poleAngle) {
  238. Matrix.RotationAxisToRef(yaxis, this.poleAngle, mat2);
  239. mat1.multiplyToRef(mat2, mat1);
  240. }
  241. if (this._bone1) {
  242. if (this.slerpAmount < 1) {
  243. if (!this._slerping) {
  244. Quaternion.FromRotationMatrixToRef(this._bone1Mat, this._bone1Quat);
  245. }
  246. Quaternion.FromRotationMatrixToRef(mat1, _tmpQuat);
  247. Quaternion.SlerpToRef(this._bone1Quat, _tmpQuat, this.slerpAmount, this._bone1Quat);
  248. angC = this._bone2Ang * (1.0 - this.slerpAmount) + angC * this.slerpAmount;
  249. this._bone1.setRotationQuaternion(this._bone1Quat, Space.WORLD, this.mesh);
  250. this._slerping = true;
  251. } else {
  252. this._bone1.setRotationMatrix(mat1, Space.WORLD, this.mesh);
  253. this._bone1Mat.copyFrom(mat1);
  254. this._slerping = false;
  255. }
  256. }
  257. this._bone2.setAxisAngle(this._bendAxis, angC, Space.LOCAL);
  258. this._bone2Ang = angC;
  259. }
  260. }