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