@woosh/meep-engine 2.34.32 → 2.34.36

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 (56) hide show
  1. package/core/assert.js +11 -0
  2. package/core/binary/BitSet.js +5 -0
  3. package/core/bvh2/BinaryNode.js +210 -74
  4. package/core/bvh2/Node.d.ts +3 -0
  5. package/core/bvh2/aabb3/AABB3.js +50 -4
  6. package/core/bvh2/aabb3/AABB3Math.js +1 -1
  7. package/core/bvh2/aabb3/aabb3_from_v3_array.js +34 -0
  8. package/core/bvh2/aabb3/aabb3_from_v3_array_transformed.js +39 -0
  9. package/core/bvh2/aabb3/aabb3_intersects_frustum_degree.js +46 -0
  10. package/core/bvh2/transform/RotationOptimizer.js +2 -262
  11. package/core/bvh2/transform/tryRotateSingleNode.js +259 -0
  12. package/core/bvh2/traversal/ThreeFrustumsIntersectionBVHVisitor.js +16 -35
  13. package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +52 -0
  14. package/core/collection/list/List.js +0 -34
  15. package/core/events/signal/Signal.js +1 -1
  16. package/core/geom/3d/aabb/aabb3_matrix4_project.js +2 -3
  17. package/core/geom/Quaternion.d.ts +2 -0
  18. package/engine/Engine.js +5 -0
  19. package/engine/asset/loaders/ArrayBufferLoader.js +7 -0
  20. package/engine/asset/loaders/image/ImageRGBADataLoader.js +11 -6
  21. package/engine/development/performance/AbstractMetric.js +4 -0
  22. package/engine/development/performance/RingBufferMetric.js +4 -0
  23. package/engine/ecs/EntityBuilder.js +12 -5
  24. package/engine/ecs/EntityComponentDataset.js +26 -6
  25. package/engine/ecs/attachment/AttachmentSystem.js +1 -1
  26. package/engine/ecs/attachment/TransformAttachmentBinding.js +0 -1
  27. package/engine/ecs/foliage/FoliageVisibilitySetBuilder.js +1 -31
  28. package/engine/ecs/foliage/InstancedFoliage.js +81 -152
  29. package/engine/ecs/foliage/ViewState.js +190 -0
  30. package/engine/ecs/foliage/ecs/Foliage2System.js +80 -12
  31. package/engine/ecs/foliage/ecs/InstancedMeshComponent.js +30 -0
  32. package/engine/ecs/foliage/ecs/InstancedMeshLayer.js +16 -0
  33. package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +2 -2
  34. package/engine/ecs/parent/EntityNode.js +31 -49
  35. package/engine/ecs/sockets/AttachmentSockets.js +11 -1
  36. package/engine/ecs/transform-attachment/TransformAttachment.js +88 -0
  37. package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +59 -0
  38. package/engine/graphics/GraphicsEngine.js +23 -2
  39. package/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +2 -19
  40. package/engine/graphics/ecs/mesh/updateNodeByTransformAndBBB.js +21 -21
  41. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +9 -1
  42. package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +4 -1
  43. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +3 -0
  44. package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +40 -8
  45. package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +3 -3
  46. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +17 -11
  47. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -34
  48. package/engine/graphics/micron/build/fill_patch_geometry_data.js +19 -6
  49. package/engine/graphics/micron/format/MicronGeometry.js +21 -0
  50. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -3
  51. package/engine/graphics/micron/prototypeVirtualGeometry.js +69 -1
  52. package/engine/graphics/render/layers/RenderLayer.js +22 -85
  53. package/engine/graphics/render/visibility/VisibilityComputer.js +23 -103
  54. package/engine/sound/ecs/emitter/SoundEmitterSystem.js +1 -1
  55. package/package.json +1 -1
  56. package/samples/terrain/editor.js +0 -1
@@ -1,23 +1,19 @@
1
1
  import { BinaryNode } from "../../../core/bvh2/BinaryNode.js";
2
2
  import { RowFirstTable } from "../../../core/collection/table/RowFirstTable.js";
