babylon.node.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. module BABYLON {
  2. /**
  3. * Node is the basic class for all scene objects (Mesh, Light Camera).
  4. */
  5. export class Node {
  6. @serialize()
  7. public name: string;
  8. @serialize()
  9. public id: string;
  10. @serialize()
  11. public uniqueId: number;
  12. @serialize()
  13. public state = "";
  14. @serialize()
  15. public metadata: any = null;
  16. public doNotSerialize = false;
  17. public animations = new Array<Animation>();
  18. private _ranges: { [name: string]: Nullable<AnimationRange> } = {};
  19. public onReady: (node: Node) => void;
  20. private _isEnabled = true;
  21. private _isReady = true;
  22. public _currentRenderId = -1;
  23. private _parentRenderId = -1;
  24. public _waitingParentId: Nullable<string>;
  25. private _scene: Scene;
  26. public _cache: any;
  27. private _parentNode: Nullable<Node>;
  28. private _children: Node[];
  29. public set parent(parent: Nullable<Node>) {
  30. if (this._parentNode === parent) {
  31. return;
  32. }
  33. // Remove self from list of children of parent
  34. if (this._parentNode && this._parentNode._children !== undefined && this._parentNode._children !== null) {
  35. var index = this._parentNode._children.indexOf(this);
  36. if (index !== -1) {
  37. this._parentNode._children.splice(index, 1);
  38. }
  39. }
  40. // Store new parent
  41. this._parentNode = parent;
  42. // Add as child to new parent
  43. if (this._parentNode) {
  44. if (this._parentNode._children === undefined || this._parentNode._children === null) {
  45. this._parentNode._children = new Array<Node>();
  46. }
  47. this._parentNode._children.push(this);
  48. }
  49. }
  50. public get parent(): Nullable<Node> {
  51. return this._parentNode;
  52. }
  53. public getClassName(): string {
  54. return "Node";
  55. }
  56. /**
  57. * An event triggered when the mesh is disposed.
  58. * @type {BABYLON.Observable}
  59. */
  60. public onDisposeObservable = new Observable<Node>();
  61. private _onDisposeObserver: Nullable<Observer<Node>>;
  62. public set onDispose(callback: () => void) {
  63. if (this._onDisposeObserver) {
  64. this.onDisposeObservable.remove(this._onDisposeObserver);
  65. }
  66. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  67. }
  68. /**
  69. * @constructor
  70. * @param {string} name - the name and id to be given to this node
  71. * @param {BABYLON.Scene} the scene this node will be added to
  72. */
  73. constructor(name: string, scene: Nullable<Scene> = null) {
  74. this.name = name;
  75. this.id = name;
  76. this._scene = <Scene>(scene || Engine.LastCreatedScene);
  77. this.uniqueId = this._scene.getUniqueId();
  78. this._initCache();
  79. }
  80. public getScene(): Scene {
  81. return this._scene;
  82. }
  83. public getEngine(): Engine {
  84. return this._scene.getEngine();
  85. }
  86. // Behaviors
  87. private _behaviors = new Array<Behavior<Node>>();
  88. public addBehavior(behavior: Behavior<Node>): Node {
  89. var index = this._behaviors.indexOf(behavior);
  90. if (index !== -1) {
  91. return this;
  92. }
  93. behavior.init();
  94. if (this._scene.isLoading) {
  95. // We defer the attach when the scene will be loaded
  96. var observer = this._scene.onDataLoadedObservable.add(() => {
  97. behavior.attach(this);
  98. setTimeout(() => {
  99. // Need to use a timeout to avoid removing an observer while iterating the list of observers
  100. this._scene.onDataLoadedObservable.remove(observer);
  101. }, 0);
  102. });
  103. } else {
  104. behavior.attach(this);
  105. }
  106. this._behaviors.push(behavior);
  107. return this;
  108. }
  109. public removeBehavior(behavior: Behavior<Node>): Node {
  110. var index = this._behaviors.indexOf(behavior);
  111. if (index === -1) {
  112. return this;
  113. }
  114. this._behaviors[index].detach();
  115. this._behaviors.splice(index, 1);
  116. return this;
  117. }
  118. public get behaviors(): Behavior<Node>[] {
  119. return this._behaviors;
  120. }
  121. public getBehaviorByName(name: string): Nullable<Behavior<Node>> {
  122. for (var behavior of this._behaviors) {
  123. if (behavior.name === name) {
  124. return behavior;
  125. }
  126. }
  127. return null;
  128. }
  129. // override it in derived class
  130. public getWorldMatrix(): Matrix {
  131. return Matrix.Identity();
  132. }
  133. // override it in derived class if you add new variables to the cache
  134. // and call the parent class method
  135. public _initCache() {
  136. this._cache = {};
  137. this._cache.parent = undefined;
  138. }
  139. public updateCache(force?: boolean): void {
  140. if (!force && this.isSynchronized())
  141. return;
  142. this._cache.parent = this.parent;
  143. this._updateCache();
  144. }
  145. // override it in derived class if you add new variables to the cache
  146. // and call the parent class method if !ignoreParentClass
  147. public _updateCache(ignoreParentClass?: boolean): void {
  148. }
  149. // override it in derived class if you add new variables to the cache
  150. public _isSynchronized(): boolean {
  151. return true;
  152. }
  153. public _markSyncedWithParent() {
  154. if (this.parent) {
  155. this._parentRenderId = this.parent._currentRenderId;
  156. }
  157. }
  158. public isSynchronizedWithParent(): boolean {
  159. if (!this.parent) {
  160. return true;
  161. }
  162. if (this._parentRenderId !== this.parent._currentRenderId) {
  163. return false;
  164. }
  165. return this.parent.isSynchronized();
  166. }
  167. public isSynchronized(updateCache?: boolean): boolean {
  168. var check = this.hasNewParent();
  169. check = check || !this.isSynchronizedWithParent();
  170. check = check || !this._isSynchronized();
  171. if (updateCache)
  172. this.updateCache(true);
  173. return !check;
  174. }
  175. public hasNewParent(update?: boolean): boolean {
  176. if (this._cache.parent === this.parent)
  177. return false;
  178. if (update)
  179. this._cache.parent = this.parent;
  180. return true;
  181. }
  182. /**
  183. * Is this node ready to be used/rendered
  184. * @return {boolean} is it ready
  185. */
  186. public isReady(): boolean {
  187. return this._isReady;
  188. }
  189. /**
  190. * Is this node enabled.
  191. * 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.
  192. * @param {boolean} [checkAncestors=true] - 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.
  193. * @return {boolean} whether this node (and its parent) is enabled.
  194. * @see setEnabled
  195. */
  196. public isEnabled(checkAncestors: boolean = true): boolean {
  197. if (checkAncestors === false) {
  198. return this._isEnabled;
  199. }
  200. if (this._isEnabled === false) {
  201. return false;
  202. }
  203. if (this.parent !== undefined && this.parent !== null) {
  204. return this.parent.isEnabled(checkAncestors);
  205. }
  206. return true;
  207. }
  208. /**
  209. * Set the enabled state of this node.
  210. * @param {boolean} value - the new enabled state
  211. * @see isEnabled
  212. */
  213. public setEnabled(value: boolean): void {
  214. this._isEnabled = value;
  215. }
  216. /**
  217. * Is this node a descendant of the given node.
  218. * The function will iterate up the hierarchy until the ancestor was found or no more parents defined.
  219. * @param {BABYLON.Node} ancestor - The parent node to inspect
  220. * @see parent
  221. */
  222. public isDescendantOf(ancestor: Node): boolean {
  223. if (this.parent) {
  224. if (this.parent === ancestor) {
  225. return true;
  226. }
  227. return this.parent.isDescendantOf(ancestor);
  228. }
  229. return false;
  230. }
  231. /**
  232. * Evaluate the list of children and determine if they should be considered as descendants considering the given criterias
  233. * @param {BABYLON.Node[]} results the result array containing the nodes matching the given criterias
  234. * @param {boolean} directDescendantsOnly 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.
  235. * @param predicate: an optional predicate that will be called on every evaluated children, the predicate must return true for a given child to be part of the result, otherwise it will be ignored.
  236. */
  237. public _getDescendants(results: Node[], directDescendantsOnly: boolean = false, predicate?: (node: Node) => boolean): void {
  238. if (!this._children) {
  239. return;
  240. }
  241. for (var index = 0; index < this._children.length; index++) {
  242. var item = this._children[index];
  243. if (!predicate || predicate(item)) {
  244. results.push(item);
  245. }
  246. if (!directDescendantsOnly) {
  247. item._getDescendants(results, false, predicate);
  248. }
  249. }
  250. }
  251. /**
  252. * Will return all nodes that have this node as ascendant.
  253. * @param {boolean} directDescendantsOnly 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.
  254. * @param predicate: an optional predicate that will be called on every evaluated children, the predicate must return true for a given child to be part of the result, otherwise it will be ignored.
  255. * @return {BABYLON.Node[]} all children nodes of all types.
  256. */
  257. public getDescendants(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): Node[] {
  258. var results = new Array<Node>();
  259. this._getDescendants(results, directDescendantsOnly, predicate);
  260. return results;
  261. }
  262. /**
  263. * Get all child-meshes of this node.
  264. */
  265. public getChildMeshes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): AbstractMesh[] {
  266. var results: Array<AbstractMesh> = [];
  267. this._getDescendants(results, directDescendantsOnly, (node: Node) => {
  268. return ((!predicate || predicate(node)) && (node instanceof AbstractMesh));
  269. });
  270. return results;
  271. }
  272. /**
  273. * Get all child-transformNodes of this node.
  274. */
  275. public getChildTransformNodes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): TransformNode[] {
  276. var results: Array<TransformNode> = [];
  277. this._getDescendants(results, directDescendantsOnly, (node: Node) => {
  278. return ((!predicate || predicate(node)) && (node instanceof TransformNode));
  279. });
  280. return results;
  281. }
  282. /**
  283. * Get all direct children of this node.
  284. */
  285. public getChildren(predicate?: (node: Node) => boolean): Node[] {
  286. return this.getDescendants(true, predicate);
  287. }
  288. public _setReady(state: boolean): void {
  289. if (state === this._isReady) {
  290. return;
  291. }
  292. if (!state) {
  293. this._isReady = false;
  294. return;
  295. }
  296. this._isReady = true;
  297. if (this.onReady) {
  298. this.onReady(this);
  299. }
  300. }
  301. public getAnimationByName(name: string): Nullable<Animation> {
  302. for (var i = 0; i < this.animations.length; i++) {
  303. var animation = this.animations[i];
  304. if (animation.name === name) {
  305. return animation;
  306. }
  307. }
  308. return null;
  309. }
  310. public createAnimationRange(name: string, from: number, to: number): void {
  311. // check name not already in use
  312. if (!this._ranges[name]) {
  313. this._ranges[name] = new AnimationRange(name, from, to);
  314. for (var i = 0, nAnimations = this.animations.length; i < nAnimations; i++) {
  315. if (this.animations[i]) {
  316. this.animations[i].createRange(name, from, to);
  317. }
  318. }
  319. }
  320. }
  321. public deleteAnimationRange(name: string, deleteFrames = true): void {
  322. for (var i = 0, nAnimations = this.animations.length; i < nAnimations; i++) {
  323. if (this.animations[i]) {
  324. this.animations[i].deleteRange(name, deleteFrames);
  325. }
  326. }
  327. this._ranges[name] = null; // said much faster than 'delete this._range[name]'
  328. }
  329. public getAnimationRange(name: string): Nullable<AnimationRange> {
  330. return this._ranges[name];
  331. }
  332. public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): void {
  333. var range = this.getAnimationRange(name);
  334. if (!range) {
  335. return;
  336. }
  337. this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
  338. }
  339. public serializeAnimationRanges(): any {
  340. var serializationRanges = [];
  341. for (var name in this._ranges) {
  342. var localRange = this._ranges[name];
  343. if (!localRange) {
  344. continue;
  345. }
  346. var range: any = {};
  347. range.name = name;
  348. range.from = localRange.from;
  349. range.to = localRange.to;
  350. serializationRanges.push(range);
  351. }
  352. return serializationRanges;
  353. }
  354. // override it in derived class
  355. public computeWorldMatrix(force?: boolean): Matrix {
  356. return Matrix.Identity();
  357. }
  358. public dispose(): void {
  359. this.parent = null;
  360. // Callback
  361. this.onDisposeObservable.notifyObservers(this);
  362. this.onDisposeObservable.clear();
  363. // Behaviors
  364. for (var behavior of this._behaviors) {
  365. behavior.detach();
  366. }
  367. this._behaviors = [];
  368. }
  369. public static ParseAnimationRanges(node: Node, parsedNode: any, scene: Scene): void {
  370. if (parsedNode.ranges) {
  371. for (var index = 0; index < parsedNode.ranges.length; index++) {
  372. var data = parsedNode.ranges[index];
  373. node.createAnimationRange(data.name, data.from, data.to);
  374. }
  375. }
  376. }
  377. }
  378. }