@woosh/meep-engine 2.36.23 → 2.36.26

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
@@ -84,6 +84,39 @@ function makeIdMaterialStatic() {
84
84
 
85
85
  const material_id_unskinned = makeIdMaterialStatic();
86
86
 
87
+ /**
88
+ *
89
+ * @type {WeakMap<Object3D, Object3D>}
90
+ */
91
+ const object_id_cache = new WeakMap();
92
+
93
+ /**
94
+ *
95
+ * @param {Mesh|SkinnedMesh} object
96
+ */
97
+ function obtain_id_mesh(object) {
98
+ const existing = object_id_cache.get(object);
99
+
100
+ if (existing !== undefined) {
101
+ return existing;
102
+ }
103
+
104
+ let material, mesh;
105
+ if (object.isSkinnedMesh) {
106
+ material = makeIdMaterialSkinned();
107
+ mesh = new SkinnedMesh(object.geometry, material);
108
+ mesh.bindMatrix.copy(object.bindMatrix);
109
+ mesh.bindMatrixInverse.copy(object.bindMatrixInverse);
110
+ mesh.skeleton = object.skeleton;
111
+ } else {
112
+ material = makeIdMaterialStatic();
113
+ mesh = new Mesh(object.geometry, material);
114
+ }
115
+
116
+ object_id_cache.set(object, mesh);
117
+
118
+ return mesh;
119
+ }
87
120
 
88
121
  function makeIdMaterialSkinned() {
89
122
  return new ShaderMaterial({
@@ -99,8 +132,6 @@ const material_id_skinned = makeIdMaterialSkinned();
99
132
  material_id_skinned.skinning = true;
100
133
 
101
134
 
102
- const PARAMETER_COUNT = 5;
103
- const PARAMETER_PER_OBJECT_SLOTS = 4;
104
135
  const OBJECT_MAX_COUNT = 256;
105
136
 
106
137
 
@@ -373,22 +404,10 @@ export class OutlineRenderer {
373
404
  __append_to_id_scene(object) {
374
405
  if (object.isMesh) {
375
406
 
376
- let material;
377
- let mesh;
378
-
379
- if (object.isSkinnedMesh) {
380
- material = makeIdMaterialSkinned();
381
- mesh = new SkinnedMesh(object.geometry, material);
382
- mesh.bindMatrix.copy(object.bindMatrix);
383
- mesh.bindMatrixInverse.copy(object.bindMatrixInverse);
384
- mesh.skeleton = object.skeleton;
385
- } else {
386
- material = makeIdMaterialStatic();
387
- mesh = new Mesh(object.geometry, material);
388
- }
407
+ const mesh = obtain_id_mesh(object);
389
408
 
390
409
  const parameter_set_id = this.__definition_dictionary.get(this.__active_definition);
391
- material.uniforms.id.value = parameter_set_id;
410
+ mesh.material.uniforms.id.value = parameter_set_id;
392
411
 
393
412
  mesh.matrixAutoUpdate = false;
394
413
  mesh.matrixWorldNeedsUpdate = false;
@@ -399,6 +418,7 @@ export class OutlineRenderer {
399
418
  mesh.modelViewMatrix.copy(object.modelViewMatrix);
400
419
 
401
420
  this.__id_scene.children.push(mesh);
421
+
402
422
  }
403
423
  }
404
424
 
@@ -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,9 +20,10 @@ 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
 
@@ -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,109 @@
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
+ sg.draw_method = best_adapter;
106
+
107
+ return true;
108
+ }
109
+ }
@@ -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,6 +50,12 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
36
50
  this.__geometry_building_promises = new Map();
37
51
  }
38
52
 
53
+ score(geometry, material, instance_count) {
54
+ const polycount = compute_geometry_polycount(geometry);
55
+
56
+ return inverseLerp(MICRON_POLYCOUNT_THRESHOLD_MIN, MICRON_POLYCOUNT_THRESHOLD_MAX, polycount);
57
+ }
58
+
39
59
  add(sg) {
40
60
 
41
61
  const geometry_id = sg.geometry.id;
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.23",
8
+ "version": "2.36.26",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",