3
- import { InstancedMeshGroup } from "../../graphics/geometry/instancing/InstancedMeshGroup.js";
4
- import Vector3 from "../../../core/geom/Vector3.js";
5
3
  import { LeafNode } from "../../../core/bvh2/LeafNode.js";
6
4
  import Vector4 from "../../../core/geom/Vector4.js";
7
5
 
8
6
  import { BufferGeometry } from 'three';
9
7
  import { DataType } from "../../../core/collection/table/DataType.js";
10
8
  import { RowFirstTableSpec } from "../../../core/collection/table/RowFirstTableSpec.js";
11
- import Quaternion from "../../../core/geom/Quaternion.js";
12
- import { ThreeFrustumsIntersectionBVHVisitor } from "../../../core/bvh2/traversal/ThreeFrustumsIntersectionBVHVisitor.js";
13
- import { FoliageVisibilitySetBuilder } from "./FoliageVisibilitySetBuilder.js";
14
- import { traverseBinaryNodeUsingVisitor } from "../../../core/bvh2/traversal/traverseBinaryNodeUsingVisitor.js";
15
9
  import { serializeBinaryNodeToBinaryBuffer } from "../../../core/bvh2/serialization/serializeBinaryNodeToBinaryBuffer.js";
16
10
  import { deserializeBinaryNodeFromBinaryBuffer } from "../../../core/bvh2/serialization/deserializeBinaryNodeFromBinaryBuffer.js";
17
11
  import { computeGeometryBoundingSphereMiniball } from "../../graphics/geometry/buffered/computeGeometryBoundingSphereMiniball.js";
18
12
  import { serializeRowFirstTable } from "../../../core/collection/table/serializeRowFirstTable.js";
19
13
  import { deserializeRowFirstTable } from "../../../core/collection/table/deserializeRowFirstTable.js";
20
- import { assert } from "../../../core/assert.js";
14
+ import { compose_matrix4_array } from "../../graphics/util/composeMatrix4.js";
15
+ import { aabb3_from_v3_array_transformed } from "../../../core/bvh2/aabb3/aabb3_from_v3_array_transformed.js";
16
+ import { ViewState } from "./ViewState.js";
21
17
 
22
18
  /**
23
19
  * @readonly
@@ -57,15 +53,16 @@ export class InstancedFoliage {
57
53
 
58
54
  /**
59
55
  *
60
- * @type {InstancedMeshGroup}
56
+ * @type {THREE.BufferGeometry|null}
61
57
  */
62
- this.instances = new InstancedMeshGroup();
58
+ this.geometry = null;
63
59
 
64
60
  /**
65
61
  *
66
- * @type {THREE.BufferGeometry|null}
62
+ * @type {THREE.Material|null}
63
+ * @private
67
64
  */
68
- this.geometry = null;
65
+ this.__material = null;
69
66
 
70
67
  /**
71
68
  *
@@ -73,32 +70,39 @@ export class InstancedFoliage {
73
70
  */
74
71
  this.instanceBoundingSphere = new Vector4(0, 0, 0, 0);
75
72
 
76
-
77
73
  /**
78
74
  *
79
- * @type {ThreeFrustumsIntersectionBVHVisitor}
75
+ * @type {Map<CameraView, ViewState>}
76
+ * @private
80
77
  */
81
- this.bvhVisibilityVisitor = new ThreeFrustumsIntersectionBVHVisitor();
78
+ this.__views = new Map();
82
79
 
83
- /**
84
- *
85
- * @type {FoliageVisibilitySetBuilder}
86
- */
87
- this.visibilitySetBuilder = new FoliageVisibilitySetBuilder();
88
- this.bvhVisibilityVisitor.collector = this.visibilitySetBuilder;
80
+ this.__cast_shadow = true;
81
+ this.__receive_shadow = true;
82
+ }
89
83
 
