babylon.node.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. module BABYLON {
  2. /**
  3. * Node is the basic class for all scene objects (Mesh, Light Camera).
  4. */
  5. export class Node {
  6. /**
  7. * Gets or sets the name of the node
  8. */
  9. @serialize()
  10. public name: string;
  11. /**
  12. * Gets or sets the id of the node
  13. */
  14. @serialize()
  15. public id: string;
  16. /**
  17. * Gets or sets the unique id of the node
  18. */
  19. @serialize()
  20. public uniqueId: number;
  21. /**
  22. * Gets or sets a string used to store user defined state for the node
  23. */
  24. @serialize()
  25. public state = "";
  26. /**
  27. * Gets or sets an object used to store user defined information for the node
  28. */
  29. @serialize()
  30. public metadata: any = null;
  31. /**
  32. * Gets or sets a boolean used to define if the node must be serialized
  33. */
  34. public doNotSerialize = false;
  35. /** @ignore */
  36. public _isDisposed = false;
  37. /**
  38. * Gets a list of {BABYLON.Animation} associated with the node
  39. */
  40. public animations = new Array<Animation>();
  41. private _ranges: { [name: string]: Nullable<AnimationRange> } = {};
  42. /**
  43. * Callback raised when the node is ready to be used
  44. */
  45. public onReady: (node: Node) => void;
  46. private _isEnabled = true;
  47. private _isReady = true;
  48. /** @ignore */
  49. public _currentRenderId = -1;
  50. private _parentRenderId = -1;
  51. /** @ignore */
  52. public _waitingParentId: Nullable<string>;
  53. private _scene: Scene;
  54. /** @ignore */
  55. public _cache: any;
  56. private _parentNode: Nullable<Node>;
  57. private _children: Node[];
  58. /**
  59. * Gets a boolean indicating if the node has been disposed
  60. * @returns true if the node was disposed
  61. */
  62. public isDisposed(): boolean {
  63. return this._isDisposed;
  64. }
  65. /**
  66. * Gets or sets the parent of the node
  67. */
  68. public set parent(parent: Nullable<Node>) {
  69. if (this._parentNode === parent) {
  70. return;
  71. }
  72. // Remove self from list of children of parent
  73. if (this._parentNode && this._parentNode._children !== undefined && this._parentNode._children !== null) {
  74. var index = this._parentNode._children.indexOf(this);
  75. if (index !== -1) {
  76. this._parentNode._children.splice(index, 1);
  77. }
  78. }
  79. // Store new parent
  80. this._parentNode = parent;
  81. // Add as child to new parent
  82. if (this._parentNode) {
  83. if (this._parentNode._children === undefined || this._parentNode._children === null) {
  84. this._parentNode._children = new Array<Node>();
  85. }
  86. this._parentNode._children.push(this);
  87. }
  88. }
  89. public get parent(): Nullable<Node> {
  90. return this._parentNode;
  91. }
  92. /**
  93. * Gets a string idenfifying the name of the class
  94. * @returns "Node" string
  95. */
  96. public getClassName(): string {
  97. return "Node";
  98. }
  99. /**
  100. * An event triggered when the mesh is disposed
  101. * @type {BABYLON.Observable}
  102. */
  103. public onDisposeObservable = new Observable<Node>();
  104. private _onDisposeObserver: Nullable<Observer<Node>>;
  105. /**
  106. * Sets a callback that will be raised when the node will be disposed
  107. */
  108. public set onDispose(callback: () => void) {
  109. if (this._onDisposeObserver) {
  110. this.onDisposeObservable.remove(this._onDisposeObserver);
  111. }
  112. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  113. }
  114. /**
  115. * Creates a new Node
  116. * @param {string} name - the name and id to be given to this node
  117. * @param {BABYLON.Scene} the scene this node will be added to
  118. */
  119. constructor(name: string, scene: Nullable<Scene> = null) {
  120. this.name = name;
  121. this.id = name;
  122. this._scene = <Scene>(scene || Engine.LastCreatedScene);
  123. this.uniqueId = this._scene.getUniqueId();
  124. this._initCache();
  125. }
  126. /**
  127. * Gets the scene of the node
  128. * @returns a {BABYLON.Scene}
  129. */
  130. public getScene(): Scene {
  131. return this._scene;
  132. }
  133. /**
  134. * Gets the engine of the node
  135. * @returns a {BABYLON.Engine}
  136. */
  137. public getEngine(): Engine {
  138. return this._scene.getEngine();
  139. }
  140. // Behaviors
  141. private _behaviors = new Array<Behavior<Node>>();
  142. /**
  143. * Attach a behavior to the node
  144. * @see http://doc.babylonjs.com/features/behaviour
  145. * @param behavior defines the behavior to attach
  146. * @returns the current Node
  147. */
  148. public addBehavior(behavior: Behavior<Node>): Node {
  149. var index = this._behaviors.indexOf(behavior);
  150. if (index !== -1) {
  151. return this;
  152. }
  153. behavior.init();
  154. if (this._scene.isLoading) {
  155. // We defer the attach when the scene will be loaded
  156. var observer = this._scene.onDataLoadedObservable.add(() => {
  157. behavior.attach(this);
  158. setTimeout(() => {
  159. // Need to use a timeout to avoid removing an observer while iterating the list of observers
  160. this._scene.onDataLoadedObservable.remove(observer);
  161. }, 0);
  162. });
  163. } else {
  164. behavior.attach(this);
  165. }
  166. this._behaviors.push(behavior);
  167. return this;
  168. }
  169. /**
  170. * Remove an attached behavior
  171. * @see http://doc.babylonjs.com/features/behaviour
  172. * @param behavior defines the behavior to attach
  173. * @returns the current Node
  174. */
  175. public removeBehavior(behavior: Behavior<Node>): Node {
  176. var index = this._behaviors.indexOf(behavior);
  177. if (index === -1) {
  178. return this;
  179. }
  180. this._behaviors[index].detach();
  181. this._behaviors.splice(index, 1);
  182. return this;
  183. }
  184. /**
  185. * Gets the list of attached behaviors
  186. * @see http://doc.babylonjs.com/features/behaviour
  187. */
  188. public get behaviors(): Behavior<Node>[] {
  189. return this._behaviors;
  190. }
  191. /**
  192. * Gets an attached behavior by name
  193. * @param name defines the name of the behavior to look for
  194. * @see http://doc.babylonjs.com/features/behaviour
  195. * @returns null if behavior was not found else the requested behavior
  196. */
  197. public getBehaviorByName(name: string): Nullable<Behavior<Node>> {
  198. for (var behavior of this._behaviors) {
  199. if (behavior.name === name) {
  200. return behavior;
  201. }
  202. }
  203. return null;
  204. }
  205. /**
  206. * Returns the world matrix of the node
  207. * @returns a matrix containing the node's world matrix
  208. */
  209. public getWorldMatrix(): Matrix {
  210. return Matrix.Identity();
  211. }
  212. // override it in derived class if you add new variables to the cache
  213. // and call the parent class method
  214. /** @ignore */
  215. public _initCache() {
  216. this._cache = {};
  217. this._cache.parent = undefined;
  218. }
  219. /** @ignore */
  220. public updateCache(force?: boolean): void {
  221. if (!force && this.isSynchronized())
  222. return;
  223. this._cache.parent = this.parent;
  224. this._updateCache();
  225. }
  226. // override it in derived class if you add new variables to the cache
  227. // and call the parent class method if !ignoreParentClass
  228. /** @ignore */
  229. public _updateCache(ignoreParentClass?: boolean): void {
  230. }
  231. // override it in derived class if you add new variables to the cache
  232. /** @ignore */
  233. public _isSynchronized(): boolean {
  234. return true;
  235. }
  236. /** @ignore */
  237. public _markSyncedWithParent() {
  238. if (this.parent) {
  239. this._parentRenderId = this.parent._currentRenderId;
  240. }
  241. }
  242. /** @ignore */
  243. public isSynchronizedWithParent(): boolean {
  244. if (!this.parent) {
  245. return true;
  246. }
  247. if (this._parentRenderId !== this.parent._currentRenderId) {
  248. return false;
  249. }
  250. return this.parent.isSynchronized();
  251. }
  252. /** @ignore */
  253. public isSynchronized(updateCache?: boolean): boolean {
  254. var check = this.hasNewParent();
  255. check = check || !this.isSynchronizedWithParent();
  256. check = check || !this._isSynchronized();
  257. if (updateCache)
  258. this.updateCache(true);
  259. return !check;
  260. }
  261. /** @ignore */
  262. public hasNewParent(update?: boolean): boolean {
  263. if (this._cache.parent === this.parent)
  264. return false;
  265. if (update)
  266. this._cache.parent = this.parent;
  267. return true;
  268. }
  269. /**
  270. * Is this node ready to be used/rendered
  271. * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
  272. * @return true if the node is ready
  273. */
  274. public isReady(completeCheck = false): boolean {
  275. return this._isReady;
  276. }
  277. /**
  278. * Is this node enabled?
  279. * 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
  280. * @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
  281. * @return whether this node (and its parent) is enabled
  282. * @see setEnabled
  283. */
  284. public isEnabled(checkAncestors: boolean = true): boolean {
  285. if (checkAncestors === false) {
  286. return this._isEnabled;
  287. }
  288. if (this._isEnabled === false) {
  289. return false;
  290. }
  291. if (this.parent !== undefined && this.parent !== null) {
  292. return this.parent.isEnabled(checkAncestors);
  293. }
  294. return true;
  295. }
  296. /**
  297. * Set the enabled state of this node
  298. * @param value defines the new enabled state
  299. * @see isEnabled
  300. */
  301. public setEnabled(value: boolean): void {
  302. this._isEnabled = value;
  303. }
  304. /**
  305. * Is this node a descendant of the given node?
  306. * The function will iterate up the hierarchy until the ancestor was found or no more parents defined
  307. * @param ancestor defines the parent node to inspect
  308. * @see parent
  309. * @returns a boolean indicating if this node is a descendant of the given node
  310. */
  311. public isDescendantOf(ancestor: Node): boolean {
  312. if (this.parent) {
  313. if (this.parent === ancestor) {
  314. return true;
  315. }
  316. return this.parent.isDescendantOf(ancestor);
  317. }
  318. return false;
  319. }
  320. /** @ignore */
  321. public _getDescendants(results: Node[], directDescendantsOnly: boolean = false, predicate?: (node: Node) => boolean): void {
  322. if (!this._children) {
  323. return;
  324. }
  325. for (var index = 0; index < this._children.length; index++) {
  326. var item = this._children[index];
  327. if (!predicate || predicate(item)) {
  328. results.push(item);
  329. }
  330. if (!directDescendantsOnly) {
  331. item._getDescendants(results, false, predicate);
  332. }
  333. }
  334. }
  335. /**
  336. * Will return all nodes that have this node as ascendant
  337. * @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
  338. * @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
  339. * @return all children nodes of all types
  340. */
  341. public getDescendants(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): Node[] {
  342. var results = new Array<Node>();
  343. this._getDescendants(results, directDescendantsOnly, predicate);
  344. return results;
  345. }
  346. /**
  347. * Get all child-meshes of this node
  348. * @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
  349. * @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
  350. * @returns an array of {BABYLON.AbstractMesh}
  351. */
  352. public getChildMeshes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): AbstractMesh[] {
  353. var results: Array<AbstractMesh> = [];
  354. this._getDescendants(results, directDescendantsOnly, (node: Node) => {
  355. return ((!predicate || predicate(node)) && (node instanceof AbstractMesh));
  356. });
  357. return results;
  358. }
  359. /**
  360. * Get all child-transformNodes of this node
  361. * @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
  362. * @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
  363. * @returns an array of {BABYLON.TransformNode}
  364. */
  365. public getChildTransformNodes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): TransformNode[] {
  366. var results: Array<TransformNode> = [];
  367. this._getDescendants(results, directDescendantsOnly, (node: Node) => {
  368. return ((!predicate || predicate(node)) && (node instanceof TransformNode));
  369. });
  370. return results;
  371. }
  372. /**
  373. * Get all direct children of this node
  374. * @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
  375. * @returns an array of {BABYLON.Node}
  376. */
  377. public getChildren(predicate?: (node: Node) => boolean): Node[] {
  378. return this.getDescendants(true, predicate);
  379. }
  380. /** @ignore */
  381. public _setReady(state: boolean): void {
  382. if (state === this._isReady) {
  383. return;
  384. }
  385. if (!state) {
  386. this._isReady = false;
  387. return;
  388. }
  389. this._isReady = true;
  390. if (this.onReady) {
  391. this.onReady(this);
  392. }
  393. }
  394. /**
  395. * Get an animation by name
  396. * @param name defines the name of the animation to look for
  397. * @returns null if not found else the requested animation
  398. */
  399. public getAnimationByName(name: string): Nullable<Animation> {
  400. for (var i = 0; i < this.animations.length; i++) {
  401. var animation = this.animations[i];
  402. if (animation.name === name) {
  403. return animation;
  404. }
  405. }
  406. return null;
  407. }
  408. /**
  409. * Creates an animation range for this node
  410. * @param name defines the name of the range
  411. * @param from defines the starting key
  412. * @param to defines the end key
  413. */
  414. public createAnimationRange(name: string, from: number, to: number): void {
  415. // check name not already in use
  416. if (!this._ranges[name]) {
  417. this._ranges[name] = new AnimationRange(name, from, to);
  418. for (var i = 0, nAnimations = this.animations.length; i < nAnimations; i++) {
  419. if (this.animations[i]) {
  420. this.animations[i].createRange(name, from, to);
  421. }
  422. }
  423. }
  424. }
  425. /**
  426. * Delete a specific animation range
  427. * @param name defines the name of the range to delete
  428. * @param deleteFrames defines if animation frames from the range must be deleted as well
  429. */
  430. public deleteAnimationRange(name: string, deleteFrames = true): void {
  431. for (var i = 0, nAnimations = this.animations.length; i < nAnimations; i++) {
  432. if (this.animations[i]) {
  433. this.animations[i].deleteRange(name, deleteFrames);
  434. }
  435. }
  436. this._ranges[name] = null; // said much faster than 'delete this._range[name]'
  437. }
  438. /**
  439. * Get an animation range by name
  440. * @param name defines the name of the animation range to look for
  441. * @returns null if not found else the requested animation range
  442. */
  443. public getAnimationRange(name: string): Nullable<AnimationRange> {
  444. return this._ranges[name];
  445. }
  446. /**
  447. * Will start the animation sequence
  448. * @param name defines the range frames for animation sequence
  449. * @param loop defines if the animation should loop (false by default)
  450. * @param speedRatio defines the speed factor in which to run the animation (1 by default)
  451. * @param onAnimationEnd defines a function to be executed when the animation ended (undefined by default)
  452. * @returns the object created for this animation. If range does not exist, it will return null
  453. */
  454. public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Nullable<Animatable> {
  455. var range = this.getAnimationRange(name);
  456. if (!range) {
  457. return null;
  458. }
  459. return this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
  460. }
  461. /**
  462. * Serialize animation ranges into a JSON compatible object
  463. * @returns serialization object
  464. */
  465. public serializeAnimationRanges(): any {
  466. var serializationRanges = [];
  467. for (var name in this._ranges) {
  468. var localRange = this._ranges[name];
  469. if (!localRange) {
  470. continue;
  471. }
  472. var range: any = {};
  473. range.name = name;
  474. range.from = localRange.from;
  475. range.to = localRange.to;
  476. serializationRanges.push(range);
  477. }
  478. return serializationRanges;
  479. }
  480. /**
  481. * Computes the world matrix of the node
  482. * @param force defines if the cache version should be invalidated forcing the world matrix to be created from scratch
  483. * @returns the world matrix
  484. */
  485. public computeWorldMatrix(force?: boolean): Matrix {
  486. return Matrix.Identity();
  487. }
  488. /**
  489. * Releases all associated resources
  490. */
  491. public dispose(): void {
  492. this.parent = null;
  493. // Callback
  494. this.onDisposeObservable.notifyObservers(this);
  495. this.onDisposeObservable.clear();
  496. // Behaviors
  497. for (var behavior of this._behaviors) {
  498. behavior.detach();
  499. }
  500. this._behaviors = [];
  501. this._isDisposed = true;
  502. }
  503. /**
  504. * Parse animation range data from a serialization object and store them into a given node
  505. * @param node defines where to store the animation ranges
  506. * @param parsedNode defines the serialization object to read data from
  507. * @param scene defines the hosting scene
  508. */
  509. public static ParseAnimationRanges(node: Node, parsedNode: any, scene: Scene): void {
  510. if (parsedNode.ranges) {
  511. for (var index = 0; index < parsedNode.ranges.length; index++) {
  512. var data = parsedNode.ranges[index];
  513. node.createAnimationRange(data.name, data.from, data.to);
  514. }
  515. }
  516. }
  517. }
  518. }