@woosh/meep-engine 2.33.60 → 2.33.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/core/geom/3d/SurfacePoint3.js +44 -5
  2. package/core/geom/Vector2.d.ts +2 -0
  3. package/core/geom/packing/MaxRectangles.js +13 -4
  4. package/core/math/hash/murmur3_32.js +3 -3
  5. package/engine/asset/loaders/material/computeTextureHash.js +7 -13
  6. package/engine/ecs/attachment/AttachmentSystem.js +18 -17
  7. package/engine/ecs/grid/HeightMap2NormalMap.js +1 -1
  8. package/engine/ecs/grid/NormalMap2AOMap.js +1 -1
  9. package/engine/ecs/gui/position/ViewportPositionSystem.js +5 -5
  10. package/engine/ecs/parent/EntityNode.d.ts +20 -0
  11. package/engine/ecs/parent/EntityNode.js +284 -0
  12. package/engine/ecs/parent/FaceEditor.js +137 -0
  13. package/engine/ecs/parent/testCompositionEntityNode.js +153 -0
  14. package/engine/ecs/parent/testHuDPerf.js +123 -0
  15. package/engine/ecs/terrain/ecs/Terrain.js +14 -5
  16. package/engine/graphics/GraphicsEngine.js +1 -2
  17. package/engine/graphics/camera/testClippingPlaneComputation.js +0 -4
  18. package/engine/graphics/ecs/light/LightSystem.js +1 -1
  19. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +29 -0
  20. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +158 -1
  21. package/engine/graphics/ecs/path/testPathDisplaySystem.js +0 -2
  22. package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +55 -11
  23. package/engine/graphics/micron/render/v1/MaterialContext.js +1 -0
  24. package/engine/graphics/particles/particular/engine/simulator/SimulationStepFixedPhysics.js +19 -3
  25. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +0 -2
  26. package/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +0 -2
  27. package/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +0 -2
  28. package/engine/graphics/render/frame_graph/FrameGraphBuilder.js +36 -0
  29. package/engine/graphics/render/frame_graph/IRenderContext.js +8 -0
  30. package/engine/graphics/render/frame_graph/RenderPass.js +32 -0
  31. package/engine/graphics/render/frame_graph/RenderPassFlags.js +6 -0
  32. package/engine/graphics/render/frame_graph/RenderPassResources.js +15 -0
  33. package/engine/graphics/render/frame_graph/Resource.js +21 -0
  34. package/engine/graphics/render/frame_graph/ResourceReference.js +14 -0
  35. package/engine/graphics/render/frame_graph/TextureDescriptor.js +30 -0
  36. package/engine/graphics/render/frame_graph/UsageModeType.js +5 -0
  37. package/engine/graphics/render/frame_graph/sample/deferred/GBufferDrawPass.js +27 -0
  38. package/engine/graphics/render/frame_graph/sample/deferred/PresentPass.js +17 -0
  39. package/engine/graphics/render/visibility/hiz/prototypeHiZ.js +0 -2
  40. package/engine/graphics/render/webgpu/sample/_main.ts +650 -0
  41. package/engine/graphics/render/webgpu/sample/fragmentDeferredRendering.wgsl +67 -0
  42. package/engine/graphics/render/webgpu/sample/fragmentGBuffersDebugView.wgsl +38 -0
  43. package/engine/graphics/render/webgpu/sample/fragmentWriteGBuffers.wgsl +21 -0
  44. package/engine/graphics/render/webgpu/sample/lightUpdate.wgsl +34 -0
  45. package/engine/graphics/render/webgpu/sample/main.js +566 -0
  46. package/engine/graphics/render/webgpu/sample/vertexTextureQuad.wgsl +9 -0
  47. package/engine/graphics/render/webgpu/sample/vertexWriteGBuffers.wgsl +28 -0
  48. package/engine/graphics/shadows/testShadowMapRendering.js +0 -2
  49. package/engine/{ecs/grid → graphics/texture/sampler}/sampler2DToFloat32Texture.js +1 -1
  50. package/package.json +1 -1
  51. package/samples/terrain/main.js +117 -0
  52. package/engine/ecs/sockets/AttachmentSocketsSystem.js +0 -10
@@ -1,5 +1,6 @@
1
1
  import Quaternion from "../Quaternion.js";
2
2
  import Vector3 from "../Vector3.js";
