node.ts 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. import { Scene } from "./scene";
  2. import { Nullable } from "./types";
  3. import { Matrix, Vector3 } from "./Maths/math.vector";
  4. import { Engine } from "./Engines/engine";
  5. import { IBehaviorAware, Behavior } from "./Behaviors/behavior";
  6. import { serialize } from "./Misc/decorators";
  7. import { Observable, Observer } from "./Misc/observable";
  8. import { EngineStore } from "./Engines/engineStore";
  9. import { _DevTools } from './Misc/devTools';
  10. import { AbstractActionManager } from './Actions/abstractActionManager';
  11. import { IInspectable } from './Misc/iInspectable';
  12. declare type Animatable = import("./Animations/animatable").Animatable;
  13. declare type AnimationPropertiesOverride = import("./Animations/animationPropertiesOverride").AnimationPropertiesOverride;
  14. declare type Animation = import("./Animations/animation").Animation;
  15. declare type AnimationRange = import("./Animations/animationRange").AnimationRange;
  16. declare type AbstractMesh = import("./Meshes/abstractMesh").AbstractMesh;
  17. /**
  18. * Defines how a node can be built from a string name.
  19. */
  20. export type NodeConstructor = (name: string, scene: Scene, options?: any) => () => Node;
  21. /**
  22. * Node is the basic class for all scene objects (Mesh, Light, Camera.)
  23. */
  24. export class Node implements IBehaviorAware<Node> {
  25. /** @hidden */
  26. public static _AnimationRangeFactory = (name: string, from: number, to: number): AnimationRange => {
  27. throw _DevTools.WarnImport("AnimationRange");
  28. }
  29. private static _NodeConstructors: { [key: string]: any } = {};
  30. /**
  31. * Add a new node constructor
  32. * @param type defines the type name of the node to construct
  33. * @param constructorFunc defines the constructor function
  34. */
  35. public static AddNodeConstructor(type: string, constructorFunc: NodeConstructor) {
  36. this._NodeConstructors[type] = constructorFunc;
  37. }
  38. /**
  39. * Returns a node constructor based on type name
  40. * @param type defines the type name
  41. * @param name defines the new node name
  42. * @param scene defines the hosting scene
  43. * @param options defines optional options to transmit to constructors
  44. * @returns the new constructor or null
  45. */
  46. public static Construct(type: string, name: string, scene: Scene, options?: any): Nullable<() => Node> {
  47. let constructorFunc = this._NodeConstructors[type];
  48. if (!constructorFunc) {
  49. return null;
  50. }
  51. return constructorFunc(name, scene, options);
  52. }
  53. /**
  54. * Gets or sets the name of the node
  55. */
  56. @serialize()
  57. public name: string;
  58. /**
  59. * Gets or sets the id of the node
  60. */
  61. @serialize()
  62. public id: string;
  63. /**
  64. * Gets or sets the unique id of the node
  65. */
  66. @serialize()
  67. public uniqueId: number;
  68. /**
  69. * Gets or sets a string used to store user defined state for the node
  70. */
  71. @serialize()
  72. public state = "";
  73. /**
  74. * Gets or sets an object used to store user defined information for the node
  75. */
  76. @serialize()
  77. public metadata: any = null;
  78. /**
  79. * For internal use only. Please do not use.
  80. */
  81. public reservedDataStore: any = null;
  82. /**
  83. * List of inspectable custom properties (used by the Inspector)
  84. * @see https://doc.babylonjs.com/how_to/debug_layer#extensibility
  85. */
  86. public inspectableCustomProperties: IInspectable[];
  87. private _doNotSerialize = false;
  88. /**
  89. * Gets or sets a boolean used to define if the node must be serialized
  90. */
  91. public get doNotSerialize() {
  92. if (this._doNotSerialize) {
  93. return true;
  94. }
  95. if (this._parentNode) {
  96. return this._parentNode.doNotSerialize;
  97. }
  98. return false;
  99. }
  100. public set doNotSerialize(value: boolean) {
  101. this._doNotSerialize = value;
  102. }
  103. /** @hidden */
  104. public _isDisposed = false;
  105. /**
  106. * Gets a list of Animations associated with the node
  107. */
  108. public animations = new Array<Animation>();
  109. protected _ranges: { [name: string]: Nullable<AnimationRange> } = {};
  110. /**
  111. * Callback raised when the node is ready to be used
  112. */
  113. public onReady: Nullable<(node: Node) => void> = null;
  114. private _isEnabled = true;
  115. private _isParentEnabled = true;
  116. private _isReady = true;
  117. /** @hidden */
  118. public _currentRenderId = -1;
  119. private _parentUpdateId = -1;
  120. /** @hidden */
  121. public _childUpdateId = -1;
  122. /** @hidden */
  123. public _waitingParentId: Nullable<string> = null;
  124. /** @hidden */
  125. public _scene: Scene;
  126. /** @hidden */
  127. public _cache: any = {};
  128. private _parentNode: Nullable<Node> = null;
  129. private _children: Nullable<Node[]> = null;
  130. /** @hidden */
  131. public _worldMatrix = Matrix.Identity();
  132. /** @hidden */
  133. public _worldMatrixDeterminant = 0;
  134. /** @hidden */
  135. public _worldMatrixDeterminantIsDirty = true;
  136. /** @hidden */
  137. private _sceneRootNodesIndex = -1;
  138. /**
  139. * Gets a boolean indicating if the node has been disposed
  140. * @returns true if the node was disposed
  141. */
  142. public isDisposed(): boolean {
  143. return this._isDisposed;
  144. }
  145. /**
  146. * Gets or sets the parent of the node (without keeping the current position in the scene)
  147. * @see https://doc.babylonjs.com/how_to/parenting
  148. */
  149. public set parent(parent: Nullable<Node>) {
  150. if (this._parentNode === parent) {
  151. return;
  152. }
  153. const previousParentNode = this._parentNode;
  154. // Remove self from list of children of parent
  155. if (this._parentNode && this._parentNode._children !== undefined && this._parentNode._children !== null) {
  156. var index = this._parentNode._children.indexOf(this);
  157. if (index !== -1) {
  158. this._parentNode._children.splice(index, 1);
  159. }
  160. if (!parent && !this._isDisposed) {
  161. this.addToSceneRootNodes();
  162. }
  163. }
  164. // Store new parent
  165. this._parentNode = parent;
  166. // Add as child to new parent
  167. if (this._parentNode) {
  168. if (this._parentNode._children === undefined || this._parentNode._children === null) {
  169. this._parentNode._children = new Array<Node>();
  170. }
  171. this._parentNode._children.push(this);
  172. if (!previousParentNode) {
  173. this.removeFromSceneRootNodes();
  174. }
  175. }
  176. // Enabled state
  177. this._syncParentEnabledState();
  178. }
  179. public get parent(): Nullable<Node> {
  180. return this._parentNode;
  181. }
  182. private addToSceneRootNodes() {
  183. if (this._sceneRootNodesIndex === -1) {
  184. this._sceneRootNodesIndex = this._scene.rootNodes.length;
  185. this._scene.rootNodes.push(this);
  186. }
  187. }
  188. private removeFromSceneRootNodes() {
  189. if (this._sceneRootNodesIndex !== -1) {
  190. const rootNodes = this._scene.rootNodes;
  191. const lastIdx = rootNodes.length - 1;
  192. rootNodes[this._sceneRootNodesIndex] = rootNodes[lastIdx];
  193. rootNodes[this._sceneRootNodesIndex]._sceneRootNodesIndex = this._sceneRootNodesIndex;
  194. this._scene.rootNodes.pop();
  195. this._sceneRootNodesIndex = -1;
  196. }
  197. }
  198. private _animationPropertiesOverride: Nullable<AnimationPropertiesOverride> = null;
  199. /**
  200. * Gets or sets the animation properties override
  201. */
  202. public get animationPropertiesOverride(): Nullable<AnimationPropertiesOverride> {
  203. if (!this._animationPropertiesOverride) {
  204. return this._scene.animationPropertiesOverride;
  205. }
  206. return this._animationPropertiesOverride;
  207. }
  208. public set animationPropertiesOverride(value: Nullable<AnimationPropertiesOverride>) {
  209. this._animationPropertiesOverride = value;
  210. }
  211. /**
  212. * Gets a string idenfifying the name of the class
  213. * @returns "Node" string
  214. */
  215. public getClassName(): string {
  216. return "Node";
  217. }
  218. /** @hidden */
  219. public readonly _isNode = true;
  220. /**
  221. * An event triggered when the mesh is disposed
  222. */
  223. public onDisposeObservable = new Observable<Node>();
  224. private _onDisposeObserver: Nullable<Observer<Node>> = null;
  225. /**
  226. * Sets a callback that will be raised when the node will be disposed
  227. */
  228. public set onDispose(callback: () => void) {
  229. if (this._onDisposeObserver) {
  230. this.onDisposeObservable.remove(this._onDisposeObserver);
  231. }
  232. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  233. }
  234. /**
  235. * Creates a new Node
  236. * @param name the name and id to be given to this node
  237. * @param scene the scene this node will be added to
  238. * @param addToRootNodes the node will be added to scene.rootNodes
  239. */
  240. constructor(name: string, scene: Nullable<Scene> = null, addToRootNodes = true) {
  241. this.name = name;
  242. this.id = name;
  243. this._scene = <Scene>(scene || EngineStore.LastCreatedScene);
  244. this.uniqueId = this._scene.getUniqueId();
  245. this._initCache();
  246. if (addToRootNodes) {
  247. this.addToSceneRootNodes();
  248. }
  249. }
  250. /**
  251. * Gets the scene of the node
  252. * @returns a scene
  253. */
  254. public getScene(): Scene {
  255. return this._scene;
  256. }
  257. /**
  258. * Gets the engine of the node
  259. * @returns a Engine
  260. */
  261. public getEngine(): Engine {
  262. return this._scene.getEngine();
  263. }
  264. // Behaviors
  265. private _behaviors = new Array<Behavior<Node>>();
  266. /**
  267. * Attach a behavior to the node
  268. * @see http://doc.babylonjs.com/features/behaviour
  269. * @param behavior defines the behavior to attach
  270. * @param attachImmediately defines that the behavior must be attached even if the scene is still loading
  271. * @returns the current Node
  272. */
  273. public addBehavior(behavior: Behavior<Node>, attachImmediately = false): Node {
  274. var index = this._behaviors.indexOf(behavior);
  275. if (index !== -1) {
  276. return this;
  277. }
  278. behavior.init();
  279. if (this._scene.isLoading && !attachImmediately) {
  280. // We defer the attach when the scene will be loaded
  281. this._scene.onDataLoadedObservable.addOnce(() => {
  282. behavior.attach(this);
  283. });
  284. } else {
  285. behavior.attach(this);
  286. }
  287. this._behaviors.push(behavior);
  288. return this;
  289. }
  290. /**
  291. * Remove an attached behavior
  292. * @see http://doc.babylonjs.com/features/behaviour
  293. * @param behavior defines the behavior to attach
  294. * @returns the current Node
  295. */
  296. public removeBehavior(behavior: Behavior<Node>): Node {
  297. var index = this._behaviors.indexOf(behavior);
  298. if (index === -1) {
  299. return this;
  300. }
  301. this._behaviors[index].detach();
  302. this._behaviors.splice(index, 1);
  303. return this;
  304. }
  305. /**
  306. * Gets the list of attached behaviors
  307. * @see http://doc.babylonjs.com/features/behaviour
  308. */
  309. public get behaviors(): Behavior<Node>[] {
  310. return this._behaviors;
  311. }
  312. /**
  313. * Gets an attached behavior by name
  314. * @param name defines the name of the behavior to look for
  315. * @see http://doc.babylonjs.com/features/behaviour
  316. * @returns null if behavior was not found else the requested behavior
  317. */
  318. public getBehaviorByName(name: string): Nullable<Behavior<Node>> {
  319. for (var behavior of this._behaviors) {
  320. if (behavior.name === name) {
  321. return behavior;
  322. }
  323. }
  324. return null;
  325. }
  326. /**
  327. * Returns the latest update of the World matrix
  328. * @returns a Matrix
  329. */
  330. public getWorldMatrix(): Matrix {
  331. if (this._currentRenderId !== this._scene.getRenderId()) {
  332. this.computeWorldMatrix();
  333. }
  334. return this._worldMatrix;
  335. }
  336. /** @hidden */
  337. public _getWorldMatrixDeterminant(): number {
  338. if (this._worldMatrixDeterminantIsDirty) {
  339. this._worldMatrixDeterminantIsDirty = false;
  340. this._worldMatrixDeterminant = this._worldMatrix.determinant();
  341. }
  342. return this._worldMatrixDeterminant;
  343. }
  344. /**
  345. * Returns directly the latest state of the mesh World matrix.
  346. * A Matrix is returned.
  347. */
  348. public get worldMatrixFromCache(): Matrix {
  349. return this._worldMatrix;
  350. }
  351. // override it in derived class if you add new variables to the cache
  352. // and call the parent class method
  353. /** @hidden */
  354. public _initCache() {
  355. this._cache = {};
  356. this._cache.parent = undefined;
  357. }
  358. /** @hidden */
  359. public updateCache(force?: boolean): void {
  360. if (!force && this.isSynchronized()) {
  361. return;
  362. }
  363. this._cache.parent = this.parent;
  364. this._updateCache();
  365. }
  366. /** @hidden */
  367. public _getActionManagerForTrigger(trigger?: number, initialCall = true): Nullable<AbstractActionManager> {
  368. if (!this.parent) {
  369. return null;
  370. }
  371. return this.parent._getActionManagerForTrigger(trigger, false);
  372. }
  373. // override it in derived class if you add new variables to the cache
  374. // and call the parent class method if !ignoreParentClass
  375. /** @hidden */
  376. public _updateCache(ignoreParentClass?: boolean): void {
  377. }
  378. // override it in derived class if you add new variables to the cache
  379. /** @hidden */
  380. public _isSynchronized(): boolean {
  381. return true;
  382. }
  383. /** @hidden */
  384. public _markSyncedWithParent() {
  385. if (this._parentNode) {
  386. this._parentUpdateId = this._parentNode._childUpdateId;
  387. }
  388. }
  389. /** @hidden */
  390. public isSynchronizedWithParent(): boolean {
  391. if (!this._parentNode) {
  392. return true;
  393. }
  394. if (this._parentUpdateId !== this._parentNode._childUpdateId) {
  395. return false;
  396. }
  397. return this._parentNode.isSynchronized();
  398. }
  399. /** @hidden */
  400. public isSynchronized(): boolean {
  401. if (this._cache.parent != this._parentNode) {
  402. this._cache.parent = this._parentNode;
  403. return false;
  404. }
  405. if (this._parentNode && !this.isSynchronizedWithParent()) {
  406. return false;
  407. }
  408. return this._isSynchronized();
  409. }
  410. /**
  411. * Is this node ready to be used/rendered
  412. * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
  413. * @return true if the node is ready
  414. */
  415. public isReady(completeCheck = false): boolean {
  416. return this._isReady;
  417. }
  418. /**
  419. * Is this node enabled?
  420. * If the node has a parent, all ancestors will be checked and false will be returned if any are false (not enabled), otherwise will return true
  421. * @param checkAncestors indicates if this method should check the ancestors. The default is to check the ancestors. If set to false, the method will return the value of this node without checking ancestors
  422. * @return whether this node (and its parent) is enabled
  423. */
  424. public isEnabled(checkAncestors: boolean = true): boolean {
  425. if (checkAncestors === false) {
  426. return this._isEnabled;
  427. }
  428. if (!this._isEnabled) {
  429. return false;
  430. }
  431. return this._isParentEnabled;
  432. }
  433. /** @hidden */
  434. protected _syncParentEnabledState() {
  435. this._isParentEnabled = this._parentNode ? this._parentNode.isEnabled() : true;
  436. if (this._children) {
  437. this._children.forEach((c) => {
  438. c._syncParentEnabledState(); // Force children to update accordingly
  439. });
  440. }
  441. }
  442. /**
  443. * Set the enabled state of this node
  444. * @param value defines the new enabled state
  445. */
  446. public setEnabled(value: boolean): void {
  447. this._isEnabled = value;
  448. this._syncParentEnabledState();
  449. }
  450. /**
  451. * Is this node a descendant of the given node?
  452. * The function will iterate up the hierarchy until the ancestor was found or no more parents defined
  453. * @param ancestor defines the parent node to inspect
  454. * @returns a boolean indicating if this node is a descendant of the given node
  455. */
  456. public isDescendantOf(ancestor: Node): boolean {
  457. if (this.parent) {
  458. if (this.parent === ancestor) {
  459. return true;
  460. }
  461. return this.parent.isDescendantOf(ancestor);
  462. }
  463. return false;
  464. }
  465. /** @hidden */
  466. public _getDescendants(results: Node[], directDescendantsOnly: boolean = false, predicate?: (node: Node) => boolean): void {
  467. if (!this._children) {
  468. return;
  469. }
  470. for (var index = 0; index < this._children.length; index++) {
  471. var item = this._children[index];
  472. if (!predicate || predicate(item)) {
  473. results.push(item);
  474. }
  475. if (!directDescendantsOnly) {
  476. item._getDescendants(results, false, predicate);
  477. }
  478. }
  479. }
  480. /**
  481. * Will return all nodes that have this node as ascendant
  482. * @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
  483. * @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
  484. * @return all children nodes of all types
  485. */
  486. public getDescendants(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): Node[] {
  487. var results = new Array<Node>();
  488. this._getDescendants(results, directDescendantsOnly, predicate);
  489. return results;
  490. }
  491. /**
  492. * Get all child-meshes of this node
  493. * @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 (Default: false)
  494. * @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
  495. * @returns an array of AbstractMesh
  496. */
  497. public getChildMeshes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): AbstractMesh[] {
  498. var results: Array<AbstractMesh> = [];
  499. this._getDescendants(results, directDescendantsOnly, (node: Node) => {
  500. return ((!predicate || predicate(node)) && ((<AbstractMesh>node).cullingStrategy !== undefined));
  501. });
  502. return results;
  503. }
  504. /**
  505. * Get all direct children of this node
  506. * @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
  507. * @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 (Default: true)
  508. * @returns an array of Node
  509. */
  510. public getChildren(predicate?: (node: Node) => boolean, directDescendantsOnly = true): Node[] {
  511. return this.getDescendants(directDescendantsOnly, predicate);
  512. }
  513. /** @hidden */
  514. public _setReady(state: boolean): void {
  515. if (state === this._isReady) {
  516. return;
  517. }
  518. if (!state) {
  519. this._isReady = false;
  520. return;
  521. }
  522. if (this.onReady) {
  523. this.onReady(this);
  524. }
  525. this._isReady = true;
  526. }
  527. /**
  528. * Get an animation by name
  529. * @param name defines the name of the animation to look for
  530. * @returns null if not found else the requested animation
  531. */
  532. public getAnimationByName(name: string): Nullable<Animation> {
  533. for (var i = 0; i < this.animations.length; i++) {
  534. var animation = this.animations[i];
  535. if (animation.name === name) {
  536. return animation;
  537. }
  538. }
  539. return null;
  540. }
  541. /**
  542. * Creates an animation range for this node
  543. * @param name defines the name of the range
  544. * @param from defines the starting key
  545. * @param to defines the end key
  546. */
  547. public createAnimationRange(name: string, from: number, to: number): void {
  548. // check name not already in use
  549. if (!this._ranges[name]) {
  550. this._ranges[name] = Node._AnimationRangeFactory(name, from, to);
  551. for (var i = 0, nAnimations = this.animations.length; i < nAnimations; i++) {
  552. if (this.animations[i]) {
  553. this.animations[i].createRange(name, from, to);
  554. }
  555. }
  556. }
  557. }
  558. /**
  559. * Delete a specific animation range
  560. * @param name defines the name of the range to delete
  561. * @param deleteFrames defines if animation frames from the range must be deleted as well
  562. */
  563. public deleteAnimationRange(name: string, deleteFrames = true): void {
  564. for (var i = 0, nAnimations = this.animations.length; i < nAnimations; i++) {
  565. if (this.animations[i]) {
  566. this.animations[i].deleteRange(name, deleteFrames);
  567. }
  568. }
  569. this._ranges[name] = null; // said much faster than 'delete this._range[name]'
  570. }
  571. /**
  572. * Get an animation range by name
  573. * @param name defines the name of the animation range to look for
  574. * @returns null if not found else the requested animation range
  575. */
  576. public getAnimationRange(name: string): Nullable<AnimationRange> {
  577. return this._ranges[name];
  578. }
  579. /**
  580. * Gets the list of all animation ranges defined on this node
  581. * @returns an array
  582. */
  583. public getAnimationRanges(): Nullable<AnimationRange>[] {
  584. var animationRanges: Nullable<AnimationRange>[] = [];
  585. var name: string;
  586. for (name in this._ranges) {
  587. animationRanges.push(this._ranges[name]);
  588. }
  589. return animationRanges;
  590. }
  591. /**
  592. * Will start the animation sequence
  593. * @param name defines the range frames for animation sequence
  594. * @param loop defines if the animation should loop (false by default)
  595. * @param speedRatio defines the speed factor in which to run the animation (1 by default)
  596. * @param onAnimationEnd defines a function to be executed when the animation ended (undefined by default)
  597. * @returns the object created for this animation. If range does not exist, it will return null
  598. */
  599. public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Nullable<Animatable> {
  600. var range = this.getAnimationRange(name);
  601. if (!range) {
  602. return null;
  603. }
  604. return this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
  605. }
  606. /**
  607. * Serialize animation ranges into a JSON compatible object
  608. * @returns serialization object
  609. */
  610. public serializeAnimationRanges(): any {
  611. var serializationRanges = [];
  612. for (var name in this._ranges) {
  613. var localRange = this._ranges[name];
  614. if (!localRange) {
  615. continue;
  616. }
  617. var range: any = {};
  618. range.name = name;
  619. range.from = localRange.from;
  620. range.to = localRange.to;
  621. serializationRanges.push(range);
  622. }
  623. return serializationRanges;
  624. }
  625. /**
  626. * Computes the world matrix of the node
  627. * @param force defines if the cache version should be invalidated forcing the world matrix to be created from scratch
  628. * @returns the world matrix
  629. */
  630. public computeWorldMatrix(force?: boolean): Matrix {
  631. if (!this._worldMatrix) {
  632. this._worldMatrix = Matrix.Identity();
  633. }
  634. return this._worldMatrix;
  635. }
  636. /**
  637. * Releases resources associated with this node.
  638. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  639. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  640. */
  641. public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
  642. this._isDisposed = true;
  643. if (!doNotRecurse) {
  644. const nodes = this.getDescendants(true);
  645. for (const node of nodes) {
  646. node.dispose(doNotRecurse, disposeMaterialAndTextures);
  647. }
  648. }
  649. if (!this.parent) {
  650. this.removeFromSceneRootNodes();
  651. } else {
  652. this.parent = null;
  653. }
  654. // Callback
  655. this.onDisposeObservable.notifyObservers(this);
  656. this.onDisposeObservable.clear();
  657. // Behaviors
  658. for (var behavior of this._behaviors) {
  659. behavior.detach();
  660. }
  661. this._behaviors = [];
  662. }
  663. /**
  664. * Parse animation range data from a serialization object and store them into a given node
  665. * @param node defines where to store the animation ranges
  666. * @param parsedNode defines the serialization object to read data from
  667. * @param scene defines the hosting scene
  668. */
  669. public static ParseAnimationRanges(node: Node, parsedNode: any, scene: Scene): void {
  670. if (parsedNode.ranges) {
  671. for (var index = 0; index < parsedNode.ranges.length; index++) {
  672. var data = parsedNode.ranges[index];
  673. node.createAnimationRange(data.name, data.from, data.to);
  674. }
  675. }
  676. }
  677. /**
  678. * Return the minimum and maximum world vectors of the entire hierarchy under current node
  679. * @param includeDescendants Include bounding info from descendants as well (true by default)
  680. * @param predicate defines a callback function that can be customize to filter what meshes should be included in the list used to compute the bounding vectors
  681. * @returns the new bounding vectors
  682. */
  683. public getHierarchyBoundingVectors(includeDescendants = true, predicate: Nullable<(abstractMesh: AbstractMesh) => boolean> = null): { min: Vector3, max: Vector3 } {
  684. // Ensures that all world matrix will be recomputed.
  685. this.getScene().incrementRenderId();
  686. this.computeWorldMatrix(true);
  687. let min: Vector3;
  688. let max: Vector3;
  689. let thisAbstractMesh = (this as Node as AbstractMesh);
  690. if (thisAbstractMesh.getBoundingInfo && thisAbstractMesh.subMeshes) {
  691. // If this is an abstract mesh get its bounding info
  692. let boundingInfo = thisAbstractMesh.getBoundingInfo();
  693. min = boundingInfo.boundingBox.minimumWorld.clone();
  694. max = boundingInfo.boundingBox.maximumWorld.clone();
  695. } else {
  696. min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  697. max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
  698. }
  699. if (includeDescendants) {
  700. let descendants = this.getDescendants(false);
  701. for (var descendant of descendants) {
  702. let childMesh = <AbstractMesh>descendant;
  703. childMesh.computeWorldMatrix(true);
  704. // Filters meshes based on custom predicate function.
  705. if (predicate && !predicate(childMesh)) {
  706. continue;
  707. }
  708. //make sure we have the needed params to get mix and max
  709. if (!childMesh.getBoundingInfo || childMesh.getTotalVertices() === 0) {
  710. continue;
  711. }
  712. let childBoundingInfo = childMesh.getBoundingInfo();
  713. let boundingBox = childBoundingInfo.boundingBox;
  714. var minBox = boundingBox.minimumWorld;
  715. var maxBox = boundingBox.maximumWorld;
  716. Vector3.CheckExtends(minBox, min, max);
  717. Vector3.CheckExtends(maxBox, min, max);
  718. }
  719. }
  720. return {
  721. min: min,
  722. max: max
  723. };
  724. }
  725. }