@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.
- package/engine/ecs/EntityComponentDataset.js +14 -0
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +33 -0
- package/engine/graphics/ecs/mesh-v2/render/adapters/AbstractRenderAdapter.js +11 -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 +114 -0
- package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +3 -0
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -82
- package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +47 -2
- 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
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
8
|
+
"version": "2.36.27",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|