transformNode.ts 55 KB

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