@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.
- package/engine/ecs/EntityComponentDataset.js +14 -0
- package/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js +36 -16
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +33 -0
- package/engine/graphics/ecs/mesh-v2/render/adapters/AbstractRenderAdapter.js +2 -1
- package/engine/graphics/ecs/mesh-v2/render/adapters/GenericRendererAdapter.js +0 -5
- package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +10 -0
- package/engine/graphics/ecs/mesh-v2/render/optimization/RuntimeDrawMethodOptimizer.js +109 -0
- package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +20 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
|
@@ -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.
|
|
8
|
+
"version": "2.36.26",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|