90
- /**
91
- *
92
- * @type {*[]}
93
- * @private
94
- */
95
- this.__garbage = [];
96
- /**
97
- *
98
- * @type {number}
99
- * @private
100
- */
101
- this.__garbagePointer = 0;
84
+ set castShadow(v) {
85
+ this.__cast_shadow = v;
86
+
87
+ for (const [view, state] of this.__views) {
88
+ state.instances.mesh.castShadow = v;
89
+ }
90
+ }
91
+
92
+ get castShadow() {
93
+ return this.__cast_shadow;
94
+ }
95
+
96
+ set receiveShadow(v) {
97
+ this.__receive_shadow = v;
98
+
99
+ for (const [view, state] of this.__views) {
100
+ state.instances.mesh.receiveShadow = v;
101
+ }
102
+ }
103
+
104
+ get receiveShadow() {
105
+ return this.__receive_shadow;
102
106
  }
103
107
 
104
108
  /**
@@ -136,7 +140,11 @@ export class InstancedFoliage {
136
140
  }
137
141
 
138
142
  dispose() {
139
- this.instances.dispose();
143
+ for (const [view, state] of this.__views) {
144
+ state.dispose();
145
+ }
146
+
147
+ this.__views.clear();
140
148
  }
141
149
 
142
150
  initialize() {
@@ -168,7 +176,7 @@ export class InstancedFoliage {
168
176
  );
169
177
 
170
178
  //compute bounding box for this instance
171
- this.expandBoundingBoxForInstance(position, rotation, scale, leaf);
179
+ this.bomputeInstanceBoundsTight(position, rotation, scale, leaf);
172
180
 
173
181
  //insert bounding box into the spatial index
174
182
  this.bvh.insertNode(leaf);
@@ -210,42 +218,15 @@ export class InstancedFoliage {
210
218
  * @param {Quaternion} rotation
211
219
  * @param {Vector3} scale
212
220
  */
