@woosh/meep-engine 2.36.24 → 2.36.27

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.
@@ -1216,6 +1216,20 @@ export class EntityComponentDataset {
1216
1216
  }
1217
1217
  }
1218
1218
 
1219
+ /**
1220
+ *
1221
+ * @returns {Generator<number>}
1222
+ */
1223
+ * createEntityIterator() {
1224
+
1225
+ const eo = this.entityOccupancy;
1226
+
1227
+ for (let i = eo.nextSetBit(0); i !== -1; i = eo.nextSetBit(i + 1)) {
1228
+ yield i;
1229
+ }
1230
+
1231
+ }
1232
+
1219
1233
  /**
1220
1234
  * @template T
1221
1235
  * @param {Class<T>} klass
@@ -15,6 +15,10 @@ import {
15
15
  } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
16
16
  import { bvh_query_leaves_generic } from "../../../../core/bvh2/bvh3/bvh_query_leaves_generic.js";
17
17
  import { BVHQueryIntersectsRay } from "../../../../core/bvh2/bvh3/query/BVHQueryIntersectsRay.js";
18
+ import Task from "../../../../core/process/task/Task.js";
19
+ import { RuntimeDrawMethodOptimizer } from "./render/optimization/RuntimeDrawMethodOptimizer.js";
20
+ import TaskSignal from "../../../../core/process/task/TaskSignal.js";
21
+ import TaskState from "../../../../core/process/task/TaskState.js";
18
22
 
19
23
 
20
24
  /**
@@ -81,6 +85,30 @@ export class ShadedGeometrySystem extends System {
81
85
  * @private
82
86
  */
83
87
  this.__bvh_binary = new ExplicitBinaryBoundingVolumeHierarchy();
88
+
89
+ const optimizer = new RuntimeDrawMethodOptimizer();
90
+
91
+ this.__optimization_task = new Task({
92
+ name: 'ShadedGeometry Draw Method optimizer',
93
+ initializer: () => {
94
+ optimizer.__system = this;
95
+ },
96
+ cycleFunction() {
97
+ if (optimizer.step()) {
98
+ return TaskSignal.Continue;
99
+ } else {
100
+ return TaskSignal.Yield;
101
+ }
102
+ }
103
+ });
104
+ }
105
+
106
+ /**
107
+ * NOTE: DO NOT MODIFY RESULTS
108
+ * @returns {Map<number,number>}
109
+ */
110
+ getGeometryUsageCounters() {
111
+ return this.__geometry_usage_counters;
84
112
  }
85
113
 
86
114
  /**
@@ -184,6 +212,9 @@ export class ShadedGeometrySystem extends System {
184
212
  return ctx.collect(destination, destination_offset, graphics.renderer, view, this.__bvh_binary, ecd);
185
213
  });
186
214
 
215
+ this.__optimization_task.state.set(TaskState.INITIAL);
216
+ engine.executor.run(this.__optimization_task);
217
+
187
218
  readyCallback();
188
219
  }
189
220
 
@@ -194,6 +225,8 @@ export class ShadedGeometrySystem extends System {
194
225
 
195
226
  const view_list = graphics.views.elements;
196
227
 
228
+ engine.executor.removeTask(this.__optimization_task);
229
+
197
230
  view_list.forEach(this.__handle_view_removed, this);
198
231
  view_list.on.added.remove(this.__handle_view_added, this);
199
232
  view_list.on.removed.remove(this.__handle_view_removed, this);
@@ -20,12 +20,22 @@ export class AbstractRenderAdapter {
20
20
  *
21
21
  * @param {THREE.BufferGeometry} geometry
22
22
  * @param {THREE.Material} material
23
+ * @param {number} instance_count
23
24
  * @returns {number}
24
25
  */