3
+ import { v3_length } from "../v3_length.js";
3
4
 
4
5
  const q = new Quaternion();
5
6
 
@@ -14,14 +15,52 @@ export class SurfacePoint3 {
14
15
 
15
16
  /**
16
17
  *
17
- * @param {THREE.Matrix4} matrix4
18
+ * @param {number[]|mat4|Float32Array} m4
18
19
  */
19
- applyMatrix4_three(matrix4) {
20
- this.position.applyMatrix4_three(matrix4);
20
+ applyMatrix4(m4) {
21
+ // transform position
22
+ const p = this.position;
23
+
24
+ const p_x = p.x;
25
+ const p_y = p.y;
26
+ const p_z = p.z;
27
+
28
+ const result_p_x = m4[0] * p_x + m4[4] * p_y + m4[8] * p_z + m4[12];
29
+ const result_p_y = m4[1] * p_x + m4[5] * p_y + m4[9] * p_z + m4[13];
30
+ const result_p_z = m4[2] * p_x + m4[6] * p_y + m4[10] * p_z + m4[14];
31
+
32
+ this.position.set(
33
+ result_p_x,
34
+ result_p_y,
35
+ result_p_z
36
+ );
37
+
38
+ // transform normal
39
+ const n = this.normal;
40
+
41
+ const n_x = n.x;
42
+ const n_y = n.y;
43
+ const n_z = n.z;
44
+
45
+ const result_n_x = m4[0] * n_x + m4[4] * n_y + m4[8] * n_z;
46
+ const result_n_y = m4[1] * n_x + m4[5] * n_y + m4[9] * n_z;
47
+ const result_n_z = m4[2] * n_x + m4[6] * n_y + m4[10] * n_z;
21
48
 
22
- q.setFromRotationMatrix(matrix4);
49
+ const normal_multiplier = 1 / v3_length(result_n_x, result_n_y, result_n_z);
23
50
 
24
- this.normal.applyQuaternion(q);
51
+ this.normal.set(
52
+ result_n_x * normal_multiplier,
53
+ result_n_y * normal_multiplier,
54
+ result_n_z * normal_multiplier,
55
+ );
56
+ }
57
+
58
+ /**
59
+ *
60
+ * @param {THREE.Matrix4} matrix4
61
+ */
62
+ applyMatrix4_three(matrix4) {
63
+ this.applyMatrix4(matrix4.elements);
25
64
  }
26
65
 
27
66
  /**
@@ -30,6 +30,8 @@ export default class Vector2 {
30
30
 
31
31
  copy(other: Vector2): void
32
32
 
33
+ clone(): Vector2
34
+
33
35
  distanceTo(other: Vector2): number
34
36
 
35
37
  isZero(): boolean
@@ -12,9 +12,10 @@ import Vector2 from "../Vector2.js";
12
12
  * @param {AABB2[]} boxes
13
13
  */
14
14
  function removeRedundantBoxesArray(boxes) {
15
+ let i, j;
15
16
  let numBoxes = boxes.length;
16
17
 
17
- loop_a: for (let i = 0; i < numBoxes; i++) {
18
+ loop_a: for (i = 0; i < numBoxes; i++) {
18
19
  const a = boxes[i];
19
20
 
20
21
  const ax0 = a.x0;
@@ -22,7 +23,7 @@ function removeRedundantBoxesArray(boxes) {
22
23
  const ax1 = a.x1;
23
24
  const ay1 = a.y1;
24
25
 
25
- for (let j = i + 1; j < numBoxes; j++) {
26
+ for (j = i + 1; j < numBoxes; j++) {
26
27
  const b = boxes[j];
27
28
 
28
29
  const bx0 = b.x0;
@@ -157,7 +158,7 @@ function findBestContainer(width, height, boxes, costFunction) {
157
158
  let best = null;
158
159
  let bestScore = Number.POSITIVE_INFINITY;
159
160
 
160
- boxes.traversePreOrder(node => {
161
+ const visitor = (node) => {
161
162
 
162
163
  if (node.getWidth() < width) {
163
164
  //too small, don't traverse deeper
@@ -195,7 +196,11 @@ function findBestContainer(width, height, boxes, costFunction) {
195
196
  best = box;
196
197
  }
197
198
  }
198
- });
199
+
200
+ return true;
201
+ };
202
+
203
+ boxes.traversePreOrder(visitor);
199
204
 
200
205
  return best;
201
206
  }
@@ -337,6 +342,10 @@ export class MaxRectanglesPacker {
337
342
  this.free = new QuadTreeNode(0, 0, width, height);
338
343
  this.free.add(null, 0, 0, width, height);
339
344
 
345
+ /**
346
+ *
347
+ * @type {AABB2[]}
348
+ */
340
349
  this.boxes = [];
341
350
  }
342
351
 
@@ -9,9 +9,9 @@ export function murmur3_32(key, seed) {
9
9
  let k1;
10
10
  let i = 0;
11
11
 
12
- const bytes = key.length;
12
+ const key_length = key.length;
13
13
 
14
- while (i < bytes) {
14
+ while (i < key_length) {
15
15
  k1 = key[i];
16
16
  ++i;
17
17
 
@@ -26,7 +26,7 @@ export function murmur3_32(key, seed) {
26
26
  }
27
27
 
28
28
  // mix in array length
29
- h1 ^= bytes;
29
+ h1 ^= key_length;
30
30
 
31
31
  h1 ^= h1 >>> 16;
32
32
  h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16));
@@ -1,7 +1,7 @@
1
- import { max2 } from "../../../../core/math/max2.js";
2
1
  import { computeImageBitmapHash } from "./computeImageBitmapHash.js";
3
2
  import { computeHashIntegerArray } from "../../../../core/collection/array/computeHashIntegerArray.js";
4
3
  import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
4
+ import { murmur3_32 } from "../../../../core/math/hash/murmur3_32.js";
5
5
 
6
6
  /**
7
7
  *
@@ -59,6 +59,7 @@ export function computeImageDataHash(image) {
59
59
  */
60
60
  function computeSpecificHash(texture) {
61
61
  let dataHash = 0;
62
+ let i;
62
63
 
63
64
  if (texture.image !== null && texture.image !== undefined) {
64
65
 
@@ -73,25 +74,18 @@ function computeSpecificHash(texture) {
73
74
  if (Array.isArray(mipmaps)) {
74
75
  const nMipmaps = mipmaps.length;
75
76
 
76
- for (let i = 0; i < nMipmaps; i++) {
77
+ for (i = 0; i < nMipmaps; i++) {
77
78
  const mipmap = mipmaps[i];
78
79
 
79
- if (mipmap.width > 16) {
80
- continue;
81
- }
82
-
83
80
  const data = mipmap.data;
84
-
85
81
  const dataSize = data.length;
86
82
 
87
- const dataStride = max2(1, Math.floor(dataSize / 31));
88
-
89
- for (let i = 0; i < dataSize; i += dataStride) {
90
- const datum = data[i];
91
-
92
- dataHash = ((dataHash << 5) - dataHash) + datum;
83
+ if (dataSize > 1024) {
84
+ continue;
93
85
  }
94
86
 
87
+ dataHash = ((dataHash << 5) - dataHash) + murmur3_32(data, 1337);
88
+
95
89
  break;
96
90
  }
97
91
 
@@ -6,7 +6,6 @@ import { assert } from "../../../core/assert.js";
6
6
  import Mesh from "../../graphics/ecs/mesh/Mesh.js";
7
7
  import { BoneAttachmentBinding } from "./BoneAttachmentBinding.js";
8
8
  import { TransformAttachmentBinding } from "./TransformAttachmentBinding.js";
9
- import { MeshSystem } from "../../graphics/ecs/mesh/MeshSystem.js";
10
9
 
11
10
  /**
12
11
  * @readonly
@@ -318,10 +317,10 @@ export class AttachmentSystem extends System {
318
317
 
319
318
  /**
320
319
  *
321
- * @param {Mesh} mesh
322
320
  * @param {number} entity
321
+ * @private
323
322
  */
324
- visitVisibleMesh(mesh, entity) {
323
+ __update_entity(entity) {
325
324
  const bindings = this.parentBindingIndex.get(entity);
326
325
 
327
326
  if (bindings !== undefined) {
@@ -335,25 +334,27 @@ export class AttachmentSystem extends System {
335
334
  }
336
335
  }
337
336
 
337
+ /**
338
+ *
339
+ * @param {Mesh} mesh
340
+ * @param {number} entity
341
+ */
342
+ visitComponent(mesh, entity) {
343
+ this.__update_entity(entity);
344
+ }
345
+
338
346
  update(timeDelta) {
339
347
  //process wait queue
340
348
  this.processWaiting();
341
349
 
342
- /**
343
- *
344
- * @type {MeshSystem}
345
- */
346
- const meshSystem = this.entityManager.getSystem(MeshSystem);
350
+ const em = this.entityManager;
347
351
 
348
- meshSystem.traverseVisible(this.visitVisibleMesh, this);
352
+ const ecd = em.dataset;
349
353
 
350
- }
351
- }
354
+ if (ecd === null) {
355
+ return;
356
+ }
352
357
 
353
- /**
354
- *
355
- * @param {AttachmentBinding} binding
356
- */
357
- function updateBinding(binding) {
358
- binding.update();
358
+ ecd.traverseComponents(Attachment, this.visitComponent, this);
359
+ }
359
360
  }
@@ -5,7 +5,7 @@
5
5
  import { Sampler2D } from '../../graphics/texture/sampler/Sampler2D.js';
6
6
  import NormalMapShader from '../../graphics/shaders/NormalMapShader2.js';
7
7
  import ImageFilter from '../../graphics/filter/ImageFilter.js';
8
- import { sampler2DtoFloat32Texture } from "./sampler2DToFloat32Texture.js";
8
+ import { sampler2DtoFloat32Texture } from "../../graphics/texture/sampler/sampler2DToFloat32Texture.js";
9
9
 
10
10
 
11
11
  function convertChannel(v) {
@@ -9,7 +9,7 @@ import sampler2D2Texture from '../../graphics/texture/sampler/Sampler2D2Texture.
9
9
  import AmbientOcclusionShader from '../../graphics/shaders/AmbientOcclusionShader.js';
10
10
  import GaussianBlurShader from '../../graphics/shaders/GaussianBlurShader.js';
11
11
  import ImageFilter from '../../graphics/filter/ImageFilter.js';
12
- import { sampler2DtoFloat32Texture } from "./sampler2DToFloat32Texture.js";
12
+ import { sampler2DtoFloat32Texture } from "../../graphics/texture/sampler/sampler2DToFloat32Texture.js";
13
13
 
14
14
  function filterResult2Texture(data, width, height) {
15
15
  const result = new DataTexture();
@@ -5,7 +5,6 @@ import { System } from '../../System.js';
5
5
  import ViewportPosition from './ViewportPosition.js';
6
6
  import GUIElement from "../GUIElement.js";
7
7
  import { assert } from "../../../../core/assert.js";
8
- import Vector2 from "../../../../core/geom/Vector2.js";
9
8
  import { SignalBinding } from "../../../../core/events/signal/SignalBinding.js";
10
9
  import { EPSILON } from "../../../../core/math/MathUtils.js";
11
10
  import AABB2 from "../../../../core/geom/AABB2.js";
@@ -202,8 +201,9 @@ function setElementPosition(view, bounds, viewportSize, vp) {
202
201
  if (vp.stickToScreenEdge) {
203
202
 
204
203
 
205
- const clipEdgeX = vp.screenEdgeWidth / viewportSize.x;
206
- const clipEdgeY = vp.screenEdgeWidth / viewportSize.y;
204
+ const screenEdgeWidth = vp.screenEdgeWidth;
205
+ const clipEdgeX = screenEdgeWidth / viewportSize.x;
206
+ const clipEdgeY = screenEdgeWidth / viewportSize.y;
207
207
 
208
208
 
209
209
  stickyBounds.set(clipEdgeX, clipEdgeY, 1 - clipEdgeX, 1 - clipEdgeY);
@@ -241,8 +241,8 @@ function setElementPosition(view, bounds, viewportSize, vp) {
241
241
  }
242
242
 
243
243
 
244
- const targetX = (x) * (viewportSize.x);
245
- const targetY = (y) * (viewportSize.y);
244
+ const targetX = x * (viewportSize.x);
245
+ const targetY = y * (viewportSize.y);
246
246
 
247
247
  const viewPosition = view.position;
248
248
 
@@ -0,0 +1,20 @@
1
+ import EntityBuilder from "../EntityBuilder";
2
+ import {EntityComponentDataset} from "../EntityComponentDataset";
3
+ import {Transform} from "../transform/Transform";
4
+
5
+ export class EntityNode {
6
+ constructor(entity?: EntityBuilder)
7
+
8
+ parent: EntityNode | null
9
+ entity: EntityBuilder
10
+
11
+ transform: Transform
12
+
13
+ addChild(node: EntityNode): boolean
14
+
15
+ removeChild(node: EntityNode): boolean
16
+
17
+ build(ecd: EntityComponentDataset): void
18
+
19
+ destroy(): void
20
+ }
@@ -0,0 +1,284 @@
1
+ import { ParentEntity } from "./ParentEntity.js";
2
+ import EntityBuilder from "../EntityBuilder.js";
3
+ import { array_remove_first } from "../../../core/collection/array/array_remove_first.js";
4
+ import { assert } from "../../../core/assert.js";
5
+ import { array_push_if_unique } from "../../../core/collection/array/array_push_if_unique.js";
6
+ import { Attachment } from "../attachment/Attachment.js";
7
+ import { AttachmentSockets } from "../sockets/AttachmentSockets.js";
8
+ import { AttachmentSocket } from "../sockets/AttachmentSocket.js";
9
+ import { Transform } from "../transform/Transform.js";
10
+
11
+ export class EntityNode {
12
+ /**
13
+ *
14
+ * @param {EntityBuilder} [entity] optional entity to be wrapped
15
+ */
16
+ constructor(entity = new EntityBuilder()) {
17
+ /**
18
+ *
19
+ * @type {EntityNode|null}
20
+ * @private
21
+ */
22
+ this.__parent = null;
23
+ /**
24
+ *
25
+ * @type {EntityBuilder}
26
+ * @private
27
+ */
28
+ this.__entity = entity;
29
+ /**
30
+ *
31
+ * @type {EntityNode[]}
32
+ * @private
33
+ */
34
+ this.__children = [];
35
+
36
+ /**
37
+ *
38
+ * @type {Transform}
39
+ * @private
40
+ */
41
+ this.__transform = new Transform();
42
+ }
43
+
44
+ /**
45
+ *
46
+ * @private
47
+ */
48
+ __transform_sync_down() {
49
+
50
+ if (this.__parent === null) {
51
+ const t = this.__entity.getComponent(Transform);
52
+ if (t !== null) {
53
+ t.copy(this.__transform);
54
+ }
55
+ } else {
56
+ this.__safe_get_attachment().transform.copy(this.__transform);
57
+ }
58
+
59
+ }
60
+
61
+ /**
62
+ *
63
+ * @param {*} components
64
+ * @return {EntityNode}
65
+ */
66
+ static fromComponents(...components) {
67
+ const node = new EntityNode();
68
+
69
+ for (let i = 0; i < components.length; i++) {
70
+ const component = components[i];
71
+
72
+ node.entity.add(component);
73
+ }
74
+
75
+ return node;
76
+ }
77
+
78
+ /**
79
+ *
80
+ * @return {Attachment}
81
+ * @private
82
+ */
83
+ __safe_get_attachment() {
84
+ let attachment = this.__entity.getComponent(Attachment);
85
+
86
+ if (attachment === null) {
87
+ attachment = new Attachment();
88
+ attachment.socket = "root";
89
+ attachment.immediate = true;
90
+
91
+ this.__entity.add(attachment);
92
+ }
93
+
94
+ return attachment;
95
+ }
96
+
97
+ /**
98
+ * @returns {Transform}
99
+ */
100
+ get transform() {
101
+ return this.__transform;
102
+ }
103
+
104
+ /**
105
+ *
106
+ * @return {EntityNode|null}
107
+ */
108
+ get parent() {
109
+ return this.__parent;
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @param {EntityNode|null} node
115
+ */
116
+ set parent(node) {
117
+ // check if entity is built
118
+ if (this.__entity.isBuilt) {
119
+ // live re-wiring
120
+ let parent_entity = this.__entity.removeComponent(ParentEntity);
121
+ let attachment = this.__entity.removeComponent(Attachment);
122
+
123
+ if (node !== null) {
124
+
125
+ // handle component ParentEntity
126
+
127
+ if (parent_entity === null) {
128
+ parent_entity = new ParentEntity();
129
+ }
130
+
131
+ const parent_entity_builder = node.entity;
132
+
133
+ if (!parent_entity_builder.isBuilt) {
134
+ throw new Error('Parent entity is not built');
135
+ }
136
+
137
+ const parent_entity_id = parent_entity_builder.entity;
138
+ parent_entity.entity = parent_entity_id;
139
+
140
+ // add component back
141
+ this.__entity.add(parent_entity);
142
+
143
+
144
+ // handle component Attachment
145
+ if (attachment === null) {
146
+ attachment = this.__safe_get_attachment();
147
+ }
148
+
149
+ attachment.parent = parent_entity_id;
150
+ }
151
+ }
152
+
153
+ this.__parent = node;
154
+ }
155
+
156
+ get entity() {
157
+ return this.__entity;
158
+ }
159
+
160
+ /**
161
+ *
162
+ * @param {EntityNode} node
163
+ * @returns {boolean}
164
+ */
165
+ addChild(node) {
166
+ const added = array_push_if_unique(this.__children, node);
167
+
168
+ if (added) {
169
+ if (node.parent !== null) {
170
+ // has a parent already, detach
171
+ node.parent.removeChild(node);
172
+ }
173
+
174
+ node.parent = this;
175
+ }
176
+
177
+ return added;
178
+ }
179
+
180
+ /**
181
+ *
182
+ * @param {EntityNode} node
183
+ * @returns {boolean}
184
+ */
185
+ removeChild(node) {
186
+ const removed = array_remove_first(this.__children, node);
187
+
188
+ if (removed) {
189
+ assert.equal(node.parent, this, 'node is a child, but parent property points to something else instead of this node');
190
+
191
+ node.parent = null;
192
+ }
193
+
194
+ return removed;
195
+ }
196
+
197
+ /**
198
+ *
199
+ * @param {EntityComponentDataset} ecd
200
+ */
201
+ build(ecd) {
202
+
203
+ // assume that parent is built
204
+ const parent = this.__parent;
205
+
206
+ if (parent !== null) {
207
+ /**
208
+ *
209
+ * @type {ParentEntity}
210
+ */
211
+ let parent_entity = this.__entity.getComponent(ParentEntity);
212
+ if (parent_entity === null) {
213
+ parent_entity = new ParentEntity();
214
+ this.__entity.add(parent_entity);
215
+ }
216
+
217
+ const parent_entity_builder = parent.__entity;
218
+
219
+ if (!parent_entity_builder.isBuilt) {
220
+ throw new Error('Parent entity is not built');
221
+ }
222
+
223
+ const parent_entity_id = parent_entity_builder.entity;
224
+ parent_entity.entity = parent_entity_id;
225
+
226
+
227
+ const attachment = this.__safe_get_attachment();
228
+
229
+ attachment.parent = parent_entity_id;
230
+ }
231
+
232
+ // ensure "root" socket exists for transform parenting
233
+ /**
234
+ *
235
+ * @type {AttachmentSockets}
236
+ */
237
+ let sockets = this.__entity.getComponent(AttachmentSockets);
238
+ if (sockets === null) {
239
+ sockets = new AttachmentSockets();
240
+ this.__entity.add(sockets);
241
+ }
242
+
243
+ if (sockets.get("root") === undefined) {
244
+ const socket = new AttachmentSocket();
245
+ socket.id = "root";
246
+
247
+ sockets.add(socket);
248
+ }
249
+
250
+ this.__transform_sync_down();
251
+ this.__transform.position.onChanged.add(this.__transform_sync_down, this);
252
+ this.__transform.scale.onChanged.add(this.__transform_sync_down, this);
253
+ this.__transform.rotation.onChanged.add(this.__transform_sync_down, this);
254
+
255
+ this.__entity.build(ecd);
256
+
257
+ // build children
258
+ const children = this.__children;
259
+ const child_count = children.length;
260
+ for (let i = 0; i < child_count; i++) {
261
+ const child = children[i];
262
+
263
+ child.build(ecd);
264
+ }
265
+ }
266
+
267
+ destroy() {
268
+ this.__transform.position.onChanged.remove(this.__transform_sync_down, this);
269
+ this.__transform.scale.onChanged.remove(this.__transform_sync_down, this);
270
+ this.__transform.rotation.onChanged.remove(this.__transform_sync_down, this);
271
+
272
+ // destroy children first
273
+ const children = this.__children;
274
+ const child_count = children.length;
275
+ for (let i = 0; i < child_count; i++) {
276
+ const child = children[i];
277
+
278
+ child.destroy();
279
+ }
280
+
281
+ // then main entity
282
+ this.__entity.destroy();
283
+ }
284
+ }