bone.ts 42 KB

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