transformNode.js 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404
  1. import { __decorate } from "../tslib.es6.js";
  2. import { serialize, serializeAsVector3, serializeAsQuaternion } from "../Misc/decorators.js";
  3. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  4. import { Observable } from "../Misc/observable.js";
  5. import { Quaternion, Matrix, Vector3, TmpVectors } from "../Maths/math.vector.js";
  6. import { Node } from "../node.js";
  7. import { Space } from "../Maths/math.axis.js";
  8. import { GetClass } from "../Misc/typeStore.js";
  9. const convertRHSToLHS = Matrix.Compose(Vector3.One(), Quaternion.FromEulerAngles(0, Math.PI, 0), Vector3.Zero());
  10. /**
  11. * A TransformNode is an object that is not rendered but can be used as a center of transformation. This can decrease memory usage and increase rendering speed compared to using an empty mesh as a parent and is less complicated than using a pivot matrix.
  12. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/transforms/parent_pivot/transform_node
  13. */
  14. export class TransformNode extends Node {
  15. /**
  16. * Gets or sets the billboard mode. Default is 0.
  17. *
  18. * | Value | Type | Description |
  19. * | --- | --- | --- |
  20. * | 0 | BILLBOARDMODE_NONE | |
  21. * | 1 | BILLBOARDMODE_X | |
  22. * | 2 | BILLBOARDMODE_Y | |
  23. * | 4 | BILLBOARDMODE_Z | |
  24. * | 7 | BILLBOARDMODE_ALL | |
  25. *
  26. */
  27. get billboardMode() {
  28. return this._billboardMode;
  29. }
  30. set billboardMode(value) {
  31. if (this._billboardMode === value) {
  32. return;
  33. }
  34. this._billboardMode = value;
  35. this._cache.useBillboardPosition = (this._billboardMode & TransformNode.BILLBOARDMODE_USE_POSITION) !== 0;
  36. this._computeUseBillboardPath();
  37. }
  38. /**
  39. * Gets or sets a boolean indicating that parent rotation should be preserved when using billboards.
  40. * This could be useful for glTF objects where parent rotation helps converting from right handed to left handed
  41. */
  42. get preserveParentRotationForBillboard() {
  43. return this._preserveParentRotationForBillboard;
  44. }
  45. set preserveParentRotationForBillboard(value) {
  46. if (value === this._preserveParentRotationForBillboard) {
  47. return;
  48. }
  49. this._preserveParentRotationForBillboard = value;
  50. this._computeUseBillboardPath();
  51. }
  52. _computeUseBillboardPath() {
  53. this._cache.useBillboardPath = this._billboardMode !== TransformNode.BILLBOARDMODE_NONE && !this.preserveParentRotationForBillboard;
  54. }
  55. /**
  56. * Gets or sets the distance of the object to max, often used by skybox
  57. */
  58. get infiniteDistance() {
  59. return this._infiniteDistance;
  60. }
  61. set infiniteDistance(value) {
  62. if (this._infiniteDistance === value) {
  63. return;
  64. }
  65. this._infiniteDistance = value;
  66. }
  67. constructor(name, scene = null, isPure = true) {
  68. super(name, scene);
  69. this._forward = new Vector3(0, 0, 1);
  70. this._up = new Vector3(0, 1, 0);
  71. this._right = new Vector3(1, 0, 0);
  72. // Properties
  73. this._position = Vector3.Zero();
  74. this._rotation = Vector3.Zero();
  75. this._rotationQuaternion = null;
  76. this._scaling = Vector3.One();
  77. this._transformToBoneReferal = null;
  78. this._isAbsoluteSynced = false;
  79. this._billboardMode = TransformNode.BILLBOARDMODE_NONE;
  80. this._preserveParentRotationForBillboard = false;
  81. /**
  82. * Multiplication factor on scale x/y/z when computing the world matrix. Eg. for a 1x1x1 cube setting this to 2 will make it a 2x2x2 cube
  83. */
  84. this.scalingDeterminant = 1;
  85. this._infiniteDistance = false;
  86. /**
  87. * Gets or sets a boolean indicating that non uniform scaling (when at least one component is different from others) should be ignored.
  88. * By default the system will update normals to compensate
  89. */
  90. this.ignoreNonUniformScaling = false;
  91. /**
  92. * Gets or sets a boolean indicating that even if rotationQuaternion is defined, you can keep updating rotation property and Babylon.js will just mix both
  93. */
  94. this.reIntegrateRotationIntoRotationQuaternion = false;
  95. // Cache
  96. /** @internal */
  97. this._poseMatrix = null;
  98. /** @internal */
  99. this._localMatrix = Matrix.Zero();
  100. this._usePivotMatrix = false;
  101. this._absolutePosition = Vector3.Zero();
  102. this._absoluteScaling = Vector3.Zero();
  103. this._absoluteRotationQuaternion = Quaternion.Identity();
  104. this._pivotMatrix = Matrix.Identity();
  105. /** @internal */
  106. this._postMultiplyPivotMatrix = false;
  107. this._isWorldMatrixFrozen = false;
  108. /** @internal */
  109. this._indexInSceneTransformNodesArray = -1;
  110. /**
  111. * An event triggered after the world matrix is updated
  112. */
  113. this.onAfterWorldMatrixUpdateObservable = new Observable();
  114. this._nonUniformScaling = false;
  115. if (isPure) {
  116. this.getScene().addTransformNode(this);
  117. }
  118. }
  119. /**
  120. * Gets a string identifying the name of the class
  121. * @returns "TransformNode" string
  122. */
  123. getClassName() {
  124. return "TransformNode";
  125. }
  126. /**
  127. * Gets or set the node position (default is (0.0, 0.0, 0.0))
  128. */
  129. get position() {
  130. return this._position;
  131. }
  132. set position(newPosition) {
  133. this._position = newPosition;
  134. this._isDirty = true;
  135. }
  136. /**
  137. * return true if a pivot has been set
  138. * @returns true if a pivot matrix is used
  139. */
  140. isUsingPivotMatrix() {
  141. return this._usePivotMatrix;
  142. }
  143. /**
  144. * @returns true if pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect.
  145. */
  146. isUsingPostMultiplyPivotMatrix() {
  147. return this._postMultiplyPivotMatrix;
  148. }
  149. /**
  150. * Gets or sets the rotation property : a Vector3 defining the rotation value in radians around each local axis X, Y, Z (default is (0.0, 0.0, 0.0)).
  151. * If rotation quaternion is set, this Vector3 will be ignored and copy from the quaternion
  152. */
  153. get rotation() {
  154. return this._rotation;
  155. }
  156. set rotation(newRotation) {
  157. this._rotation = newRotation;
  158. this._rotationQuaternion = null;
  159. this._isDirty = true;
  160. }
  161. /**
  162. * Gets or sets the scaling property : a Vector3 defining the node scaling along each local axis X, Y, Z (default is (1.0, 1.0, 1.0)).
  163. */
  164. get scaling() {
  165. return this._scaling;
  166. }
  167. set scaling(newScaling) {
  168. this._scaling = newScaling;
  169. this._isDirty = true;
  170. }
  171. /**
  172. * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (undefined by default, but can be null).
  173. * If set, only the rotationQuaternion is then used to compute the node rotation (ie. node.rotation will be ignored)
  174. */
  175. get rotationQuaternion() {
  176. return this._rotationQuaternion;
  177. }
  178. set rotationQuaternion(quaternion) {
  179. this._rotationQuaternion = quaternion;
  180. //reset the rotation vector.
  181. if (quaternion) {
  182. this._rotation.setAll(0.0);
  183. }
  184. this._isDirty = true;
  185. }
  186. /**
  187. * The forward direction of that transform in world space.
  188. */
  189. get forward() {
  190. Vector3.TransformNormalFromFloatsToRef(0, 0, this.getScene().useRightHandedSystem ? -1.0 : 1.0, this.getWorldMatrix(), this._forward);
  191. return this._forward.normalize();
  192. }
  193. /**
  194. * The up direction of that transform in world space.
  195. */
  196. get up() {
  197. Vector3.TransformNormalFromFloatsToRef(0, 1, 0, this.getWorldMatrix(), this._up);
  198. return this._up.normalize();
  199. }
  200. /**
  201. * The right direction of that transform in world space.
  202. */
  203. get right() {
  204. Vector3.TransformNormalFromFloatsToRef(this.getScene().useRightHandedSystem ? -1.0 : 1.0, 0, 0, this.getWorldMatrix(), this._right);
  205. return this._right.normalize();
  206. }
  207. /**
  208. * Copies the parameter passed Matrix into the mesh Pose matrix.
  209. * @param matrix the matrix to copy the pose from
  210. * @returns this TransformNode.
  211. */
  212. updatePoseMatrix(matrix) {
  213. if (!this._poseMatrix) {
  214. this._poseMatrix = matrix.clone();
  215. return this;
  216. }
  217. this._poseMatrix.copyFrom(matrix);
  218. return this;
  219. }
  220. /**
  221. * Returns the mesh Pose matrix.
  222. * @returns the pose matrix
  223. */
  224. getPoseMatrix() {
  225. if (!this._poseMatrix) {
  226. this._poseMatrix = Matrix.Identity();
  227. }
  228. return this._poseMatrix;
  229. }
  230. /** @internal */
  231. _isSynchronized() {
  232. const cache = this._cache;
  233. if (this._billboardMode !== cache.billboardMode || this._billboardMode !== TransformNode.BILLBOARDMODE_NONE) {
  234. return false;
  235. }
  236. if (cache.pivotMatrixUpdated) {
  237. return false;
  238. }
  239. if (this._infiniteDistance) {
  240. return false;
  241. }
  242. if (this._position._isDirty) {
  243. return false;
  244. }
  245. if (this._scaling._isDirty) {
  246. return false;
  247. }
  248. if ((this._rotationQuaternion && this._rotationQuaternion._isDirty) || this._rotation._isDirty) {
  249. return false;
  250. }
  251. return true;
  252. }
  253. /** @internal */
  254. _initCache() {
  255. super._initCache();
  256. const cache = this._cache;
  257. cache.localMatrixUpdated = false;
  258. cache.billboardMode = -1;
  259. cache.infiniteDistance = false;
  260. cache.useBillboardPosition = false;
  261. cache.useBillboardPath = false;
  262. }
  263. /**
  264. * Returns the current mesh absolute position.
  265. * Returns a Vector3.
  266. */
  267. get absolutePosition() {
  268. return this.getAbsolutePosition();
  269. }
  270. /**
  271. * Returns the current mesh absolute scaling.
  272. * Returns a Vector3.
  273. */
  274. get absoluteScaling() {
  275. this._syncAbsoluteScalingAndRotation();
  276. return this._absoluteScaling;
  277. }
  278. /**
  279. * Returns the current mesh absolute rotation.
  280. * Returns a Quaternion.
  281. */
  282. get absoluteRotationQuaternion() {
  283. this._syncAbsoluteScalingAndRotation();
  284. return this._absoluteRotationQuaternion;
  285. }
  286. /**
  287. * Sets a new matrix to apply before all other transformation
  288. * @param matrix defines the transform matrix
  289. * @returns the current TransformNode
  290. */
  291. setPreTransformMatrix(matrix) {
  292. return this.setPivotMatrix(matrix, false);
  293. }
  294. /**
  295. * Sets a new pivot matrix to the current node
  296. * @param matrix defines the new pivot matrix to use
  297. * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
  298. * @returns the current TransformNode
  299. */
  300. setPivotMatrix(matrix, postMultiplyPivotMatrix = true) {
  301. this._pivotMatrix.copyFrom(matrix);
  302. this._usePivotMatrix = !this._pivotMatrix.isIdentity();
  303. this._cache.pivotMatrixUpdated = true;
  304. this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
  305. if (this._postMultiplyPivotMatrix) {
  306. if (!this._pivotMatrixInverse) {
  307. this._pivotMatrixInverse = Matrix.Invert(this._pivotMatrix);
  308. }
  309. else {
  310. this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
  311. }
  312. }
  313. return this;
  314. }
  315. /**
  316. * Returns the mesh pivot matrix.
  317. * Default : Identity.
  318. * @returns the matrix
  319. */
  320. getPivotMatrix() {
  321. return this._pivotMatrix;
  322. }
  323. /**
  324. * Instantiate (when possible) or clone that node with its hierarchy
  325. * @param newParent defines the new parent to use for the instance (or clone)
  326. * @param options defines options to configure how copy is done
  327. * @param options.doNotInstantiate defines if the model must be instantiated or just cloned
  328. * @param onNewNodeCreated defines an option callback to call when a clone or an instance is created
  329. * @returns an instance (or a clone) of the current node with its hierarchy
  330. */
  331. instantiateHierarchy(newParent = null, options, onNewNodeCreated) {
  332. const clone = this.clone("Clone of " + (this.name || this.id), newParent || this.parent, true);
  333. if (clone) {
  334. if (onNewNodeCreated) {
  335. onNewNodeCreated(this, clone);
  336. }
  337. }
  338. for (const child of this.getChildTransformNodes(true)) {
  339. child.instantiateHierarchy(clone, options, onNewNodeCreated);
  340. }
  341. return clone;
  342. }
  343. /**
  344. * Prevents the World matrix to be computed any longer
  345. * @param newWorldMatrix defines an optional matrix to use as world matrix
  346. * @param decompose defines whether to decompose the given newWorldMatrix or directly assign
  347. * @returns the TransformNode.
  348. */
  349. freezeWorldMatrix(newWorldMatrix = null, decompose = false) {
  350. if (newWorldMatrix) {
  351. if (decompose) {
  352. this._rotation.setAll(0);
  353. this._rotationQuaternion = this._rotationQuaternion || Quaternion.Identity();
  354. newWorldMatrix.decompose(this._scaling, this._rotationQuaternion, this._position);
  355. this.computeWorldMatrix(true);
  356. }
  357. else {
  358. this._worldMatrix = newWorldMatrix;
  359. this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
  360. this._afterComputeWorldMatrix();
  361. }
  362. }
  363. else {
  364. this._isWorldMatrixFrozen = false; // no guarantee world is not already frozen, switch off temporarily
  365. this.computeWorldMatrix(true);
  366. }
  367. this._isDirty = false;
  368. this._isWorldMatrixFrozen = true;
  369. return this;
  370. }
  371. /**
  372. * Allows back the World matrix computation.
  373. * @returns the TransformNode.
  374. */
  375. unfreezeWorldMatrix() {
  376. this._isWorldMatrixFrozen = false;
  377. this.computeWorldMatrix(true);
  378. return this;
  379. }
  380. /**
  381. * True if the World matrix has been frozen.
  382. */
  383. get isWorldMatrixFrozen() {
  384. return this._isWorldMatrixFrozen;
  385. }
  386. /**
  387. * Returns the mesh absolute position in the World.
  388. * @returns a Vector3.
  389. */
  390. getAbsolutePosition() {
  391. this.computeWorldMatrix();
  392. return this._absolutePosition;
  393. }
  394. /**
  395. * Sets the mesh absolute position in the World from a Vector3 or an Array(3).
  396. * @param absolutePosition the absolute position to set
  397. * @returns the TransformNode.
  398. */
  399. setAbsolutePosition(absolutePosition) {
  400. if (!absolutePosition) {
  401. return this;
  402. }
  403. let absolutePositionX;
  404. let absolutePositionY;
  405. let absolutePositionZ;
  406. if (absolutePosition.x === undefined) {
  407. if (arguments.length < 3) {
  408. return this;
  409. }
  410. absolutePositionX = arguments[0];
  411. absolutePositionY = arguments[1];
  412. absolutePositionZ = arguments[2];
  413. }
  414. else {
  415. absolutePositionX = absolutePosition.x;
  416. absolutePositionY = absolutePosition.y;
  417. absolutePositionZ = absolutePosition.z;
  418. }
  419. if (this.parent) {
  420. const invertParentWorldMatrix = TmpVectors.Matrix[0];
  421. this.parent.getWorldMatrix().invertToRef(invertParentWorldMatrix);
  422. Vector3.TransformCoordinatesFromFloatsToRef(absolutePositionX, absolutePositionY, absolutePositionZ, invertParentWorldMatrix, this.position);
  423. }
  424. else {
  425. this.position.x = absolutePositionX;
  426. this.position.y = absolutePositionY;
  427. this.position.z = absolutePositionZ;
  428. }
  429. this._absolutePosition.copyFrom(absolutePosition);
  430. return this;
  431. }
  432. /**
  433. * Sets the mesh position in its local space.
  434. * @param vector3 the position to set in localspace
  435. * @returns the TransformNode.
  436. */
  437. setPositionWithLocalVector(vector3) {
  438. this.computeWorldMatrix();
  439. this.position = Vector3.TransformNormal(vector3, this._localMatrix);
  440. return this;
  441. }
  442. /**
  443. * Returns the mesh position in the local space from the current World matrix values.
  444. * @returns a new Vector3.
  445. */
  446. getPositionExpressedInLocalSpace() {
  447. this.computeWorldMatrix();
  448. const invLocalWorldMatrix = TmpVectors.Matrix[0];
  449. this._localMatrix.invertToRef(invLocalWorldMatrix);
  450. return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
  451. }
  452. /**
  453. * Translates the mesh along the passed Vector3 in its local space.
  454. * @param vector3 the distance to translate in localspace
  455. * @returns the TransformNode.
  456. */
  457. locallyTranslate(vector3) {
  458. this.computeWorldMatrix(true);
  459. this.position = Vector3.TransformCoordinates(vector3, this._localMatrix);
  460. return this;
  461. }
  462. /**
  463. * Orients a mesh towards a target point. Mesh must be drawn facing user.
  464. * @param targetPoint the position (must be in same space as current mesh) to look at
  465. * @param yawCor optional yaw (y-axis) correction in radians
  466. * @param pitchCor optional pitch (x-axis) correction in radians
  467. * @param rollCor optional roll (z-axis) correction in radians
  468. * @param space the chosen space of the target
  469. * @returns the TransformNode.
  470. */
  471. lookAt(targetPoint, yawCor = 0, pitchCor = 0, rollCor = 0, space = Space.LOCAL) {
  472. const dv = TransformNode._LookAtVectorCache;
  473. const pos = space === Space.LOCAL ? this.position : this.getAbsolutePosition();
  474. targetPoint.subtractToRef(pos, dv);
  475. this.setDirection(dv, yawCor, pitchCor, rollCor);
  476. // Correct for parent's rotation offset
  477. if (space === Space.WORLD && this.parent) {
  478. if (this.rotationQuaternion) {
  479. // Get local rotation matrix of the looking object
  480. const rotationMatrix = TmpVectors.Matrix[0];
  481. this.rotationQuaternion.toRotationMatrix(rotationMatrix);
  482. // Offset rotation by parent's inverted rotation matrix to correct in world space
  483. const parentRotationMatrix = TmpVectors.Matrix[1];
  484. this.parent.getWorldMatrix().getRotationMatrixToRef(parentRotationMatrix);
  485. parentRotationMatrix.invert();
  486. rotationMatrix.multiplyToRef(parentRotationMatrix, rotationMatrix);
  487. this.rotationQuaternion.fromRotationMatrix(rotationMatrix);
  488. }
  489. else {
  490. // Get local rotation matrix of the looking object
  491. const quaternionRotation = TmpVectors.Quaternion[0];
  492. Quaternion.FromEulerVectorToRef(this.rotation, quaternionRotation);
  493. const rotationMatrix = TmpVectors.Matrix[0];
  494. quaternionRotation.toRotationMatrix(rotationMatrix);
  495. // Offset rotation by parent's inverted rotation matrix to correct in world space
  496. const parentRotationMatrix = TmpVectors.Matrix[1];
  497. this.parent.getWorldMatrix().getRotationMatrixToRef(parentRotationMatrix);
  498. parentRotationMatrix.invert();
  499. rotationMatrix.multiplyToRef(parentRotationMatrix, rotationMatrix);
  500. quaternionRotation.fromRotationMatrix(rotationMatrix);
  501. quaternionRotation.toEulerAnglesToRef(this.rotation);
  502. }
  503. }
  504. return this;
  505. }
  506. /**
  507. * Returns a new Vector3 that is the localAxis, expressed in the mesh local space, rotated like the mesh.
  508. * This Vector3 is expressed in the World space.
  509. * @param localAxis axis to rotate
  510. * @returns a new Vector3 that is the localAxis, expressed in the mesh local space, rotated like the mesh.
  511. */
  512. getDirection(localAxis) {
  513. const result = Vector3.Zero();
  514. this.getDirectionToRef(localAxis, result);
  515. return result;
  516. }
  517. /**
  518. * Sets the Vector3 "result" as the rotated Vector3 "localAxis" in the same rotation than the mesh.
  519. * localAxis is expressed in the mesh local space.
  520. * result is computed in the World space from the mesh World matrix.
  521. * @param localAxis axis to rotate
  522. * @param result the resulting transformnode
  523. * @returns this TransformNode.
  524. */
  525. getDirectionToRef(localAxis, result) {
  526. Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
  527. return this;
  528. }
  529. /**
  530. * Sets this transform node rotation to the given local axis.
  531. * @param localAxis the axis in local space
  532. * @param yawCor optional yaw (y-axis) correction in radians
  533. * @param pitchCor optional pitch (x-axis) correction in radians
  534. * @param rollCor optional roll (z-axis) correction in radians
  535. * @returns this TransformNode
  536. */
  537. setDirection(localAxis, yawCor = 0, pitchCor = 0, rollCor = 0) {
  538. const yaw = -Math.atan2(localAxis.z, localAxis.x) + Math.PI / 2;
  539. const len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
  540. const pitch = -Math.atan2(localAxis.y, len);
  541. if (this.rotationQuaternion) {
  542. Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
  543. }
  544. else {
  545. this.rotation.x = pitch + pitchCor;
  546. this.rotation.y = yaw + yawCor;
  547. this.rotation.z = rollCor;
  548. }
  549. return this;
  550. }
  551. /**
  552. * Sets a new pivot point to the current node
  553. * @param point defines the new pivot point to use
  554. * @param space defines if the point is in world or local space (local by default)
  555. * @returns the current TransformNode
  556. */
  557. setPivotPoint(point, space = Space.LOCAL) {
  558. if (this.getScene().getRenderId() == 0) {
  559. this.computeWorldMatrix(true);
  560. }
  561. const wm = this.getWorldMatrix();
  562. if (space == Space.WORLD) {
  563. const tmat = TmpVectors.Matrix[0];
  564. wm.invertToRef(tmat);
  565. point = Vector3.TransformCoordinates(point, tmat);
  566. }
  567. return this.setPivotMatrix(Matrix.Translation(-point.x, -point.y, -point.z), true);
  568. }
  569. /**
  570. * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.
  571. * @returns the pivot point
  572. */
  573. getPivotPoint() {
  574. const point = Vector3.Zero();
  575. this.getPivotPointToRef(point);
  576. return point;
  577. }
  578. /**
  579. * Sets the passed Vector3 "result" with the coordinates of the mesh pivot point in the local space.
  580. * @param result the vector3 to store the result
  581. * @returns this TransformNode.
  582. */
  583. getPivotPointToRef(result) {
  584. result.x = -this._pivotMatrix.m[12];
  585. result.y = -this._pivotMatrix.m[13];
  586. result.z = -this._pivotMatrix.m[14];
  587. return this;
  588. }
  589. /**
  590. * Returns a new Vector3 set with the mesh pivot point World coordinates.
  591. * @returns a new Vector3 set with the mesh pivot point World coordinates.
  592. */
  593. getAbsolutePivotPoint() {
  594. const point = Vector3.Zero();
  595. this.getAbsolutePivotPointToRef(point);
  596. return point;
  597. }
  598. /**
  599. * Sets the Vector3 "result" coordinates with the mesh pivot point World coordinates.
  600. * @param result vector3 to store the result
  601. * @returns this TransformNode.
  602. */
  603. getAbsolutePivotPointToRef(result) {
  604. this.getPivotPointToRef(result);
  605. Vector3.TransformCoordinatesToRef(result, this.getWorldMatrix(), result);
  606. return this;
  607. }
  608. /**
  609. * Flag the transform node as dirty (Forcing it to update everything)
  610. * @param property if set to "rotation" the objects rotationQuaternion will be set to null
  611. * @returns this node
  612. */
  613. markAsDirty(property) {
  614. if (this._isDirty) {
  615. return this;
  616. }
  617. // We need to explicitly update the children
  618. // as the scene.evaluateActiveMeshes will not poll the transform nodes
  619. if (this._children) {
  620. for (const child of this._children) {
  621. child.markAsDirty(property);
  622. }
  623. }
  624. return super.markAsDirty(property);
  625. }
  626. /**
  627. * Defines the passed node as the parent of the current node.
  628. * The node will remain exactly where it is and its position / rotation will be updated accordingly.
  629. * Note that if the mesh has a pivot matrix / point defined it will be applied after the parent was updated.
  630. * In that case the node will not remain in the same space as it is, as the pivot will be applied.
  631. * To avoid this, you can set updatePivot to true and the pivot will be updated to identity
  632. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/transforms/parent_pivot/parent
  633. * @param node the node ot set as the parent
  634. * @param preserveScalingSign if true, keep scaling sign of child. Otherwise, scaling sign might change.
  635. * @param updatePivot if true, update the pivot matrix to keep the node in the same space as before
  636. * @returns this TransformNode.
  637. */
  638. setParent(node, preserveScalingSign = false, updatePivot = false) {
  639. if (!node && !this.parent) {
  640. return this;
  641. }
  642. const quatRotation = TmpVectors.Quaternion[0];
  643. const position = TmpVectors.Vector3[0];
  644. const scale = TmpVectors.Vector3[1];
  645. const invParentMatrix = TmpVectors.Matrix[1];
  646. Matrix.IdentityToRef(invParentMatrix);
  647. const composedMatrix = TmpVectors.Matrix[0];
  648. this.computeWorldMatrix(true);
  649. let currentRotation = this.rotationQuaternion;
  650. if (!currentRotation) {
  651. currentRotation = TransformNode._TmpRotation;
  652. Quaternion.RotationYawPitchRollToRef(this._rotation.y, this._rotation.x, this._rotation.z, currentRotation);
  653. }
  654. // current global transformation without pivot
  655. Matrix.ComposeToRef(this.scaling, currentRotation, this.position, composedMatrix);
  656. if (this.parent) {
  657. composedMatrix.multiplyToRef(this.parent.computeWorldMatrix(true), composedMatrix);
  658. }
  659. // is a node was set, calculate the difference between this and the node
  660. if (node) {
  661. node.computeWorldMatrix(true).invertToRef(invParentMatrix);
  662. composedMatrix.multiplyToRef(invParentMatrix, composedMatrix);
  663. }
  664. composedMatrix.decompose(scale, quatRotation, position, preserveScalingSign ? this : undefined);
  665. if (this.rotationQuaternion) {
  666. this.rotationQuaternion.copyFrom(quatRotation);
  667. }
  668. else {
  669. quatRotation.toEulerAnglesToRef(this.rotation);
  670. }
  671. this.scaling.copyFrom(scale);
  672. this.position.copyFrom(position);
  673. this.parent = node;
  674. if (updatePivot) {
  675. this.setPivotMatrix(Matrix.Identity());
  676. }
  677. return this;
  678. }
  679. /**
  680. * True if the scaling property of this object is non uniform eg. (1,2,1)
  681. */
  682. get nonUniformScaling() {
  683. return this._nonUniformScaling;
  684. }
  685. /**
  686. * @internal
  687. */
  688. _updateNonUniformScalingState(value) {
  689. if (this._nonUniformScaling === value) {
  690. return false;
  691. }
  692. this._nonUniformScaling = value;
  693. return true;
  694. }
  695. /**
  696. * Attach the current TransformNode to another TransformNode associated with a bone
  697. * @param bone Bone affecting the TransformNode
  698. * @param affectedTransformNode TransformNode associated with the bone
  699. * @returns this object
  700. */
  701. attachToBone(bone, affectedTransformNode) {
  702. this._currentParentWhenAttachingToBone = this.parent;
  703. this._transformToBoneReferal = affectedTransformNode;
  704. this.parent = bone;
  705. bone.getSkeleton().prepare(true); // make sure bone.getFinalMatrix() is up to date
  706. if (bone.getFinalMatrix().determinant() < 0) {
  707. this.scalingDeterminant *= -1;
  708. }
  709. return this;
  710. }
  711. /**
  712. * Detach the transform node if its associated with a bone
  713. * @param resetToPreviousParent Indicates if the parent that was in effect when attachToBone was called should be set back or if we should set parent to null instead (defaults to the latter)
  714. * @returns this object
  715. */
  716. detachFromBone(resetToPreviousParent = false) {
  717. if (!this.parent) {
  718. if (resetToPreviousParent) {
  719. this.parent = this._currentParentWhenAttachingToBone;
  720. }
  721. return this;
  722. }
  723. if (this.parent.getWorldMatrix().determinant() < 0) {
  724. this.scalingDeterminant *= -1;
  725. }
  726. this._transformToBoneReferal = null;
  727. if (resetToPreviousParent) {
  728. this.parent = this._currentParentWhenAttachingToBone;
  729. }
  730. else {
  731. this.parent = null;
  732. }
  733. return this;
  734. }
  735. /**
  736. * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in the given space.
  737. * space (default LOCAL) can be either Space.LOCAL, either Space.WORLD.
  738. * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.
  739. * The passed axis is also normalized.
  740. * @param axis the axis to rotate around
  741. * @param amount the amount to rotate in radians
  742. * @param space Space to rotate in (Default: local)
  743. * @returns the TransformNode.
  744. */
  745. rotate(axis, amount, space) {
  746. axis.normalize();
  747. if (!this.rotationQuaternion) {
  748. this.rotationQuaternion = this.rotation.toQuaternion();
  749. this.rotation.setAll(0);
  750. }
  751. let rotationQuaternion;
  752. if (!space || space === Space.LOCAL) {
  753. rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, TransformNode._RotationAxisCache);
  754. this.rotationQuaternion.multiplyToRef(rotationQuaternion, this.rotationQuaternion);
  755. }
  756. else {
  757. if (this.parent) {
  758. const parentWorldMatrix = this.parent.getWorldMatrix();
  759. const invertParentWorldMatrix = TmpVectors.Matrix[0];
  760. parentWorldMatrix.invertToRef(invertParentWorldMatrix);
  761. axis = Vector3.TransformNormal(axis, invertParentWorldMatrix);
  762. if (parentWorldMatrix.determinant() < 0) {
  763. amount *= -1;
  764. }
  765. }
  766. rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, TransformNode._RotationAxisCache);
  767. rotationQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
  768. }
  769. return this;
  770. }
  771. /**
  772. * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in world space.
  773. * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.
  774. * The passed axis is also normalized. .
  775. * Method is based on http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/index.htm
  776. * @param point the point to rotate around
  777. * @param axis the axis to rotate around
  778. * @param amount the amount to rotate in radians
  779. * @returns the TransformNode
  780. */
  781. rotateAround(point, axis, amount) {
  782. axis.normalize();
  783. if (!this.rotationQuaternion) {
  784. this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
  785. this.rotation.setAll(0);
  786. }
  787. const tmpVector = TmpVectors.Vector3[0];
  788. const finalScale = TmpVectors.Vector3[1];
  789. const finalTranslation = TmpVectors.Vector3[2];
  790. const finalRotation = TmpVectors.Quaternion[0];
  791. const translationMatrix = TmpVectors.Matrix[0]; // T
  792. const translationMatrixInv = TmpVectors.Matrix[1]; // T'
  793. const rotationMatrix = TmpVectors.Matrix[2]; // R
  794. const finalMatrix = TmpVectors.Matrix[3]; // T' x R x T
  795. point.subtractToRef(this.position, tmpVector);
  796. Matrix.TranslationToRef(tmpVector.x, tmpVector.y, tmpVector.z, translationMatrix); // T
  797. Matrix.TranslationToRef(-tmpVector.x, -tmpVector.y, -tmpVector.z, translationMatrixInv); // T'
  798. Matrix.RotationAxisToRef(axis, amount, rotationMatrix); // R
  799. translationMatrixInv.multiplyToRef(rotationMatrix, finalMatrix); // T' x R
  800. finalMatrix.multiplyToRef(translationMatrix, finalMatrix); // T' x R x T
  801. finalMatrix.decompose(finalScale, finalRotation, finalTranslation);
  802. this.position.addInPlace(finalTranslation);
  803. finalRotation.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
  804. return this;
  805. }
  806. /**
  807. * Translates the mesh along the axis vector for the passed distance in the given space.
  808. * space (default LOCAL) can be either Space.LOCAL, either Space.WORLD.
  809. * @param axis the axis to translate in
  810. * @param distance the distance to translate
  811. * @param space Space to rotate in (Default: local)
  812. * @returns the TransformNode.
  813. */
  814. translate(axis, distance, space) {
  815. const displacementVector = axis.scale(distance);
  816. if (!space || space === Space.LOCAL) {
  817. const tempV3 = this.getPositionExpressedInLocalSpace().add(displacementVector);
  818. this.setPositionWithLocalVector(tempV3);
  819. }
  820. else {
  821. this.setAbsolutePosition(this.getAbsolutePosition().add(displacementVector));
  822. }
  823. return this;
  824. }
  825. /**
  826. * Adds a rotation step to the mesh current rotation.
  827. * x, y, z are Euler angles expressed in radians.
  828. * This methods updates the current mesh rotation, either mesh.rotation, either mesh.rotationQuaternion if it's set.
  829. * This means this rotation is made in the mesh local space only.
  830. * It's useful to set a custom rotation order different from the BJS standard one YXZ.
  831. * Example : this rotates the mesh first around its local X axis, then around its local Z axis, finally around its local Y axis.
  832. * ```javascript
  833. * mesh.addRotation(x1, 0, 0).addRotation(0, 0, z2).addRotation(0, 0, y3);
  834. * ```
  835. * Note that `addRotation()` accumulates the passed rotation values to the current ones and computes the .rotation or .rotationQuaternion updated values.
  836. * Under the hood, only quaternions are used. So it's a little faster is you use .rotationQuaternion because it doesn't need to translate them back to Euler angles.
  837. * @param x Rotation to add
  838. * @param y Rotation to add
  839. * @param z Rotation to add
  840. * @returns the TransformNode.
  841. */
  842. addRotation(x, y, z) {
  843. let rotationQuaternion;
  844. if (this.rotationQuaternion) {
  845. rotationQuaternion = this.rotationQuaternion;
  846. }
  847. else {
  848. rotationQuaternion = TmpVectors.Quaternion[1];
  849. Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, rotationQuaternion);
  850. }
  851. const accumulation = TmpVectors.Quaternion[0];
  852. Quaternion.RotationYawPitchRollToRef(y, x, z, accumulation);
  853. rotationQuaternion.multiplyInPlace(accumulation);
  854. if (!this.rotationQuaternion) {
  855. rotationQuaternion.toEulerAnglesToRef(this.rotation);
  856. }
  857. return this;
  858. }
  859. /**
  860. * @internal
  861. */
  862. _getEffectiveParent() {
  863. return this.parent;
  864. }
  865. /**
  866. * Returns whether the transform node world matrix computation needs the camera information to be computed.
  867. * This is the case when the node is a billboard or has an infinite distance for instance.
  868. * @returns true if the world matrix computation needs the camera information to be computed
  869. */
  870. isWorldMatrixCameraDependent() {
  871. return (this._infiniteDistance && !this.parent) || (this._billboardMode !== TransformNode.BILLBOARDMODE_NONE && !this.preserveParentRotationForBillboard);
  872. }
  873. /**
  874. * Computes the world matrix of the node
  875. * @param force defines if the cache version should be invalidated forcing the world matrix to be created from scratch
  876. * @param camera defines the camera used if different from the scene active camera (This is used with modes like Billboard or infinite distance)
  877. * @returns the world matrix
  878. */
  879. computeWorldMatrix(force = false, camera = null) {
  880. if (this._isWorldMatrixFrozen && !this._isDirty) {
  881. return this._worldMatrix;
  882. }
  883. const currentRenderId = this.getScene().getRenderId();
  884. if (!this._isDirty && !force && (this._currentRenderId === currentRenderId || this.isSynchronized())) {
  885. this._currentRenderId = currentRenderId;
  886. return this._worldMatrix;
  887. }
  888. camera = camera || this.getScene().activeCamera;
  889. this._updateCache();
  890. const cache = this._cache;
  891. cache.pivotMatrixUpdated = false;
  892. cache.billboardMode = this.billboardMode;
  893. cache.infiniteDistance = this.infiniteDistance;
  894. cache.parent = this._parentNode;
  895. this._currentRenderId = currentRenderId;
  896. this._childUpdateId += 1;
  897. this._isDirty = false;
  898. this._position._isDirty = false;
  899. this._rotation._isDirty = false;
  900. this._scaling._isDirty = false;
  901. const parent = this._getEffectiveParent();
  902. // Scaling
  903. const scaling = TransformNode._TmpScaling;
  904. let translation = this._position;
  905. // Translation
  906. if (this._infiniteDistance) {
  907. if (!this.parent && camera) {
  908. const cameraWorldMatrix = camera.getWorldMatrix();
  909. const cameraGlobalPosition = new Vector3(cameraWorldMatrix.m[12], cameraWorldMatrix.m[13], cameraWorldMatrix.m[14]);
  910. translation = TransformNode._TmpTranslation;
  911. translation.copyFromFloats(this._position.x + cameraGlobalPosition.x, this._position.y + cameraGlobalPosition.y, this._position.z + cameraGlobalPosition.z);
  912. }
  913. }
  914. // Scaling
  915. scaling.copyFromFloats(this._scaling.x * this.scalingDeterminant, this._scaling.y * this.scalingDeterminant, this._scaling.z * this.scalingDeterminant);
  916. // Rotation
  917. let rotation;
  918. if (this._rotationQuaternion) {
  919. this._rotationQuaternion._isDirty = false;
  920. rotation = this._rotationQuaternion;
  921. if (this.reIntegrateRotationIntoRotationQuaternion) {
  922. const len = this.rotation.lengthSquared();
  923. if (len) {
  924. this._rotationQuaternion.multiplyInPlace(Quaternion.RotationYawPitchRoll(this._rotation.y, this._rotation.x, this._rotation.z));
  925. this._rotation.copyFromFloats(0, 0, 0);
  926. }
  927. }
  928. }
  929. else {
  930. rotation = TransformNode._TmpRotation;
  931. Quaternion.RotationYawPitchRollToRef(this._rotation.y, this._rotation.x, this._rotation.z, rotation);
  932. }
  933. // Compose
  934. if (this._usePivotMatrix) {
  935. const scaleMatrix = TmpVectors.Matrix[1];
  936. Matrix.ScalingToRef(scaling.x, scaling.y, scaling.z, scaleMatrix);
  937. // Rotation
  938. const rotationMatrix = TmpVectors.Matrix[0];
  939. rotation.toRotationMatrix(rotationMatrix);
  940. // Composing transformations
  941. this._pivotMatrix.multiplyToRef(scaleMatrix, TmpVectors.Matrix[4]);
  942. TmpVectors.Matrix[4].multiplyToRef(rotationMatrix, this._localMatrix);
  943. // Post multiply inverse of pivotMatrix
  944. if (this._postMultiplyPivotMatrix) {
  945. this._localMatrix.multiplyToRef(this._pivotMatrixInverse, this._localMatrix);
  946. }
  947. this._localMatrix.addTranslationFromFloats(translation.x, translation.y, translation.z);
  948. }
  949. else {
  950. Matrix.ComposeToRef(scaling, rotation, translation, this._localMatrix);
  951. }
  952. // Parent
  953. if (parent && parent.getWorldMatrix) {
  954. if (force) {
  955. parent.computeWorldMatrix(force);
  956. }
  957. if (cache.useBillboardPath) {
  958. if (this._transformToBoneReferal) {
  959. const bone = this.parent;
  960. bone.getSkeleton().prepare();
  961. bone.getFinalMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), TmpVectors.Matrix[7]);
  962. }
  963. else {
  964. TmpVectors.Matrix[7].copyFrom(parent.getWorldMatrix());
  965. }
  966. // Extract scaling and translation from parent
  967. const translation = TmpVectors.Vector3[5];
  968. const scale = TmpVectors.Vector3[6];
  969. const orientation = TmpVectors.Quaternion[0];
  970. TmpVectors.Matrix[7].decompose(scale, orientation, translation);
  971. Matrix.ScalingToRef(scale.x, scale.y, scale.z, TmpVectors.Matrix[7]);
  972. TmpVectors.Matrix[7].setTranslation(translation);
  973. if (TransformNode.BillboardUseParentOrientation) {
  974. // set localMatrix translation to be transformed against parent's orientation.
  975. this._position.applyRotationQuaternionToRef(orientation, translation);
  976. this._localMatrix.setTranslation(translation);
  977. }
  978. this._localMatrix.multiplyToRef(TmpVectors.Matrix[7], this._worldMatrix);
  979. }
  980. else {
  981. if (this._transformToBoneReferal) {
  982. const bone = this.parent;
  983. bone.getSkeleton().prepare();
  984. this._localMatrix.multiplyToRef(bone.getFinalMatrix(), TmpVectors.Matrix[6]);
  985. TmpVectors.Matrix[6].multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), this._worldMatrix);
  986. }
  987. else {
  988. this._localMatrix.multiplyToRef(parent.getWorldMatrix(), this._worldMatrix);
  989. }
  990. }
  991. this._markSyncedWithParent();
  992. }
  993. else {
  994. this._worldMatrix.copyFrom(this._localMatrix);
  995. }
  996. // Billboarding based on camera orientation (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
  997. if (cache.useBillboardPath && camera && this.billboardMode && !cache.useBillboardPosition) {
  998. const storedTranslation = TmpVectors.Vector3[0];
  999. this._worldMatrix.getTranslationToRef(storedTranslation); // Save translation
  1000. // Cancel camera rotation
  1001. TmpVectors.Matrix[1].copyFrom(camera.getViewMatrix());
  1002. if (this._scene.useRightHandedSystem) {
  1003. TmpVectors.Matrix[1].multiplyToRef(convertRHSToLHS, TmpVectors.Matrix[1]);
  1004. }
  1005. TmpVectors.Matrix[1].setTranslationFromFloats(0, 0, 0);
  1006. TmpVectors.Matrix[1].invertToRef(TmpVectors.Matrix[0]);
  1007. if ((this.billboardMode & TransformNode.BILLBOARDMODE_ALL) !== TransformNode.BILLBOARDMODE_ALL) {
  1008. TmpVectors.Matrix[0].decompose(undefined, TmpVectors.Quaternion[0], undefined);
  1009. const eulerAngles = TmpVectors.Vector3[1];
  1010. TmpVectors.Quaternion[0].toEulerAnglesToRef(eulerAngles);
  1011. if ((this.billboardMode & TransformNode.BILLBOARDMODE_X) !== TransformNode.BILLBOARDMODE_X) {
  1012. eulerAngles.x = 0;
  1013. }
  1014. if ((this.billboardMode & TransformNode.BILLBOARDMODE_Y) !== TransformNode.BILLBOARDMODE_Y) {
  1015. eulerAngles.y = 0;
  1016. }
  1017. if ((this.billboardMode & TransformNode.BILLBOARDMODE_Z) !== TransformNode.BILLBOARDMODE_Z) {
  1018. eulerAngles.z = 0;
  1019. }
  1020. Matrix.RotationYawPitchRollToRef(eulerAngles.y, eulerAngles.x, eulerAngles.z, TmpVectors.Matrix[0]);
  1021. }
  1022. this._worldMatrix.setTranslationFromFloats(0, 0, 0);
  1023. this._worldMatrix.multiplyToRef(TmpVectors.Matrix[0], this._worldMatrix);
  1024. // Restore translation
  1025. this._worldMatrix.setTranslation(TmpVectors.Vector3[0]);
  1026. }
  1027. // Billboarding based on camera position
  1028. else if (cache.useBillboardPath && camera && cache.useBillboardPosition) {
  1029. const storedTranslation = TmpVectors.Vector3[0];
  1030. // Save translation
  1031. this._worldMatrix.getTranslationToRef(storedTranslation);
  1032. // Compute camera position in local space
  1033. const cameraPosition = camera.globalPosition;
  1034. this._worldMatrix.invertToRef(TmpVectors.Matrix[1]);
  1035. const camInObjSpace = TmpVectors.Vector3[1];
  1036. Vector3.TransformCoordinatesToRef(cameraPosition, TmpVectors.Matrix[1], camInObjSpace);
  1037. camInObjSpace.normalize();
  1038. // Find the lookAt info in local space
  1039. const yaw = -Math.atan2(camInObjSpace.z, camInObjSpace.x) + Math.PI / 2;
  1040. const len = Math.sqrt(camInObjSpace.x * camInObjSpace.x + camInObjSpace.z * camInObjSpace.z);
  1041. const pitch = -Math.atan2(camInObjSpace.y, len);
  1042. Quaternion.RotationYawPitchRollToRef(yaw, pitch, 0, TmpVectors.Quaternion[0]);
  1043. if ((this.billboardMode & TransformNode.BILLBOARDMODE_ALL) !== TransformNode.BILLBOARDMODE_ALL) {
  1044. const eulerAngles = TmpVectors.Vector3[1];
  1045. TmpVectors.Quaternion[0].toEulerAnglesToRef(eulerAngles);
  1046. if ((this.billboardMode & TransformNode.BILLBOARDMODE_X) !== TransformNode.BILLBOARDMODE_X) {
  1047. eulerAngles.x = 0;
  1048. }
  1049. if ((this.billboardMode & TransformNode.BILLBOARDMODE_Y) !== TransformNode.BILLBOARDMODE_Y) {
  1050. eulerAngles.y = 0;
  1051. }
  1052. if ((this.billboardMode & TransformNode.BILLBOARDMODE_Z) !== TransformNode.BILLBOARDMODE_Z) {
  1053. eulerAngles.z = 0;
  1054. }
  1055. Matrix.RotationYawPitchRollToRef(eulerAngles.y, eulerAngles.x, eulerAngles.z, TmpVectors.Matrix[0]);
  1056. }
  1057. else {
  1058. Matrix.FromQuaternionToRef(TmpVectors.Quaternion[0], TmpVectors.Matrix[0]);
  1059. }
  1060. // Cancel translation
  1061. this._worldMatrix.setTranslationFromFloats(0, 0, 0);
  1062. // Rotate according to lookat (diff from local to lookat)
  1063. this._worldMatrix.multiplyToRef(TmpVectors.Matrix[0], this._worldMatrix);
  1064. // Restore translation
  1065. this._worldMatrix.setTranslation(TmpVectors.Vector3[0]);
  1066. }
  1067. // Normal matrix
  1068. if (!this.ignoreNonUniformScaling) {
  1069. if (this._scaling.isNonUniformWithinEpsilon(0.000001)) {
  1070. this._updateNonUniformScalingState(true);
  1071. }
  1072. else if (parent && parent._nonUniformScaling) {
  1073. this._updateNonUniformScalingState(parent._nonUniformScaling);
  1074. }
  1075. else {
  1076. this._updateNonUniformScalingState(false);
  1077. }
  1078. }
  1079. else {
  1080. this._updateNonUniformScalingState(false);
  1081. }
  1082. this._afterComputeWorldMatrix();
  1083. // Absolute position
  1084. this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
  1085. this._isAbsoluteSynced = false;
  1086. // Callbacks
  1087. this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
  1088. if (!this._poseMatrix) {
  1089. this._poseMatrix = Matrix.Invert(this._worldMatrix);
  1090. }
  1091. // Cache the determinant
  1092. this._worldMatrixDeterminantIsDirty = true;
  1093. return this._worldMatrix;
  1094. }
  1095. /**
  1096. * Resets this nodeTransform's local matrix to Matrix.Identity().
  1097. * @param independentOfChildren indicates if all child nodeTransform's world-space transform should be preserved.
  1098. */
  1099. resetLocalMatrix(independentOfChildren = true) {
  1100. this.computeWorldMatrix();
  1101. if (independentOfChildren) {
  1102. const children = this.getChildren();
  1103. for (let i = 0; i < children.length; ++i) {
  1104. const child = children[i];
  1105. if (child) {
  1106. child.computeWorldMatrix();
  1107. const bakedMatrix = TmpVectors.Matrix[0];
  1108. child._localMatrix.multiplyToRef(this._localMatrix, bakedMatrix);
  1109. const tmpRotationQuaternion = TmpVectors.Quaternion[0];
  1110. bakedMatrix.decompose(child.scaling, tmpRotationQuaternion, child.position);
  1111. if (child.rotationQuaternion) {
  1112. child.rotationQuaternion.copyFrom(tmpRotationQuaternion);
  1113. }
  1114. else {
  1115. tmpRotationQuaternion.toEulerAnglesToRef(child.rotation);
  1116. }
  1117. }
  1118. }
  1119. }
  1120. this.scaling.copyFromFloats(1, 1, 1);
  1121. this.position.copyFromFloats(0, 0, 0);
  1122. this.rotation.copyFromFloats(0, 0, 0);
  1123. //only if quaternion is already set
  1124. if (this.rotationQuaternion) {
  1125. this.rotationQuaternion = Quaternion.Identity();
  1126. }
  1127. this._worldMatrix = Matrix.Identity();
  1128. }
  1129. _afterComputeWorldMatrix() { }
  1130. /**
  1131. * If you'd like to be called back after the mesh position, rotation or scaling has been updated.
  1132. * @param func callback function to add
  1133. *
  1134. * @returns the TransformNode.
  1135. */
  1136. registerAfterWorldMatrixUpdate(func) {
  1137. this.onAfterWorldMatrixUpdateObservable.add(func);
  1138. return this;
  1139. }
  1140. /**
  1141. * Removes a registered callback function.
  1142. * @param func callback function to remove
  1143. * @returns the TransformNode.
  1144. */
  1145. unregisterAfterWorldMatrixUpdate(func) {
  1146. this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
  1147. return this;
  1148. }
  1149. /**
  1150. * Gets the position of the current mesh in camera space
  1151. * @param camera defines the camera to use
  1152. * @returns a position
  1153. */
  1154. getPositionInCameraSpace(camera = null) {
  1155. if (!camera) {
  1156. camera = this.getScene().activeCamera;
  1157. }
  1158. return Vector3.TransformCoordinates(this.getAbsolutePosition(), camera.getViewMatrix());
  1159. }
  1160. /**
  1161. * Returns the distance from the mesh to the active camera
  1162. * @param camera defines the camera to use
  1163. * @returns the distance
  1164. */
  1165. getDistanceToCamera(camera = null) {
  1166. if (!camera) {
  1167. camera = this.getScene().activeCamera;
  1168. }
  1169. return this.getAbsolutePosition().subtract(camera.globalPosition).length();
  1170. }
  1171. /**
  1172. * Clone the current transform node
  1173. * @param name Name of the new clone
  1174. * @param newParent New parent for the clone
  1175. * @param doNotCloneChildren Do not clone children hierarchy
  1176. * @returns the new transform node
  1177. */
  1178. clone(name, newParent, doNotCloneChildren) {
  1179. const result = SerializationHelper.Clone(() => new TransformNode(name, this.getScene()), this);
  1180. result.name = name;
  1181. result.id = name;
  1182. if (newParent) {
  1183. result.parent = newParent;
  1184. }
  1185. if (!doNotCloneChildren) {
  1186. // Children
  1187. const directDescendants = this.getDescendants(true);
  1188. for (let index = 0; index < directDescendants.length; index++) {
  1189. const child = directDescendants[index];
  1190. if (child.clone) {
  1191. child.clone(name + "." + child.name, result);
  1192. }
  1193. }
  1194. }
  1195. return result;
  1196. }
  1197. /**
  1198. * Serializes the objects information.
  1199. * @param currentSerializationObject defines the object to serialize in
  1200. * @returns the serialized object
  1201. */
  1202. serialize(currentSerializationObject) {
  1203. const serializationObject = SerializationHelper.Serialize(this, currentSerializationObject);
  1204. serializationObject.type = this.getClassName();
  1205. serializationObject.uniqueId = this.uniqueId;
  1206. // Parent
  1207. if (this.parent) {
  1208. this.parent._serializeAsParent(serializationObject);
  1209. }
  1210. serializationObject.localMatrix = this.getPivotMatrix().asArray();
  1211. serializationObject.isEnabled = this.isEnabled();
  1212. // Animations
  1213. SerializationHelper.AppendSerializedAnimations(this, serializationObject);
  1214. serializationObject.ranges = this.serializeAnimationRanges();
  1215. return serializationObject;
  1216. }
  1217. // Statics
  1218. /**
  1219. * Returns a new TransformNode object parsed from the source provided.
  1220. * @param parsedTransformNode is the source.
  1221. * @param scene the scene the object belongs to
  1222. * @param rootUrl is a string, it's the root URL to prefix the `delayLoadingFile` property with
  1223. * @returns a new TransformNode object parsed from the source provided.
  1224. */
  1225. static Parse(parsedTransformNode, scene, rootUrl) {
  1226. const transformNode = SerializationHelper.Parse(() => new TransformNode(parsedTransformNode.name, scene), parsedTransformNode, scene, rootUrl);
  1227. if (parsedTransformNode.localMatrix) {
  1228. transformNode.setPreTransformMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
  1229. }
  1230. else if (parsedTransformNode.pivotMatrix) {
  1231. transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.pivotMatrix));
  1232. }
  1233. transformNode.setEnabled(parsedTransformNode.isEnabled);
  1234. transformNode._waitingParsedUniqueId = parsedTransformNode.uniqueId;
  1235. // Parent
  1236. if (parsedTransformNode.parentId !== undefined) {
  1237. transformNode._waitingParentId = parsedTransformNode.parentId;
  1238. }
  1239. if (parsedTransformNode.parentInstanceIndex !== undefined) {
  1240. transformNode._waitingParentInstanceIndex = parsedTransformNode.parentInstanceIndex;
  1241. }
  1242. // Animations
  1243. if (parsedTransformNode.animations) {
  1244. for (let animationIndex = 0; animationIndex < parsedTransformNode.animations.length; animationIndex++) {
  1245. const parsedAnimation = parsedTransformNode.animations[animationIndex];
  1246. const internalClass = GetClass("BABYLON.Animation");
  1247. if (internalClass) {
  1248. transformNode.animations.push(internalClass.Parse(parsedAnimation));
  1249. }
  1250. }
  1251. Node.ParseAnimationRanges(transformNode, parsedTransformNode, scene);
  1252. }
  1253. if (parsedTransformNode.autoAnimate) {
  1254. scene.beginAnimation(transformNode, parsedTransformNode.autoAnimateFrom, parsedTransformNode.autoAnimateTo, parsedTransformNode.autoAnimateLoop, parsedTransformNode.autoAnimateSpeed || 1.0);
  1255. }
  1256. return transformNode;
  1257. }
  1258. /**
  1259. * Get all child-transformNodes of this node
  1260. * @param directDescendantsOnly defines if true only direct descendants of 'this' will be considered, if false direct and also indirect (children of children, an so on in a recursive manner) descendants of 'this' will be considered
  1261. * @param predicate defines an optional predicate that will be called on every evaluated child, the predicate must return true for a given child to be part of the result, otherwise it will be ignored
  1262. * @returns an array of TransformNode
  1263. */
  1264. getChildTransformNodes(directDescendantsOnly, predicate) {
  1265. const results = [];
  1266. this._getDescendants(results, directDescendantsOnly, (node) => {
  1267. return (!predicate || predicate(node)) && node instanceof TransformNode;
  1268. });
  1269. return results;
  1270. }
  1271. /**
  1272. * Releases resources associated with this transform node.
  1273. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  1274. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  1275. */
  1276. dispose(doNotRecurse, disposeMaterialAndTextures = false) {
  1277. // Animations
  1278. this.getScene().stopAnimation(this);
  1279. // Remove from scene
  1280. this.getScene().removeTransformNode(this);
  1281. if (this._parentContainer) {
  1282. const index = this._parentContainer.transformNodes.indexOf(this);
  1283. if (index > -1) {
  1284. this._parentContainer.transformNodes.splice(index, 1);
  1285. }
  1286. this._parentContainer = null;
  1287. }
  1288. this.onAfterWorldMatrixUpdateObservable.clear();
  1289. if (doNotRecurse) {
  1290. const transformNodes = this.getChildTransformNodes(true);
  1291. for (const transformNode of transformNodes) {
  1292. transformNode.parent = null;
  1293. transformNode.computeWorldMatrix(true);
  1294. }
  1295. }
  1296. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  1297. }
  1298. /**
  1299. * Uniformly scales the mesh to fit inside of a unit cube (1 X 1 X 1 units)
  1300. * @param includeDescendants Use the hierarchy's bounding box instead of the mesh's bounding box. Default is false
  1301. * @param ignoreRotation ignore rotation when computing the scale (ie. object will be axis aligned). Default is false
  1302. * @param predicate predicate that is passed in to getHierarchyBoundingVectors when selecting which object should be included when scaling
  1303. * @returns the current mesh
  1304. */
  1305. normalizeToUnitCube(includeDescendants = true, ignoreRotation = false, predicate) {
  1306. let storedRotation = null;
  1307. let storedRotationQuaternion = null;
  1308. if (ignoreRotation) {
  1309. if (this.rotationQuaternion) {
  1310. storedRotationQuaternion = this.rotationQuaternion.clone();
  1311. this.rotationQuaternion.copyFromFloats(0, 0, 0, 1);
  1312. }
  1313. else if (this.rotation) {
  1314. storedRotation = this.rotation.clone();
  1315. this.rotation.copyFromFloats(0, 0, 0);
  1316. }
  1317. }
  1318. const boundingVectors = this.getHierarchyBoundingVectors(includeDescendants, predicate);
  1319. const sizeVec = boundingVectors.max.subtract(boundingVectors.min);
  1320. const maxDimension = Math.max(sizeVec.x, sizeVec.y, sizeVec.z);
  1321. if (maxDimension === 0) {
  1322. return this;
  1323. }
  1324. const scale = 1 / maxDimension;
  1325. this.scaling.scaleInPlace(scale);
  1326. if (ignoreRotation) {
  1327. if (this.rotationQuaternion && storedRotationQuaternion) {
  1328. this.rotationQuaternion.copyFrom(storedRotationQuaternion);
  1329. }
  1330. else if (this.rotation && storedRotation) {
  1331. this.rotation.copyFrom(storedRotation);
  1332. }
  1333. }
  1334. return this;
  1335. }
  1336. _syncAbsoluteScalingAndRotation() {
  1337. if (!this._isAbsoluteSynced) {
  1338. this._worldMatrix.decompose(this._absoluteScaling, this._absoluteRotationQuaternion);
  1339. this._isAbsoluteSynced = true;
  1340. }
  1341. }
  1342. }
  1343. // Statics
  1344. /**
  1345. * Object will not rotate to face the camera
  1346. */
  1347. TransformNode.BILLBOARDMODE_NONE = 0;
  1348. /**
  1349. * Object will rotate to face the camera but only on the x axis
  1350. */
  1351. TransformNode.BILLBOARDMODE_X = 1;
  1352. /**
  1353. * Object will rotate to face the camera but only on the y axis
  1354. */
  1355. TransformNode.BILLBOARDMODE_Y = 2;
  1356. /**
  1357. * Object will rotate to face the camera but only on the z axis
  1358. */
  1359. TransformNode.BILLBOARDMODE_Z = 4;
  1360. /**
  1361. * Object will rotate to face the camera
  1362. */
  1363. TransformNode.BILLBOARDMODE_ALL = 7;
  1364. /**
  1365. * Object will rotate to face the camera's position instead of orientation
  1366. */
  1367. TransformNode.BILLBOARDMODE_USE_POSITION = 128;
  1368. /**
  1369. * Child transform with Billboard flags should or should not apply parent rotation (default if off)
  1370. */
  1371. TransformNode.BillboardUseParentOrientation = false;
  1372. TransformNode._TmpRotation = Quaternion.Zero();
  1373. TransformNode._TmpScaling = Vector3.Zero();
  1374. TransformNode._TmpTranslation = Vector3.Zero();
  1375. TransformNode._LookAtVectorCache = new Vector3(0, 0, 0);
  1376. TransformNode._RotationAxisCache = new Quaternion();
  1377. __decorate([
  1378. serializeAsVector3("position")
  1379. ], TransformNode.prototype, "_position", void 0);
  1380. __decorate([
  1381. serializeAsVector3("rotation")
  1382. ], TransformNode.prototype, "_rotation", void 0);
  1383. __decorate([
  1384. serializeAsQuaternion("rotationQuaternion")
  1385. ], TransformNode.prototype, "_rotationQuaternion", void 0);
  1386. __decorate([
  1387. serializeAsVector3("scaling")
  1388. ], TransformNode.prototype, "_scaling", void 0);
  1389. __decorate([
  1390. serialize("billboardMode")
  1391. ], TransformNode.prototype, "_billboardMode", void 0);
  1392. __decorate([
  1393. serialize()
  1394. ], TransformNode.prototype, "scalingDeterminant", void 0);
  1395. __decorate([
  1396. serialize("infiniteDistance")
  1397. ], TransformNode.prototype, "_infiniteDistance", void 0);
  1398. __decorate([
  1399. serialize()
  1400. ], TransformNode.prototype, "ignoreNonUniformScaling", void 0);
  1401. __decorate([
  1402. serialize()
  1403. ], TransformNode.prototype, "reIntegrateRotationIntoRotationQuaternion", void 0);
  1404. //# sourceMappingURL=transformNode.js.map