25
- score(geometry, material) {
26
+ score(geometry, material, instance_count) {
26
27
  return 0;
27
28
  }
28
29
 
30
+ /**
31
+ * Express intent to draw
32
+ * @param {ShadedGeometry} sg
33
+ * @returns {boolean} true if able to draw immediately, false otherwise
34
+ */
35
+ intend_draw(sg) {
36
+ return true;
37
+ }
38
+
29
39
  /**
30
40
  *
31
41
  * @param {ShadedGeometry} sg
@@ -14,11 +14,6 @@ export class GenericRendererAdapter extends AbstractRenderAdapter {
14
14
  this.__cache = SGThreeObjectCache.INSTANCE;
15
15
  }
16
16
 
17
-
18
- score(geometry, material) {
19
- return 0;
20
- }
21
-
22
17
  /**
23
18
  *
24
19
  * @param {THREE.WebGLRenderer} renderer
@@ -69,6 +69,16 @@ export class InstancedRendererAdapter extends AbstractRenderAdapter {
69
69
  this.__active_meshes = new Set();
70
70
  }
71
71
 
72
+ score(geometry, material, instance_count) {
73
+ if (instance_count > 16) {
74
+ return 1;
75
+ } else if (instance_count < 4) {
76
+ return -1;
77
+ } else {
78
+ return 0;
79
+ }
80
+ }
81
+
72
82
  /**
73
83
  *
74
84
  * @param {ShadedGeometry} sg
@@ -0,0 +1,114 @@
1
+ import { ShadedGeometry } from "../../ShadedGeometry.js";
2
+
3
+ export class RuntimeDrawMethodOptimizer {
4
+ constructor() {
5
+
6
+ /**
7
+ *
8
+ * @type {ShadedGeometrySystem|null}
9
+ * @private
10
+ */
11
+ this.__system = null;
12
+
13
+ /**
14
+ *
15
+ * @type {Generator<number>}
16
+ * @private
17
+ */
18
+ this.__entity_iterator = null;
19
+ }
20
+
21
+ /**
22
+ *
23
+ * @returns {boolean} true if a change was made, false otherwise
24
+ */
25
+ step() {
26
+ const system = this.__system;
27
+
28
+ if (system === null) {
29
+ return false;
30
+ }
31
+
32
+ const ecd = system.entityManager.dataset;
33
+
34
+ if (ecd === null) {
35
+ // no dataset attached
36
+ return false;
37
+ }
38
+
39
+ if (!ecd.isComponentTypeRegistered(ShadedGeometry)) {
40
+ // component not registered
41
+ return false;
42
+ }
43
+
44
+ if (this.__entity_iterator === null) {
45
+ this.__entity_iterator = ecd.createEntityIterator();
46
+ }
47
+
48
+ const next = this.__entity_iterator.next();
49
+
50
+ if (next.done) {
51
+ this.__entity_iterator = ecd.createEntityIterator();
52
+ return false;
53
+ }
54
+
55
+ const entity = next.value;
56
+
57
+ /**
58
+ *
59
+ * @type {ShadedGeometry}
60
+ */
61
+ const sg = ecd.getComponent(entity, ShadedGeometry);
62
+
63
+ if (sg === undefined) {
64
+ return false;
65
+ }
66
+
67
+ const use_count = system.getGeometryUsageCounters().get(sg.geometry.id);
68
+
69
+ /**
70
+ *
71
+ * @type {ShadedGeometryRendererContext|undefined}
72
+ */
73
+ const first_context = system.__view_contexts.values().next().value;
74
+
75
+ if (first_context === undefined) {
76
+ return false;
77
+ }
78
+
79
+ const adapters = first_context.adapters;
80
+ const adapter_count = adapters.length;
81
+
82
+ let best_score = Number.NEGATIVE_INFINITY;
83
+ let best_adapter = -1;
84
+
85
+ for (let i = 0; i < adapter_count; i++) {
86
+ const adapter = adapters[i];
87
+
88
+ const score = adapter.score(sg.geometry, sg.material, use_count);
89
+
90
+ if (score > best_score) {
91
+ best_score = score;
92
+ best_adapter = i;
93
+ }
94
+ }
95
+
96
+ if (best_adapter === -1) {
97
+ // not found
98
+ return false;
99
+ }
100
+
101
+ if (sg.draw_method === best_adapter) {
102
+ return false;
103
+ }
104
+
105
+ if (!adapters[best_adapter].intend_draw(sg)) {
106
+ // unable to draw, don't do anything
107
+ return false;
108
+ }
109
+
110
+ sg.draw_method = best_adapter;
111
+
112
+ return true;
113
+ }
114
+ }
@@ -7,6 +7,7 @@ import { three_object_to_entity_composition } from "../three_object_to_entity_co
7
7
  import { AttachmentSystem } from "../../../../ecs/attachment/AttachmentSystem.js";
