bone.ts 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  1. import { Skeleton } from "./skeleton";
  2. import { Vector3, Quaternion, Matrix } from "../Maths/math.vector";
  3. import { ArrayTools } from "../Misc/arrayTools";
  4. import { Nullable } from "../types";
  5. import { AbstractMesh } from "../Meshes/abstractMesh";
  6. import { TransformNode } from "../Meshes/transformNode";
  7. import { Node } from "../node";
  8. import { Space } from '../Maths/math.axis';
  9. declare type Animation = import("../Animations/animation").Animation;
  10. declare type AnimationPropertiesOverride = import("../Animations/animationPropertiesOverride").AnimationPropertiesOverride;
  11. /**
  12. * Class used to store bone information
  13. * @see https://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons
  14. */
  15. export class Bone extends Node {
  16. private static _tmpVecs: Vector3[] = ArrayTools.BuildArray(2, Vector3.Zero);
  17. private static _tmpQuat = Quaternion.Identity();
  18. private static _tmpMats: Matrix[] = ArrayTools.BuildArray(5, Matrix.Identity);
  19. /**
  20. * Gets the list of child bones
  21. */
  22. public children = new Array<Bone>();
  23. /** Gets the animations associated with this bone */
  24. public animations = new Array<Animation>();
  25. /**
  26. * Gets or sets bone length
  27. */
  28. public length: number;
  29. /**
  30. * @hidden Internal only
  31. * Set this value to map this bone to a different index in the transform matrices
  32. * Set this value to -1 to exclude the bone from the transform matrices
  33. */
  34. public _index: Nullable<number> = null;
  35. private _skeleton: Skeleton;
  36. private _localMatrix: Matrix;
  37. private _restPose: Matrix;
  38. private _bindPose: Matrix;
  39. private _baseMatrix: Matrix;
  40. private _absoluteTransform = new Matrix();
  41. private _invertedAbsoluteTransform = new Matrix();
  42. private _parent: Nullable<Bone>;
  43. private _scalingDeterminant = 1;
  44. private _worldTransform = new Matrix();
  45. private _localScaling: Vector3;
  46. private _localRotation: Quaternion;
  47. private _localPosition: Vector3;
  48. private _needToDecompose = true;
  49. private _needToCompose = false;
  50. /** @hidden */
  51. public _linkedTransformNode: Nullable<TransformNode> = null;
  52. /** @hidden */
  53. public _waitingTransformNodeId: Nullable<string> = null;
  54. /** @hidden */
  55. get _matrix(): Matrix {
  56. this._compose();
  57. return this._localMatrix;
  58. }
  59. /** @hidden */
  60. set _matrix(value: Matrix) {
  61. this._localMatrix.copyFrom(value);
  62. this._needToDecompose = true;
  63. }
  64. /**
  65. * Create a new bone
  66. * @param name defines the bone name
  67. * @param skeleton defines the parent skeleton
  68. * @param parentBone defines the parent (can be null if the bone is the root)
  69. * @param localMatrix defines the local matrix
  70. * @param restPose defines the rest pose matrix
  71. * @param baseMatrix defines the base matrix
  72. * @param index defines index of the bone in the hiearchy
  73. */
  74. constructor(
  75. /**
  76. * defines the bone name
  77. */
  78. public name: string, skeleton: Skeleton, parentBone: Nullable<Bone> = null, localMatrix: Nullable<Matrix> = null,
  79. restPose: Nullable<Matrix> = null, baseMatrix: Nullable<Matrix> = null, index: Nullable<number> = null) {
  80. super(name, skeleton.getScene());
  81. this._skeleton = skeleton;
  82. this._localMatrix = localMatrix ? localMatrix.clone() : Matrix.Identity();
  83. this._restPose = restPose ? restPose : this._localMatrix.clone();
  84. this._bindPose = this._localMatrix.clone();
  85. this._baseMatrix = baseMatrix ? baseMatrix : this._localMatrix.clone();
  86. this._index = index;
  87. skeleton.bones.push(this);
  88. this.setParent(parentBone, false);
  89. if (baseMatrix || localMatrix) {
  90. this._updateDifferenceMatrix();
  91. }
  92. }
  93. /**
  94. * Gets the current object class name.
  95. * @return the class name
  96. */
  97. public getClassName(): string {
  98. return "Bone";
  99. }
  100. // Members
  101. /**
  102. * Gets the parent skeleton
  103. * @returns a skeleton
  104. */
  105. public getSkeleton(): Skeleton {
  106. return this._skeleton;
  107. }
  108. /**
  109. * Gets parent bone
  110. * @returns a bone or null if the bone is the root of the bone hierarchy
  111. */
  112. public getParent(): Nullable<Bone> {
  113. return this._parent;
  114. }
  115. /**
  116. * Returns an array containing the root bones
  117. * @returns an array containing the root bones
  118. */
  119. public getChildren(): Array<Bone> {
  120. return this.children;
  121. }
  122. /**
  123. * Gets the node index in matrix array generated for rendering
  124. * @returns the node index
  125. */
  126. public getIndex(): number {
  127. return this._index === null ? this.getSkeleton().bones.indexOf(this) : this._index;
  128. }
  129. /**
  130. * Sets the parent bone
  131. * @param parent defines the parent (can be null if the bone is the root)
  132. * @param updateDifferenceMatrix defines if the difference matrix must be updated
  133. */
  134. public setParent(parent: Nullable<Bone>, updateDifferenceMatrix: boolean = true): void {
  135. if (this._parent === parent) {
  136. return;
  137. }
  138. if (this._parent) {
  139. var index = this._parent.children.indexOf(this);
  140. if (index !== -1) {
  141. this._parent.children.splice(index, 1);
  142. }
  143. }
  144. this._parent = parent;
  145. if (this._parent) {
  146. this._parent.children.push(this);
  147. }
  148. if (updateDifferenceMatrix) {
  149. this._updateDifferenceMatrix();
  150. }
  151. this.markAsDirty();
  152. }
  153. /**
  154. * Gets the local matrix
  155. * @returns a matrix
  156. */
  157. public getLocalMatrix(): Matrix {
  158. this._compose();
  159. return this._localMatrix;
  160. }
  161. /**
  162. * Gets the base matrix (initial matrix which remains unchanged)
  163. * @returns a matrix
  164. */
  165. public getBaseMatrix(): Matrix {
  166. return this._baseMatrix;
  167. }
  168. /**
  169. * Gets the rest pose matrix
  170. * @returns a matrix
  171. */
  172. public getRestPose(): Matrix {
  173. return this._restPose;
  174. }
  175. /**
  176. * Sets the rest pose matrix
  177. * @param matrix the local-space rest pose to set for this bone
  178. */
  179. public setRestPose(matrix: Matrix): void {
  180. this._restPose.copyFrom(matrix);
  181. }
  182. /**
  183. * Gets the bind pose matrix
  184. * @returns the bind pose matrix
  185. */
  186. public getBindPose(): Matrix {
  187. return this._bindPose;
  188. }
  189. /**
  190. * Sets the bind pose matrix
  191. * @param matrix the local-space bind pose to set for this bone
  192. */
  193. public setBindPose(matrix: Matrix): void {
  194. this._bindPose.copyFrom(matrix);
  195. }
  196. /**
  197. * Gets a matrix used to store world matrix (ie. the matrix sent to shaders)
  198. */
  199. public getWorldMatrix(): Matrix {
  200. return this._worldTransform;
  201. }
  202. /**
  203. * Sets the local matrix to rest pose matrix
  204. */
  205. public returnToRest(): void {
  206. if (this._skeleton._numBonesWithLinkedTransformNode > 0) {
  207. this.updateMatrix(this._restPose, false, false);
  208. } else {
  209. this.updateMatrix(this._restPose, false, true);
  210. }
  211. }
  212. /**
  213. * Gets the inverse of the absolute transform matrix.
  214. * This matrix will be multiplied by local matrix to get the difference matrix (ie. the difference between original state and current state)
  215. * @returns a matrix
  216. */
  217. public getInvertedAbsoluteTransform(): Matrix {
  218. return this._invertedAbsoluteTransform;
  219. }
  220. /**
  221. * Gets the absolute transform matrix (ie base matrix * parent world matrix)
  222. * @returns a matrix
  223. */
  224. public getAbsoluteTransform(): Matrix {
  225. return this._absoluteTransform;
  226. }
  227. /**
  228. * Links with the given transform node.
  229. * The local matrix of this bone is copied from the transform node every frame.
  230. * @param transformNode defines the transform node to link to
  231. */
  232. public linkTransformNode(transformNode: Nullable<TransformNode>): void {
  233. if (this._linkedTransformNode) {
  234. this._skeleton._numBonesWithLinkedTransformNode--;
  235. }
  236. this._linkedTransformNode = transformNode;
  237. if (this._linkedTransformNode) {
  238. this._skeleton._numBonesWithLinkedTransformNode++;
  239. }
  240. }
  241. // Properties (matches AbstractMesh properties)
  242. /**
  243. * Gets the node used to drive the bone's transformation
  244. * @returns a transform node or null
  245. */
  246. public getTransformNode() {
  247. return this._linkedTransformNode;
  248. }
  249. /** Gets or sets current position (in local space) */
  250. public get position(): Vector3 {
  251. this._decompose();
  252. return this._localPosition;
  253. }
  254. public set position(newPosition: Vector3) {
  255. this._decompose();
  256. this._localPosition.copyFrom(newPosition);
  257. this._markAsDirtyAndCompose();
  258. }
  259. /** Gets or sets current rotation (in local space) */
  260. public get rotation(): Vector3 {
  261. return this.getRotation();
  262. }
  263. public set rotation(newRotation: Vector3) {
  264. this.setRotation(newRotation);
  265. }
  266. /** Gets or sets current rotation quaternion (in local space) */
  267. public get rotationQuaternion() {
  268. this._decompose();
  269. return this._localRotation;
  270. }
  271. public set rotationQuaternion(newRotation: Quaternion) {
  272. this.setRotationQuaternion(newRotation);
  273. }
  274. /** Gets or sets current scaling (in local space) */
  275. public get scaling(): Vector3 {
  276. return this.getScale();
  277. }
  278. public set scaling(newScaling: Vector3) {
  279. this.setScale(newScaling);
  280. }
  281. /**
  282. * Gets the animation properties override
  283. */
  284. public get animationPropertiesOverride(): Nullable<AnimationPropertiesOverride> {
  285. return this._skeleton.animationPropertiesOverride;
  286. }
  287. // Methods
  288. private _decompose() {
  289. if (!this._needToDecompose) {
  290. return;
  291. }
  292. this._needToDecompose = false;
  293. if (!this._localScaling) {
  294. this._localScaling = Vector3.Zero();
  295. this._localRotation = Quaternion.Zero();
  296. this._localPosition = Vector3.Zero();
  297. }
  298. this._localMatrix.decompose(this._localScaling, this._localRotation, this._localPosition);
  299. }
  300. private _compose() {
  301. if (!this._needToCompose) {
  302. return;
  303. }
  304. if (!this._localScaling) {
  305. this._needToCompose = false;
  306. return;
  307. }
  308. this._needToCompose = false;
  309. Matrix.ComposeToRef(this._localScaling, this._localRotation, this._localPosition, this._localMatrix);
  310. }
  311. /**
  312. * Update the base and local matrices
  313. * @param matrix defines the new base or local matrix
  314. * @param updateDifferenceMatrix defines if the difference matrix must be updated
  315. * @param updateLocalMatrix defines if the local matrix should be updated
  316. */
  317. public updateMatrix(matrix: Matrix, updateDifferenceMatrix = true, updateLocalMatrix = true): void {
  318. this._baseMatrix.copyFrom(matrix);
  319. if (updateDifferenceMatrix) {
  320. this._updateDifferenceMatrix();
  321. }
  322. if (updateLocalMatrix) {
  323. this._needToCompose = false; // in case there was a pending compose
  324. this._localMatrix.copyFrom(matrix);
  325. this._markAsDirtyAndDecompose();
  326. }
  327. else {
  328. this.markAsDirty();
  329. }
  330. }
  331. /** @hidden */
  332. public _updateDifferenceMatrix(rootMatrix?: Matrix, updateChildren = true): void {
  333. if (!rootMatrix) {
  334. rootMatrix = this._baseMatrix;
  335. }
  336. if (this._parent) {
  337. rootMatrix.multiplyToRef(this._parent._absoluteTransform, this._absoluteTransform);
  338. } else {
  339. this._absoluteTransform.copyFrom(rootMatrix);
  340. }
  341. this._absoluteTransform.invertToRef(this._invertedAbsoluteTransform);
  342. if (updateChildren) {
  343. for (var index = 0; index < this.children.length; index++) {
  344. this.children[index]._updateDifferenceMatrix();
  345. }
  346. }
  347. this._scalingDeterminant = (this._absoluteTransform.determinant() < 0 ? -1 : 1);
  348. }
  349. /**
  350. * Flag the bone as dirty (Forcing it to update everything)
  351. */
  352. public markAsDirty(): void {
  353. this._currentRenderId++;
  354. this._childUpdateId++;
  355. this._skeleton._markAsDirty();
  356. }
  357. /** @hidden */
  358. public _markAsDirtyAndCompose() {
  359. this.markAsDirty();
  360. this._needToCompose = true;
  361. }
  362. private _markAsDirtyAndDecompose() {
  363. this.markAsDirty();
  364. this._needToDecompose = true;
  365. }
  366. /**
  367. * Translate the bone in local or world space
  368. * @param vec The amount to translate the bone
  369. * @param space The space that the translation is in
  370. * @param mesh The mesh that this bone is attached to. This is only used in world space
  371. */
  372. public translate(vec: Vector3, space = Space.LOCAL, mesh?: AbstractMesh): void {
  373. var lm = this.getLocalMatrix();
  374. if (space == Space.LOCAL) {
  375. lm.addAtIndex(12, vec.x);
  376. lm.addAtIndex(13, vec.y);
  377. lm.addAtIndex(14, vec.z);
  378. } else {
  379. var wm: Nullable<Matrix> = null;
  380. //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
  381. if (mesh) {
  382. wm = mesh.getWorldMatrix();
  383. }
  384. this._skeleton.computeAbsoluteTransforms();
  385. var tmat = Bone._tmpMats[0];
  386. var tvec = Bone._tmpVecs[0];
  387. if (this._parent) {
  388. if (mesh && wm) {
  389. tmat.copyFrom(this._parent.getAbsoluteTransform());
  390. tmat.multiplyToRef(wm, tmat);
  391. } else {
  392. tmat.copyFrom(this._parent.getAbsoluteTransform());
  393. }
  394. } else {
  395. Matrix.IdentityToRef(tmat);
  396. }
  397. tmat.setTranslationFromFloats(0, 0, 0);
  398. tmat.invert();
  399. Vector3.TransformCoordinatesToRef(vec, tmat, tvec);
  400. lm.addAtIndex(12, tvec.x);
  401. lm.addAtIndex(13, tvec.y);
  402. lm.addAtIndex(14, tvec.z);
  403. }
  404. this._markAsDirtyAndDecompose();
  405. }
  406. /**
  407. * Set the postion of the bone in local or world space
  408. * @param position The position to set the bone
  409. * @param space The space that the position is in
  410. * @param mesh The mesh that this bone is attached to. This is only used in world space
  411. */
  412. public setPosition(position: Vector3, space = Space.LOCAL, mesh?: AbstractMesh): void {
  413. var lm = this.getLocalMatrix();
  414. if (space == Space.LOCAL) {
  415. lm.setTranslationFromFloats(position.x, position.y, position.z);
  416. } else {
  417. var wm: Nullable<Matrix> = null;
  418. //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
  419. if (mesh) {
  420. wm = mesh.getWorldMatrix();
  421. }
  422. this._skeleton.computeAbsoluteTransforms();
  423. var tmat = Bone._tmpMats[0];
  424. var vec = Bone._tmpVecs[0];
  425. if (this._parent) {
  426. if (mesh && wm) {
  427. tmat.copyFrom(this._parent.getAbsoluteTransform());
  428. tmat.multiplyToRef(wm, tmat);
  429. } else {
  430. tmat.copyFrom(this._parent.getAbsoluteTransform());
  431. }
  432. tmat.invert();
  433. } else {
  434. Matrix.IdentityToRef(tmat);
  435. }
  436. Vector3.TransformCoordinatesToRef(position, tmat, vec);
  437. lm.setTranslationFromFloats(vec.x, vec.y, vec.z);
  438. }
  439. this._markAsDirtyAndDecompose();
  440. }
  441. /**
  442. * Set the absolute position of the bone (world space)
  443. * @param position The position to set the bone
  444. * @param mesh The mesh that this bone is attached to
  445. */
  446. public setAbsolutePosition(position: Vector3, mesh?: AbstractMesh) {
  447. this.setPosition(position, Space.WORLD, mesh);
  448. }
  449. /**
  450. * Scale the bone on the x, y and z axes (in local space)
  451. * @param x The amount to scale the bone on the x axis
  452. * @param y The amount to scale the bone on the y axis
  453. * @param z The amount to scale the bone on the z axis
  454. * @param scaleChildren sets this to true if children of the bone should be scaled as well (false by default)
  455. */
  456. public scale(x: number, y: number, z: number, scaleChildren = false): void {
  457. var locMat = this.getLocalMatrix();
  458. // Apply new scaling on top of current local matrix
  459. var scaleMat = Bone._tmpMats[0];
  460. Matrix.ScalingToRef(x, y, z, scaleMat);
  461. scaleMat.multiplyToRef(locMat, locMat);
  462. // Invert scaling matrix and apply the inverse to all children
  463. scaleMat.invert();
  464. for (var child of this.children) {
  465. var cm = child.getLocalMatrix();
  466. cm.multiplyToRef(scaleMat, cm);
  467. cm.multiplyAtIndex(12, x);
  468. cm.multiplyAtIndex(13, y);
  469. cm.multiplyAtIndex(14, z);
  470. child._markAsDirtyAndDecompose();
  471. }
  472. this._markAsDirtyAndDecompose();
  473. if (scaleChildren) {
  474. for (var child of this.children) {
  475. child.scale(x, y, z, scaleChildren);
  476. }
  477. }
  478. }
  479. /**
  480. * Set the bone scaling in local space
  481. * @param scale defines the scaling vector
  482. */
  483. public setScale(scale: Vector3): void {
  484. this._decompose();
  485. this._localScaling.copyFrom(scale);
  486. this._markAsDirtyAndCompose();
  487. }
  488. /**
  489. * Gets the current scaling in local space
  490. * @returns the current scaling vector
  491. */
  492. public getScale(): Vector3 {
  493. this._decompose();
  494. return this._localScaling;
  495. }
  496. /**
  497. * Gets the current scaling in local space and stores it in a target vector
  498. * @param result defines the target vector
  499. */
  500. public getScaleToRef(result: Vector3) {
  501. this._decompose();
  502. result.copyFrom(this._localScaling);
  503. }
  504. /**
  505. * Set the yaw, pitch, and roll of the bone in local or world space
  506. * @param yaw The rotation of the bone on the y axis
  507. * @param pitch The rotation of the bone on the x axis
  508. * @param roll The rotation of the bone on the z axis
  509. * @param space The space that the axes of rotation are in
  510. * @param mesh The mesh that this bone is attached to. This is only used in world space
  511. */
  512. public setYawPitchRoll(yaw: number, pitch: number, roll: number, space = Space.LOCAL, mesh?: AbstractMesh): void {
  513. if (space === Space.LOCAL) {
  514. var quat = Bone._tmpQuat;
  515. Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, quat);
  516. this.setRotationQuaternion(quat, space, mesh);
  517. return;
  518. }
  519. var rotMatInv = Bone._tmpMats[0];
  520. if (!this._getNegativeRotationToRef(rotMatInv, mesh)) {
  521. return;
  522. }
  523. var rotMat = Bone._tmpMats[1];
  524. Matrix.RotationYawPitchRollToRef(yaw, pitch, roll, rotMat);
  525. rotMatInv.multiplyToRef(rotMat, rotMat);
  526. this._rotateWithMatrix(rotMat, space, mesh);
  527. }
  528. /**
  529. * Add a rotation to the bone on an axis in local or world space
  530. * @param axis The axis to rotate the bone on
  531. * @param amount The amount to rotate the bone
  532. * @param space The space that the axis is in
  533. * @param mesh The mesh that this bone is attached to. This is only used in world space
  534. */
  535. public rotate(axis: Vector3, amount: number, space = Space.LOCAL, mesh?: AbstractMesh): void {
  536. var rmat = Bone._tmpMats[0];
  537. rmat.setTranslationFromFloats(0, 0, 0);
  538. Matrix.RotationAxisToRef(axis, amount, rmat);
  539. this._rotateWithMatrix(rmat, space, mesh);
  540. }
  541. /**
  542. * Set the rotation of the bone to a particular axis angle in local or world space
  543. * @param axis The axis to rotate the bone on
  544. * @param angle The angle that the bone should be rotated to
  545. * @param space The space that the axis is in
  546. * @param mesh The mesh that this bone is attached to. This is only used in world space
  547. */
  548. public setAxisAngle(axis: Vector3, angle: number, space = Space.LOCAL, mesh?: AbstractMesh): void {
  549. if (space === Space.LOCAL) {
  550. var quat = Bone._tmpQuat;
  551. Quaternion.RotationAxisToRef(axis, angle, quat);
  552. this.setRotationQuaternion(quat, space, mesh);
  553. return;
  554. }
  555. var rotMatInv = Bone._tmpMats[0];
  556. if (!this._getNegativeRotationToRef(rotMatInv, mesh)) {
  557. return;
  558. }
  559. var rotMat = Bone._tmpMats[1];
  560. Matrix.RotationAxisToRef(axis, angle, rotMat);
  561. rotMatInv.multiplyToRef(rotMat, rotMat);
  562. this._rotateWithMatrix(rotMat, space, mesh);
  563. }
  564. /**
  565. * Set the euler rotation of the bone in local or world space
  566. * @param rotation The euler rotation that the bone should be set to
  567. * @param space The space that the rotation is in
  568. * @param mesh The mesh that this bone is attached to. This is only used in world space
  569. */
  570. public setRotation(rotation: Vector3, space = Space.LOCAL, mesh?: AbstractMesh): void {
  571. this.setYawPitchRoll(rotation.y, rotation.x, rotation.z, space, mesh);
  572. }
  573. /**
  574. * Set the quaternion rotation of the bone in local or world space
  575. * @param quat The quaternion rotation that the bone should be set to
  576. * @param space The space that the rotation is in
  577. * @param mesh The mesh that this bone is attached to. This is only used in world space
  578. */
  579. public setRotationQuaternion(quat: Quaternion, space = Space.LOCAL, mesh?: AbstractMesh): void {
  580. if (space === Space.LOCAL) {
  581. this._decompose();
  582. this._localRotation.copyFrom(quat);
  583. this._markAsDirtyAndCompose();
  584. return;
  585. }
  586. var rotMatInv = Bone._tmpMats[0];
  587. if (!this._getNegativeRotationToRef(rotMatInv, mesh)) {
  588. return;
  589. }
  590. var rotMat = Bone._tmpMats[1];
  591. Matrix.FromQuaternionToRef(quat, rotMat);
  592. rotMatInv.multiplyToRef(rotMat, rotMat);
  593. this._rotateWithMatrix(rotMat, space, mesh);
  594. }
  595. /**
  596. * Set the rotation matrix of the bone in local or world space
  597. * @param rotMat The rotation matrix that the bone should be set to
  598. * @param space The space that the rotation is in
  599. * @param mesh The mesh that this bone is attached to. This is only used in world space
  600. */
  601. public setRotationMatrix(rotMat: Matrix, space = Space.LOCAL, mesh?: AbstractMesh): void {
  602. if (space === Space.LOCAL) {
  603. var quat = Bone._tmpQuat;
  604. Quaternion.FromRotationMatrixToRef(rotMat, quat);
  605. this.setRotationQuaternion(quat, space, mesh);
  606. return;
  607. }
  608. var rotMatInv = Bone._tmpMats[0];
  609. if (!this._getNegativeRotationToRef(rotMatInv, mesh)) {
  610. return;
  611. }
  612. var rotMat2 = Bone._tmpMats[1];
  613. rotMat2.copyFrom(rotMat);
  614. rotMatInv.multiplyToRef(rotMat, rotMat2);
  615. this._rotateWithMatrix(rotMat2, space, mesh);
  616. }
  617. private _rotateWithMatrix(rmat: Matrix, space = Space.LOCAL, mesh?: AbstractMesh): void {
  618. var lmat = this.getLocalMatrix();
  619. var lx = lmat.m[12];
  620. var ly = lmat.m[13];
  621. var lz = lmat.m[14];
  622. var parent = this.getParent();
  623. var parentScale = Bone._tmpMats[3];
  624. var parentScaleInv = Bone._tmpMats[4];
  625. if (parent && space == Space.WORLD) {
  626. if (mesh) {
  627. parentScale.copyFrom(mesh.getWorldMatrix());
  628. parent.getAbsoluteTransform().multiplyToRef(parentScale, parentScale);
  629. } else {
  630. parentScale.copyFrom(parent.getAbsoluteTransform());
  631. }
  632. parentScaleInv.copyFrom(parentScale);
  633. parentScaleInv.invert();
  634. lmat.multiplyToRef(parentScale, lmat);
  635. lmat.multiplyToRef(rmat, lmat);
  636. lmat.multiplyToRef(parentScaleInv, lmat);
  637. } else {
  638. if (space == Space.WORLD && mesh) {
  639. parentScale.copyFrom(mesh.getWorldMatrix());
  640. parentScaleInv.copyFrom(parentScale);
  641. parentScaleInv.invert();
  642. lmat.multiplyToRef(parentScale, lmat);
  643. lmat.multiplyToRef(rmat, lmat);
  644. lmat.multiplyToRef(parentScaleInv, lmat);
  645. } else {
  646. lmat.multiplyToRef(rmat, lmat);
  647. }
  648. }
  649. lmat.setTranslationFromFloats(lx, ly, lz);
  650. this.computeAbsoluteTransforms();
  651. this._markAsDirtyAndDecompose();
  652. }
  653. private _getNegativeRotationToRef(rotMatInv: Matrix, mesh?: AbstractMesh): boolean {
  654. var scaleMatrix = Bone._tmpMats[2];
  655. rotMatInv.copyFrom(this.getAbsoluteTransform());
  656. if (mesh) {
  657. rotMatInv.multiplyToRef(mesh.getWorldMatrix(), rotMatInv);
  658. Matrix.ScalingToRef(mesh.scaling.x, mesh.scaling.y, mesh.scaling.z, scaleMatrix);
  659. }
  660. rotMatInv.invert();
  661. if (isNaN(rotMatInv.m[0])) {
  662. // Matrix failed to invert.
  663. // This can happen if scale is zero for example.
  664. return false;
  665. }
  666. scaleMatrix.multiplyAtIndex(0, this._scalingDeterminant);
  667. rotMatInv.multiplyToRef(scaleMatrix, rotMatInv);
  668. return true;
  669. }
  670. /**
  671. * Get the position of the bone in local or world space
  672. * @param space The space that the returned position is in
  673. * @param mesh The mesh that this bone is attached to. This is only used in world space
  674. * @returns The position of the bone
  675. */
  676. public getPosition(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null): Vector3 {
  677. var pos = Vector3.Zero();
  678. this.getPositionToRef(space, mesh, pos);
  679. return pos;
  680. }
  681. /**
  682. * Copy the position of the bone to a vector3 in local or world space
  683. * @param space The space that the returned position is in
  684. * @param mesh The mesh that this bone is attached to. This is only used in world space
  685. * @param result The vector3 to copy the position to
  686. */
  687. public getPositionToRef(space = Space.LOCAL, mesh: Nullable<AbstractMesh>, result: Vector3): void {
  688. if (space == Space.LOCAL) {
  689. var lm = this.getLocalMatrix();
  690. result.x = lm.m[12];
  691. result.y = lm.m[13];
  692. result.z = lm.m[14];
  693. } else {
  694. var wm: Nullable<Matrix> = null;
  695. //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
  696. if (mesh) {
  697. wm = mesh.getWorldMatrix();
  698. }
  699. this._skeleton.computeAbsoluteTransforms();
  700. var tmat = Bone._tmpMats[0];
  701. if (mesh && wm) {
  702. tmat.copyFrom(this.getAbsoluteTransform());
  703. tmat.multiplyToRef(wm, tmat);
  704. } else {
  705. tmat = this.getAbsoluteTransform();
  706. }
  707. result.x = tmat.m[12];
  708. result.y = tmat.m[13];
  709. result.z = tmat.m[14];
  710. }
  711. }
  712. /**
  713. * Get the absolute position of the bone (world space)
  714. * @param mesh The mesh that this bone is attached to
  715. * @returns The absolute position of the bone
  716. */
  717. public getAbsolutePosition(mesh: Nullable<AbstractMesh> = null): Vector3 {
  718. var pos = Vector3.Zero();
  719. this.getPositionToRef(Space.WORLD, mesh, pos);
  720. return pos;
  721. }
  722. /**
  723. * Copy the absolute position of the bone (world space) to the result param
  724. * @param mesh The mesh that this bone is attached to
  725. * @param result The vector3 to copy the absolute position to
  726. */
  727. public getAbsolutePositionToRef(mesh: AbstractMesh, result: Vector3) {
  728. this.getPositionToRef(Space.WORLD, mesh, result);
  729. }
  730. /**
  731. * Compute the absolute transforms of this bone and its children
  732. */
  733. public computeAbsoluteTransforms(): void {
  734. this._compose();
  735. if (this._parent) {
  736. this._localMatrix.multiplyToRef(this._parent._absoluteTransform, this._absoluteTransform);
  737. } else {
  738. this._absoluteTransform.copyFrom(this._localMatrix);
  739. var poseMatrix = this._skeleton.getPoseMatrix();
  740. if (poseMatrix) {
  741. this._absoluteTransform.multiplyToRef(poseMatrix, this._absoluteTransform);
  742. }
  743. }
  744. var children = this.children;
  745. var len = children.length;
  746. for (var i = 0; i < len; i++) {
  747. children[i].computeAbsoluteTransforms();
  748. }
  749. }
  750. /**
  751. * Get the world direction from an axis that is in the local space of the bone
  752. * @param localAxis The local direction that is used to compute the world direction
  753. * @param mesh The mesh that this bone is attached to
  754. * @returns The world direction
  755. */
  756. public getDirection(localAxis: Vector3, mesh: Nullable<AbstractMesh> = null): Vector3 {
  757. var result = Vector3.Zero();
  758. this.getDirectionToRef(localAxis, mesh, result);
  759. return result;
  760. }
  761. /**
  762. * Copy the world direction to a vector3 from an axis that is in the local space of the bone
  763. * @param localAxis The local direction that is used to compute the world direction
  764. * @param mesh The mesh that this bone is attached to
  765. * @param result The vector3 that the world direction will be copied to
  766. */
  767. public getDirectionToRef(localAxis: Vector3, mesh: Nullable<AbstractMesh> = null, result: Vector3): void {
  768. var wm: Nullable<Matrix> = null;
  769. //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
  770. if (mesh) {
  771. wm = mesh.getWorldMatrix();
  772. }
  773. this._skeleton.computeAbsoluteTransforms();
  774. var mat = Bone._tmpMats[0];
  775. mat.copyFrom(this.getAbsoluteTransform());
  776. if (mesh && wm) {
  777. mat.multiplyToRef(wm, mat);
  778. }
  779. Vector3.TransformNormalToRef(localAxis, mat, result);
  780. result.normalize();
  781. }
  782. /**
  783. * Get the euler rotation of the bone in local or world space
  784. * @param space The space that the rotation should be in
  785. * @param mesh The mesh that this bone is attached to. This is only used in world space
  786. * @returns The euler rotation
  787. */
  788. public getRotation(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null): Vector3 {
  789. var result = Vector3.Zero();
  790. this.getRotationToRef(space, mesh, result);
  791. return result;
  792. }
  793. /**
  794. * Copy the euler rotation of the bone to a vector3. The rotation can be in either local or world space
  795. * @param space The space that the rotation should be in
  796. * @param mesh The mesh that this bone is attached to. This is only used in world space
  797. * @param result The vector3 that the rotation should be copied to
  798. */
  799. public getRotationToRef(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null, result: Vector3): void {
  800. var quat = Bone._tmpQuat;
  801. this.getRotationQuaternionToRef(space, mesh, quat);
  802. quat.toEulerAnglesToRef(result);
  803. }
  804. /**
  805. * Get the quaternion rotation of the bone in either local or world space
  806. * @param space The space that the rotation should be in
  807. * @param mesh The mesh that this bone is attached to. This is only used in world space
  808. * @returns The quaternion rotation
  809. */
  810. public getRotationQuaternion(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null): Quaternion {
  811. var result = Quaternion.Identity();
  812. this.getRotationQuaternionToRef(space, mesh, result);
  813. return result;
  814. }
  815. /**
  816. * Copy the quaternion rotation of the bone to a quaternion. The rotation can be in either local or world space
  817. * @param space The space that the rotation should be in
  818. * @param mesh The mesh that this bone is attached to. This is only used in world space
  819. * @param result The quaternion that the rotation should be copied to
  820. */
  821. public getRotationQuaternionToRef(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null, result: Quaternion): void {
  822. if (space == Space.LOCAL) {
  823. this._decompose();
  824. result.copyFrom(this._localRotation);
  825. } else {
  826. var mat = Bone._tmpMats[0];
  827. var amat = this.getAbsoluteTransform();
  828. if (mesh) {
  829. amat.multiplyToRef(mesh.getWorldMatrix(), mat);
  830. } else {
  831. mat.copyFrom(amat);
  832. }
  833. mat.multiplyAtIndex(0, this._scalingDeterminant);
  834. mat.multiplyAtIndex(1, this._scalingDeterminant);
  835. mat.multiplyAtIndex(2, this._scalingDeterminant);
  836. mat.decompose(undefined, result, undefined);
  837. }
  838. }
  839. /**
  840. * Get the rotation matrix of the bone in local or world space
  841. * @param space The space that the rotation should be in
  842. * @param mesh The mesh that this bone is attached to. This is only used in world space
  843. * @returns The rotation matrix
  844. */
  845. public getRotationMatrix(space = Space.LOCAL, mesh: AbstractMesh): Matrix {
  846. var result = Matrix.Identity();
  847. this.getRotationMatrixToRef(space, mesh, result);
  848. return result;
  849. }
  850. /**
  851. * Copy the rotation matrix of the bone to a matrix. The rotation can be in either local or world space
  852. * @param space The space that the rotation should be in
  853. * @param mesh The mesh that this bone is attached to. This is only used in world space
  854. * @param result The quaternion that the rotation should be copied to
  855. */
  856. public getRotationMatrixToRef(space = Space.LOCAL, mesh: AbstractMesh, result: Matrix): void {
  857. if (space == Space.LOCAL) {
  858. this.getLocalMatrix().getRotationMatrixToRef(result);
  859. } else {
  860. var mat = Bone._tmpMats[0];
  861. var amat = this.getAbsoluteTransform();
  862. if (mesh) {
  863. amat.multiplyToRef(mesh.getWorldMatrix(), mat);
  864. } else {
  865. mat.copyFrom(amat);
  866. }
  867. mat.multiplyAtIndex(0, this._scalingDeterminant);
  868. mat.multiplyAtIndex(1, this._scalingDeterminant);
  869. mat.multiplyAtIndex(2, this._scalingDeterminant);
  870. mat.getRotationMatrixToRef(result);
  871. }
  872. }
  873. /**
  874. * Get the world position of a point that is in the local space of the bone
  875. * @param position The local position
  876. * @param mesh The mesh that this bone is attached to
  877. * @returns The world position
  878. */
  879. public getAbsolutePositionFromLocal(position: Vector3, mesh: Nullable<AbstractMesh> = null): Vector3 {
  880. var result = Vector3.Zero();
  881. this.getAbsolutePositionFromLocalToRef(position, mesh, result);
  882. return result;
  883. }
  884. /**
  885. * Get the world position of a point that is in the local space of the bone and copy it to the result param
  886. * @param position The local position
  887. * @param mesh The mesh that this bone is attached to
  888. * @param result The vector3 that the world position should be copied to
  889. */
  890. public getAbsolutePositionFromLocalToRef(position: Vector3, mesh: Nullable<AbstractMesh> = null, result: Vector3): void {
  891. var wm: Nullable<Matrix> = null;
  892. //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
  893. if (mesh) {
  894. wm = mesh.getWorldMatrix();
  895. }
  896. this._skeleton.computeAbsoluteTransforms();
  897. var tmat = Bone._tmpMats[0];
  898. if (mesh && wm) {
  899. tmat.copyFrom(this.getAbsoluteTransform());
  900. tmat.multiplyToRef(wm, tmat);
  901. } else {
  902. tmat = this.getAbsoluteTransform();
  903. }
  904. Vector3.TransformCoordinatesToRef(position, tmat, result);
  905. }
  906. /**
  907. * Get the local position of a point that is in world space
  908. * @param position The world position
  909. * @param mesh The mesh that this bone is attached to
  910. * @returns The local position
  911. */
  912. public getLocalPositionFromAbsolute(position: Vector3, mesh: Nullable<AbstractMesh> = null): Vector3 {
  913. var result = Vector3.Zero();
  914. this.getLocalPositionFromAbsoluteToRef(position, mesh, result);
  915. return result;
  916. }
  917. /**
  918. * Get the local position of a point that is in world space and copy it to the result param
  919. * @param position The world position
  920. * @param mesh The mesh that this bone is attached to
  921. * @param result The vector3 that the local position should be copied to
  922. */
  923. public getLocalPositionFromAbsoluteToRef(position: Vector3, mesh: Nullable<AbstractMesh> = null, result: Vector3): void {
  924. var wm: Nullable<Matrix> = null;
  925. //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
  926. if (mesh) {
  927. wm = mesh.getWorldMatrix();
  928. }
  929. this._skeleton.computeAbsoluteTransforms();
  930. var tmat = Bone._tmpMats[0];
  931. tmat.copyFrom(this.getAbsoluteTransform());
  932. if (mesh && wm) {
  933. tmat.multiplyToRef(wm, tmat);
  934. }
  935. tmat.invert();
  936. Vector3.TransformCoordinatesToRef(position, tmat, result);
  937. }
  938. /**
  939. * Set the current local matrix as the restPose for this bone.
  940. */
  941. public setCurrentPoseAsRest(): void {
  942. this.setRestPose(this.getLocalMatrix());
  943. }
  944. }