213
- expandBoundingBoxForInstance(position, rotation, scale, result) {
221
+ bomputeInstanceBoundsTight(position, rotation, scale, result) {
214
222
 
215
223
  const vertexData = this.geometry.attributes.position.array;
216
224
 
217
- const rXYZ = new Vector3(rotation.x, rotation.y, rotation.z);
218
-
219
- const v = new Vector3(0, 0, 0);
220
-
221
- const c0 = new Vector3(0, 0, 0);
222
- const c1 = new Vector3(0, 0, 0);
223
- const c2 = new Vector3(0, 0, 0);
224
-
225
- for (let i = 0, l = vertexData.length; i < l; i += 3) {
226
- const x = vertexData[i];
227
- const y = vertexData[i + 1];
228
- const z = vertexData[i + 2];
229
-
230
- v.set(x, y, z);
231
-
232
- //transform point
225
+ const transform = new Float32Array(16);
233
226
 
234
- //apply scale
235
- v.multiply(scale);
227
+ compose_matrix4_array(transform, position, rotation, scale);
236
228
 
237
- //apply rotation
238
- c1.copy(v).multiplyScalar(rotation.w);
239
- c0.copy(rXYZ).cross(v).add(c1);
240
- c2.copy(rXYZ).cross(c0).multiplyScalar(2);
241
-
242
- v.add(c2);
243
-
244
- //apply translation
245
- v.add(position);
246
-
247
- result._expandToFitPoint(v.x, v.y, v.z);
248
- }
229
+ aabb3_from_v3_array_transformed(result, vertexData, vertexData.length, transform);
249
230
  }
250
231
 
251
232
  computeInstanceBoundingSphere() {
@@ -277,9 +258,12 @@ export class InstancedFoliage {
277
258
 
278
259
 
279
260
  this.geometry = bufferGeometry;
261
+ this.__material = material;
280
262
 
281
- this.instances.setMaterial(material);
282
- this.instances.setGeometry(bufferGeometry);
263
+ for (const [view, state] of this.__views) {
264
+ state.instances.setMaterial(material);
265
+ state.instances.setGeometry(bufferGeometry);
266
+ }
283
267
 
284
268
  this.computeInstanceBoundingSphere();
285
269
 
@@ -318,108 +302,56 @@ export class InstancedFoliage {
318
302
 
319
303
  /**
320
304
  *
321
- * @param index
322
- * @param {int} ref
305
+ * @param {CameraView} view
323
306
  */
324
- __updateVisibility(index, ref) {
325
- assert.isNonNegativeInteger(ref, 'ref');
326
-
327
- /**
328
- *
329
- * @type {FoliageVisibilitySetBuilder}
330
- */
331
- const builder = this.visibilitySetBuilder;
307
+ add_view(view) {
332
308
 
333
- /**
334
- *
335
- * @type {BitSet}
336
- */
337
- const visibleElementSet = builder.visibleSet;
338
-
339
- if (!visibleElementSet.get(ref)) {
340
- //no longer visible
341
- this.__garbage[this.__garbagePointer++] = ref;
342
- } else {
343
- //visible, and is already in the group, update the set
344
- visibleElementSet.set(ref, false);
309
+ if (this.__views.has(view)) {
310
+ throw new Error('View already registered');
345
311
  }
346
- }
347
-
348
- __removeGarbage() {
349
312
 
350
- const garbagePointer = this.__garbagePointer;
351
- const instances = this.instances;
313
+ const state = new ViewState();
352
314
 
315
+ state.__view = view;
353
316
 
354
- const garbage = this.__garbage;
317
+ state.instances.setGeometry(this.geometry);
318
+ state.instances.setMaterial(this.__material);
355
319
 
356
- for (let i = 0; i < garbagePointer; i++) {
357
- const ref = garbage[i];
358
- instances.remove(ref);
359
- }
320
+ state.instances.mesh.castShadow = this.castShadow;
321
+ state.instances.mesh.receiveShadow = this.receiveShadow;
360
322
 
323
+ this.__views.set(view, state);
361
324
  }
362
325
 
363
326
  /**
364
327
  *
365
- * @param {Frustum[]} frustums
366
- * @param {function[]} [visibilityFilters]
328
+ * @param {CameraView} view
367
329
  */
368
- update(frustums, visibilityFilters = []) {
369
- const instances = this.instances;
370
-
371
- this.bvhVisibilityVisitor.setFrustums(frustums);
330
+ remove_view(view) {
331
+ const state = this.__views.get(view);
372
332
 
373
- const visibilitySetBuilder = this.visibilitySetBuilder;
374
- visibilitySetBuilder.setFilters(visibilityFilters);
375
- visibilitySetBuilder.initialize();
376
-
377
- const visibleElementSet = visibilitySetBuilder.visibleSet;
378
-
379
- //build visible set
380
- traverseBinaryNodeUsingVisitor(this.bvh, this.bvhVisibilityVisitor);
381
-
382
- visibilitySetBuilder.finalize();
383
-
384
- // console.log(`Visible: `, elementsToAdd.size);
385
-
386
- //reset garbage pointer
387
- this.__garbagePointer = 0;
388
-
389
- //remove those that are no longer visible
390
- instances.traverseReferences(this.__updateVisibility, this);
391
-
392
- //cull instances that are no longer visible
393
- this.__removeGarbage();
394
-
395
-
396
- const data = this.data;
397
-
398
- //process entities that have become newly visible
399
- for (let index = visibleElementSet.nextSetBit(0); index !== -1; index = visibleElementSet.nextSetBit(index + 1)) {
400
- //read transform data for instance
401
- data.getRow(index, rowData);
402
-
403
- const positionX = rowData[0];
404
- const positionY = rowData[1];
405
- const positionZ = rowData[2];
333
+ if (state === undefined) {
334
+ return false;
335
+ }
406
336
 
407
- const encodedRotation = rowData[3];
337
+ state.dispose();
408
338
 
339
+ this.__views.delete(view);
409
340
 
410
- const scaleX = rowData[4];
411
- const scaleY = rowData[5];
412
- const scaleZ = rowData[6];
341
+ return true;
342
+ }
413
343
 
414
- const i = instances.add(index);
344
+ /**
345
+ *
346
+ * @param {CameraView} view
347
+ * @return {ViewState}
348
+ */
349
+ update(view) {
350
+ const state = this.__views.get(view);
415
351
 
416
- tempQuaternion.decodeFromUint32(encodedRotation);
352
+ state.update(this.bvh, this.data);
417
353
 
418
- //apply instance transforms
419
- instances.setPositionAt(i, positionX, positionY, positionZ);
420
- instances.setRotationAt(i, tempQuaternion.x, tempQuaternion.y, tempQuaternion.z, tempQuaternion.w);
421
- instances.setScaleAt(i, scaleX, scaleY, scaleZ);
422
- }
354
+ return state;
423
355
  }
424
356
  }
425
357
 
@@ -442,6 +374,3 @@ function serializeLeafValueUintVar(buffer, value) {
442
374
  }
443
375
 
444
376
 
445
- const tempQuaternion = new Quaternion();
446
- const rowData = [];
447
-
@@ -0,0 +1,190 @@
1
+ import { BitSet } from "../../../core/binary/BitSet.js";
2
+ import { InstancedMeshGroup } from "../../graphics/geometry/instancing/InstancedMeshGroup.js";
3
+ import { assert } from "../../../core/assert.js";
4
+ import { queryBinaryNode_FrustumIntersections_Data } from "../../../core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js";
5
+ import Quaternion from "../../../core/geom/Quaternion.js";
6
+
7
+ /**
8
+ *
9
+ * @type {number[]}
10
+ */
11
+ const scratch_array = [];
12
+
13
+ export class ViewState {
14
+ constructor() {
15
+ /**
16
+ *
17
+ * @type {CameraView|null}
18
+ * @private
19
+ */
20
+ this.__view = null;
21
+
22
+ this.__visible_set = new BitSet();
23
+
24
+ //prevent re-allocation
25
+ this.__visible_set.preventShrink();
26
+
27
+ /**
28
+ *
29
+ * @type {InstancedMeshGroup}
30
+ */
31
+ this.instances = new InstancedMeshGroup();
32
+
33
+ /**
34
+ *
35
+ * @type {number[]}
36
+ * @private
37
+ */
38
+ this.__garbage = [];
39
+ /**
40
+ *
41
+ * @type {number}
42
+ * @private
43
+ */
44
+ this.__garbagePointer = 0;
45
+ }
46
+
47
+ dispose() {
48
+ this.instances.dispose();
49
+ }
50
+
51
+
52
+ /**
53
+ *
54
+ * @param {number} index
55
+ * @param {int} ref
56
+ */
57
+ __updateVisibility(index, ref) {
58
+ assert.isNonNegativeInteger(ref, 'ref');
59
+
60
+ /**
61
+ *
62
+ * @type {BitSet}
63
+ */
64
+ const visibleElementSet = this.__visible_set;
65
+
66
+ if (!visibleElementSet.get(ref)) {
67
+ //no longer visible
68
+ this.__garbage[this.__garbagePointer++] = ref;
69
+ } else {
70
+ //visible, and is already in the group, update the set
71
+ visibleElementSet.set(ref, false);
72
+ }
73
+ }
74
+
75
+ __compute_visibility_update() {
76
+ const instances = this.instances;
77
+
78
+ const indices = instances.indices;
79
+
80
+ const index_count = indices.length;
81
+
82
+ /**
83
+ *
84
+ * @type {BitSet}
85
+ */
86
+ const visible_set = this.__visible_set;
87
+
88
+ const garbage = this.__garbage;
89
+
90
+ for (let ref = 0; ref < index_count; ref++) {
91
+ const index = indices[ref];
92
+
93
+ if (index === undefined) {
94
+ // unoccupied
95
+ continue;
96
+ }
97
+
98
+ if (!visible_set.get(ref)) {
99
+ //no longer visible
100
+ garbage[this.__garbagePointer++] = ref;
101
+ } else {
102
+ //visible, and is already in the group, update the set
103
+ visible_set.set(ref, false);
104
+ }
105
+ }
106
+ }
107
+
108
+ __removeGarbage() {
109
+
110
+ const garbagePointer = this.__garbagePointer;
111
+ const instances = this.instances;
112
+
113
+
114
+ const garbage = this.__garbage;
115
+
116
+ for (let i = 0; i < garbagePointer; i++) {
117
+ const ref = garbage[i];
118
+ instances.remove(ref);
119
+ }
120
+
121
+ }
122
+
123
+ /**
124
+ *
125
+ * @param {BinaryNode<number>} bvh
126
+ * @param {RowFirstTable} data
127
+ */
128
+ update(bvh, data) {
129
+
130
+ // reset visible set
131
+ this.__visible_set.reset();
132
+
133
+ //reset garbage pointer
134
+ this.__garbagePointer = 0;
135
+
136
+ const instances = this.instances;
137
+
138
+ const visible_instance_set = this.__visible_set;
139
+
140
+
141
+ // collect visible instances
142
+ const collected_nodes = queryBinaryNode_FrustumIntersections_Data(scratch_array, 0, bvh, this.__view.frustum);
143
+
144
+ for (let i = 0; i < collected_nodes; i++) {
145
+ const instance_id = scratch_array[i];
146
+
147
+ // mark them as visible
148
+ visible_instance_set.set(instance_id, true);
149
+ }
150
+
151
+ //remove those that are no longer visible
152
+ this.__compute_visibility_update();
153
+
154
+ //cull instances that are no longer visible
155
+ this.__removeGarbage();
156
+
157
+ //process entities that have become newly visible
158
+ for (
159
+ let index = visible_instance_set.nextSetBit(0);
160
+ index !== -1;
161
+ index = visible_instance_set.nextSetBit(index + 1)
162
+ ) {
163
+ //read transform data for instance
164
+ data.getRow(index, rowData);
165
+
166
+ const positionX = rowData[0];
167
+ const positionY = rowData[1];
168
+ const positionZ = rowData[2];
169
+
170
+ const encodedRotation = rowData[3];
171
+
172
+
173
+ const scaleX = rowData[4];
174
+ const scaleY = rowData[5];
175
+ const scaleZ = rowData[6];
176
+
177
+ const i = instances.add(index);
178
+
179
+ tempQuaternion.decodeFromUint32(encodedRotation);
180
+
181
+ //apply instance transforms
182
+ instances.setPositionAt(i, positionX, positionY, positionZ);
183
+ instances.setRotationAt(i, tempQuaternion.x, tempQuaternion.y, tempQuaternion.z, tempQuaternion.w);
184
+ instances.setScaleAt(i, scaleX, scaleY, scaleZ);
185
+ }
186
+ }
187
+ }
188
+
189
+ const tempQuaternion = new Quaternion();
190
+ const rowData = [];
@@ -112,15 +112,24 @@ export class Foliage2System extends System {
112
112
  startup(entityManager, readyCallback, errorCallback) {
113
113
  this.entityManager = entityManager;
114
114
 
115
- this.renderLayer = this.graphics.layers.create('instanced-mesh-system');
116
- this.renderLayer.buildVisibleSet = (frustums, filters, visitObject, thisArg) => {
115
+ const graphics = this.graphics;
116
+
117
+ this.renderLayer = graphics.layers.create('instanced-mesh-system');
118
+
119
+ graphics.views.elements.forEach(this.__handle_view_added, this);
120
+
121
+ graphics.views.elements.on.added.add(this.__handle_view_added, this);
122
+ graphics.views.elements.on.removed.add(this.__handle_view_removed, this);
123
+
124
+ this.renderLayer.buildVisibleSet = (view, visitObject, thisArg) => {
117
125
  const data = this.data;
118
126
 
119
127
  for (let entity in data) {
120
128
 
121
129
  const layers = data[entity];
130
+ const layer_count = layers.length;
122
131
 
123
- for (let i = 0, numLayers = layers.length; i < numLayers; i++) {
132
+ for (let i = 0; i < layer_count; i++) {
124
133
  /**
125
134
  * @type {{instances:InstancedFoliage}}
126
135
  */
@@ -133,9 +142,9 @@ export class Foliage2System extends System {
133
142
  */
134
143
  const instances = layerData.instances;
135
144
 
136
- instances.update(frustums, filters);
145
+ const view_state = instances.update(view);
137
146
 
138
- const group = instances.instances;
147
+ const group = view_state.instances;
139
148
 
140
149
  if (group.getCount() <= 0) {
141
150
  //no visible instances, skip
@@ -153,9 +162,61 @@ export class Foliage2System extends System {
153
162
  readyCallback();
154
163
  }
155
164
 
165
+ /**
166
+ *
167
+ * @param {CameraView} view
168
+ * @private
169
+ */
170
+ __handle_view_added(view) {
171
+ const em = this.entityManager;
172
+
173
+ if (em === null) {
174
+ return;
175
+ }
176
+
177
+ const ecd = em.dataset;
178
+
179
+ if (ecd === null) {
180
+ return;
181
+ }
182
+
183
+ ecd.traverseComponents(InstancedMeshComponent, im => {
184
+ im.add_view(view);
185
+ });
186
+ }
187
+
188
+
189
+ /**
190
+ *
191
+ * @param {CameraView} view
192
+ * @private
193
+ */
194
+ __handle_view_removed(view) {
195
+
196
+ const em = this.entityManager;
197
+
198
+ if (em === null) {
199
+ return;
200
+ }
201
+
202
+ const ecd = em.dataset;
203
+
204
+ if (ecd === null) {
205
+ return;
206
+ }
207
+
208
+ ecd.traverseComponents(InstancedMeshComponent, im => {
209
+ im.remove_view(view);
210
+ });
211
+ }
212
+
156
213
  shutdown(entityManager, readyCallback, errorCallback) {
157
214
  this.graphics.layers.remove(this.renderLayer);
158
215
 
216
+ this.graphics.views.elements.forEach(this.__handle_view_removed, this);
217
+ this.graphics.views.elements.on.added.remove(this.__handle_view_added, this);
218
+ this.graphics.views.elements.on.removed.remove(this.__handle_view_removed, this);
219
+
159
220
  readyCallback();
160
221
  }
161
222
 
@@ -166,16 +227,16 @@ export class Foliage2System extends System {
166
227
 
167
228
  /**
168
229
  *
169
- * @param {InstancedMeshComponent} foliage
230
+ * @param {InstancedMeshComponent} instances
170
231
  * @param entity
171
232
  */
172
- link(foliage, entity) {
233
+ link(instances, entity) {
173
234
  const layers = [];
174
235
  const data = this.data;
175
236
 
176
237
  data[entity] = layers;
177
238
 
178
- const layerList = foliage.layers;
239
+ const layerList = instances.layers;
179
240
  const layer_count = layerList.length;
180
241
 
181
242
  for (let i = 0; i < layer_count; i++) {
@@ -215,11 +276,16 @@ export class Foliage2System extends System {
215
276
  }
216
277
  }
217
278
 
218
- //TODO need to expose InstancedMesh BVH to let camera frustum calculation work correctly
219
-
279
+ //
280
+ this.graphics.views.elements.forEach(instances.add_view, instances);
220
281
  }
221
282
 
222
- unlink(foliage, entity) {
283
+ /**
284
+ *
285
+ * @param {InstancedMeshComponent} instances
286
+ * @param {number} entity
287
+ */
288
+ unlink(instances, entity) {
223
289
  const renderLayer = this.renderLayer;
224
290
  const layers = this.data[entity];
225
291
 
@@ -230,7 +296,9 @@ export class Foliage2System extends System {
230
296
  cleanupLayer(layerData, renderLayer);
231
297
  }
232
298
 
233
- foliage.dispose();
299
+ this.graphics.views.elements.forEach(instances.remove_view, instances);
300
+
301
+ instances.dispose();
234
302
 
235
303
  delete this.data[entity];
236
304
  }