8
8
  import { TransformAttachmentSystem } from "../../../../ecs/transform-attachment/TransformAttachmentSystem.js";
9
9
  import { Camera } from "../../camera/Camera.js";
10
+ import { MicronRenderPlugin } from "../../../micron/plugin/MicronRenderPlugin.js";
10
11
 
11
12
  const eh = new EngineHarness();
12
13
 
@@ -25,6 +26,8 @@ async function init(harness) {
25
26
  config.addSystem(new TransformAttachmentSystem());
26
27
  config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
27
28
 
29
+ config.addPlugin( MicronRenderPlugin);
30
+
28
31
  await config.apply(engine);
29
32
 
30
33
  eh.initialize().then(main);
@@ -37,13 +37,6 @@ export class MicronRenderPlugin extends EnginePlugin {
37
37
  layer.managed = true;
38
38
  layer.renderPass = RenderPassType.Opaque;
39
39
 
40
- /**
41
- *
42
- * @type {RenderLayer|null}
43
- * @private
44
- */
45
- this.__micron_render_layer = layer;
46
-
47
40
  this.__vgtr.on.contextAdded.add(ctx => {
48
41
  const visibleSet = layer.visibleSet;
49
42
 
@@ -54,8 +47,6 @@ export class MicronRenderPlugin extends EnginePlugin {
54
47
 
55
48
  });
56
49
 
57
- this.__view_observers = new Map();
58
-
59
50
  /**
60
51
  *
61
52
  * @type {AsyncRemoteHashMap}
@@ -97,55 +88,6 @@ export class MicronRenderPlugin extends EnginePlugin {
97
88
  this.__sg_system = null;
98
89
  }
99
90
 
100
- /**
101
- *
102
- * @param {THREE.WebGLRenderer} renderer
103
- * @param {THREE.Camera} camera
104
- * @param {THREE.Object3D[]} elements
105
- * @param element_count
106
- */
107
- render(renderer, camera, elements, element_count = elements.length) {
108
- const vgtr = this.__vgtr;
109
-
110
- vgtr.clear();
111
-
112
- vgtr.attachMany(elements, element_count);
113
-
114
- vgtr.update(renderer, camera);
115
- vgtr.render();
116
- }
117
-
118
- /**
119
- *
120
- * @param {CameraView} view
121
- * @private
122
- */
123
- __handle_view_added(view) {
124
- const observer = () => {
125
- const ge = this.engine.graphics;
126
-
127
- this.render(ge.renderer, view.camera, view.visible_objects.elements, view.visible_objects.size);
128
- };
129
-
130
- this.__view_observers.set(view, observer);
131
-
132
-
133
- view.on.preRender.add(observer);
134
- }
135
-
136
- /**
137
- *
138
- * @param {CameraView} view
139
- * @private
140
- */
141
- __handle_view_removed(view) {
142
- const observer = this.__view_observers.get(view);
143
-
144
- this.__view_observers.delete(view);
145
-
146
- view.on.preRender.remove(observer);
147
- }
148
-
149
91
  /**
150
92
  *
151
93
  * @param {System} system
@@ -214,20 +156,6 @@ export class MicronRenderPlugin extends EnginePlugin {
214
156
  am.registerTransformer("model/gltf", this.__asset_transformer);
215
157
  am.registerTransformer("model/gltf+json", this.__asset_transformer);
216
158
 
217
- /**
218
- *
219
- * @type {GraphicsEngine}
220
- */
221
- const ge = engine.graphics;
222
-
223
- ge.layers.add(this.__micron_render_layer);
224
-
225
- ge.views.elements.on.added.add(this.__handle_view_added, this);
226
- ge.views.elements.on.removed.add(this.__handle_view_removed, this);
227
-
228
- // link all views
229
- ge.views.elements.forEach(this.__handle_view_added, this);
230
-
231
159
  this.__persisted_geometry.attach_storage(engine.storage);
232
160
 
233
161
  engine.entityManager.on.systemAdded.add(this.__handle_system_added, this);
@@ -250,16 +178,6 @@ export class MicronRenderPlugin extends EnginePlugin {
250
178
 
251
179
  this.__asset_transformer.assetManager = null;
252
180
 
253
- const ge = engine.graphics;
254
-
255
- ge.layers.remove(this.__micron_render_layer);
256
-
257
- ge.views.elements.on.added.remove(this.__handle_view_added, this);
258
- ge.views.elements.on.removed.remove(this.__handle_view_removed, this);
259
-
260
- // clear all observer
261
- ge.views.elements.forEach(this.__handle_view_removed, this);
262
-
263
181
  engine.entityManager.on.systemAdded.remove(this.__handle_system_added, this);
264
182
  engine.entityManager.on.systemRemoved.remove(this.__handle_system_removed, this);
265
183
 
@@ -3,6 +3,8 @@ import Vector2 from "../../../../../core/geom/Vector2.js";
3
3
  import { buildMicronGeometryFromBufferGeometry } from "../../build/buildMicronGeometryFromBufferGeometry.js";
4
4
  import { ShadedGeometryFlags } from "../../../ecs/mesh-v2/ShadedGeometryFlags.js";
5
5
  import { MaterialVertexSpec, MaterialVertexSpecFlags } from "../../render/v1/MaterialVertexSpec.js";
6
+ import { compute_geometry_polycount } from "../../../geometry/compute_geometry_polycount.js";
7
+ import { inverseLerp } from "../../../../../core/math/inverseLerp.js";
6
8
 
7
9
  /**
8
10
  *
@@ -10,6 +12,18 @@ import { MaterialVertexSpec, MaterialVertexSpecFlags } from "../../render/v1/Mat
10
12
  */
11
13
  const scratch_mvc = new MaterialVertexSpec();
12
14
 
15
+ /**
16
+ * @readonly
17
+ * @type {number}
18
+ */
19
+ const MICRON_POLYCOUNT_THRESHOLD_MIN = 4096;
20
+
21
+ /**
22
+ * @readonly
23
+ * @type {number}
24
+ */
25
+ const MICRON_POLYCOUNT_THRESHOLD_MAX = 10000;
26
+
13
27
  export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
14
28
 
15
29
  constructor() {
@@ -36,13 +50,28 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
36
50
  this.__geometry_building_promises = new Map();
37
51
  }
38
52
 
39
- add(sg) {
53
+ score(geometry, material, instance_count) {
54
+ const polycount = compute_geometry_polycount(geometry);
40
55
 
56
+ return inverseLerp(MICRON_POLYCOUNT_THRESHOLD_MIN, MICRON_POLYCOUNT_THRESHOLD_MAX, polycount);
57
+ }
58
+
59
+ /**
60
+ *
61
+ * @param {ShadedGeometry} sg
62
+ * @returns {MicronGeometry}
63
+ * @private
64
+ */
65
+ __obtain_micron_geometry(sg) {
41
66
  const geometry_id = sg.geometry.id;
42
67
 
43
68
  const micron_geometry = this.__geometry_map.get(geometry_id);
44
69
 
45
- if (micron_geometry === undefined) {
70
+ if (micron_geometry !== undefined) {
71
+
72
+ return micron_geometry;
73
+
74
+ } else {
46
75
  // no micron geometry
47
76
  if (!this.__geometry_building_promises.has(geometry_id)) {
48
77
  const p = buildMicronGeometryFromBufferGeometry(sg.geometry);
@@ -51,6 +80,20 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
51
80
 
52
81
  p.then(mg => this.__geometry_map.set(geometry_id, mg));
53
82
  }
83
+ return undefined;
84
+ }
85
+
86
+ }
87
+
88
+ intend_draw(sg) {
89
+ return this.__obtain_micron_geometry(sg) !== undefined;
90
+ }
91
+
92
+ add(sg) {
93
+ const micron_geometry = this.__obtain_micron_geometry(sg);
94
+
95
+ if (micron_geometry === undefined) {
96
+ // no micron geometry
54
97
  return;
55
98
  }
56
99
 
@@ -74,6 +117,8 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
74
117
  * @param {CameraView} view
75
118
  */
76
119
  build_start(renderer, view) {
120
+ this.__object_count = 0;
121
+
77
122
  const resolution = new Vector2();
78
123
  let pixelRatio = 1;
79
124
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.36.24",
8
+ "version": "2.36.27",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",