bone.js 41 KB

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