@woosh/meep-engine 2.36.21 → 2.36.24
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/core/binary/BitSet.js +2 -1
- package/core/bvh2/LeafNode.d.ts +2 -0
- package/core/collection/array/arrayQuickSort.js +73 -3
- package/core/collection/array/array_shrink_to_size.js +14 -0
- package/engine/ecs/EntityComponentDataset.js +10 -5
- package/engine/graphics/GraphicsEngine.d.ts +7 -4
- package/engine/graphics/GraphicsEngine.js +3 -1
- package/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js +36 -16
- package/engine/graphics/ecs/mesh/MeshSystem.js +3 -2
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +67 -14
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +5 -5
- package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +1 -1
- package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +13 -6
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +5 -2
- package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.js +1 -1
- package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +0 -1
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +4 -3
- package/engine/graphics/micron/render/v1/VGThreeRenderer.js +1 -1
- package/engine/graphics/particles/ecs/ParticleEmitterSystem.js +3 -3
- package/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.js +4 -2
- package/engine/graphics/render/layers/RenderLayer.d.ts +3 -0
- package/engine/graphics/render/layers/RenderLayer.js +7 -2
- package/engine/graphics/render/visibility/IncrementalDeltaSet.d.ts +8 -3
- package/engine/graphics/render/visibility/IncrementalDeltaSet.js +65 -64
- package/engine/graphics/render/visibility/IncrementalDeltaSet.spec.js +39 -0
- package/engine/graphics/shadows/ShadowMapRenderer.js +5 -0
- package/package.json +1 -1
package/core/binary/BitSet.js
CHANGED
|
@@ -278,12 +278,13 @@ BitSet.prototype.nextSetBit = function (fromIndex) {
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
|
|
281
|
-
//scan the rest
|
|
281
|
+
//scan the rest of the words
|
|
282
282
|
const word_count = bit_length / 32;
|
|
283
283
|
for (; word_index < word_count; word_index++) {
|
|
284
284
|
word = data[word_index];
|
|
285
285
|
|
|
286
286
|
if (word === 0) {
|
|
287
|
+
// all 0s
|
|
287
288
|
continue;
|
|
288
289
|
}
|
|
289
290
|
|
package/core/bvh2/LeafNode.d.ts
CHANGED
|
@@ -35,15 +35,15 @@ export function arrayQuickSort(data, score_function, score_function_context, sta
|
|
|
35
35
|
|
|
36
36
|
const pivotIndex = (left + right) >> 1;
|
|
37
37
|
|
|
38
|
-
const pivot = score_function.call(score_function_context,data[pivotIndex]);
|
|
38
|
+
const pivot = score_function.call(score_function_context, data[pivotIndex]);
|
|
39
39
|
|
|
40
40
|
/* partition */
|
|
41
41
|
while (i <= j) {
|
|
42
|
-
while (score_function.call(score_function_context,data[i]) < pivot) {
|
|
42
|
+
while (score_function.call(score_function_context, data[i]) < pivot) {
|
|
43
43
|
i++;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
while (score_function.call(score_function_context,data[j]) > pivot) {
|
|
46
|
+
while (score_function.call(score_function_context, data[j]) > pivot) {
|
|
47
47
|
j--;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -70,3 +70,73 @@ export function arrayQuickSort(data, score_function, score_function_context, sta
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @template T
|
|
76
|
+
* @param {T[]} data
|
|
77
|
+
* @param {function(T,T):number} compare_function
|
|
78
|
+
* @param {*} compare_function_context
|
|
79
|
+
* @param {number} start
|
|
80
|
+
* @param {number} end
|
|
81
|
+
*/
|
|
82
|
+
export function array_quick_sort_by_comparator(data, compare_function, compare_function_context, start, end) {
|
|
83
|
+
if (start >= end) {
|
|
84
|
+
// section of 0 size, nothing to sort
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let stackPointer = 2;
|
|
89
|
+
let i, j;
|
|
90
|
+
|
|
91
|
+
stack[0] = start;
|
|
92
|
+
stack[1] = end;
|
|
93
|
+
|
|
94
|
+
while (stackPointer > 0) {
|
|
95
|
+
stackPointer -= 2;
|
|
96
|
+
|
|
97
|
+
const right = stack[stackPointer + 1];
|
|
98
|
+
const left = stack[stackPointer];
|
|
99
|
+
|
|
100
|
+
i = left;
|
|
101
|
+
j = right;
|
|
102
|
+
|
|
103
|
+
const pivotIndex = (left + right) >> 1;
|
|
104
|
+
|
|
105
|
+
const pivot = data[pivotIndex];
|
|
106
|
+
|
|
107
|
+
/* partition */
|
|
108
|
+
while (i <= j) {
|
|
109
|
+
while (compare_function.call(compare_function_context, data[i], pivot) < 0) {
|
|
110
|
+
i++;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
while (compare_function.call(compare_function_context, data[j], pivot) > 0) {
|
|
114
|
+
j--;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (i <= j) {
|
|
118
|
+
|
|
119
|
+
if (i !== j) {
|
|
120
|
+
//do swap
|
|
121
|
+
const tmp = data[i];
|
|
122
|
+
data[i] = data[j];
|
|
123
|
+
data[j] = tmp;
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
i++;
|
|
128
|
+
j--;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* recursion */
|
|
133
|
+
if (left < j) {
|
|
134
|
+
stack[stackPointer++] = left;
|
|
135
|
+
stack[stackPointer++] = j;
|
|
136
|
+
}
|
|
137
|
+
if (i < right) {
|
|
138
|
+
stack[stackPointer++] = i;
|
|
139
|
+
stack[stackPointer++] = right;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -7,6 +7,7 @@ import Signal, {
|
|
|
7
7
|
import { EventType } from "./EntityManager.js";
|
|
8
8
|
import { SignalHandler } from "../../core/events/signal/SignalHandler.js";
|
|
9
9
|
import { arraySetDiff } from "../../core/collection/array/arraySetDiff.js";
|
|
10
|
+
import { array_shrink_to_size } from "../../core/collection/array/array_shrink_to_size.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
*
|
|
@@ -1088,7 +1089,9 @@ export class EntityComponentDataset {
|
|
|
1088
1089
|
//map classes to indices
|
|
1089
1090
|
const indices = scratch_array_0;
|
|
1090
1091
|
|
|
1092
|
+
// prepare sorted array of component indices
|
|
1091
1093
|
const numClasses = classes.length;
|
|
1094
|
+
|
|
1092
1095
|
for (i = 0; i < numClasses; i++) {
|
|
1093
1096
|
const k = classes[i];
|
|
1094
1097
|
|
|
@@ -1108,11 +1111,8 @@ export class EntityComponentDataset {
|
|
|
1108
1111
|
|
|
1109
1112
|
// crop args array to the right length (since we're re-using a scratch array)
|
|
1110
1113
|
const args_length_intended = numClasses + 1;
|
|
1111
|
-
const args_length_current = scratch_array_1.length;
|
|
1112
1114
|
|
|
1113
|
-
|
|
1114
|
-
scratch_array_1.splice(args_length_intended, args_length_current - args_length_intended);
|
|
1115
|
-
}
|
|
1115
|
+
array_shrink_to_size(scratch_array_1, args_length_intended);
|
|
1116
1116
|
|
|
1117
1117
|
const entityOccupancy = this.entityOccupancy;
|
|
1118
1118
|
|
|
@@ -1120,7 +1120,12 @@ export class EntityComponentDataset {
|
|
|
1120
1120
|
const component_occupancy = this.componentOccupancy;
|
|
1121
1121
|
const components = this.components;
|
|
1122
1122
|
|
|
1123
|
-
entity_loop: for (
|
|
1123
|
+
entity_loop: for (
|
|
1124
|
+
entityIndex = entityOccupancy.nextSetBit(0);
|
|
1125
|
+
entityIndex !== -1;
|
|
1126
|
+
entityIndex = entityOccupancy.nextSetBit(entityIndex + 1)
|
|
1127
|
+
) {
|
|
1128
|
+
|
|
1124
1129
|
const componentOccupancyAddress = entityIndex * component_type_count;
|
|
1125
1130
|
|
|
1126
1131
|
for (i = 0; i < numClasses; i++) {
|
|
@@ -5,12 +5,15 @@ import Vector2 from "../../core/geom/Vector2";
|
|
|
5
5
|
import View from "../../view/View";
|
|
6
6
|
|
|
7
7
|
export class GraphicsEngine {
|
|
8
|
-
scene: Scene
|
|
9
|
-
camera: PerspectiveCamera
|
|
8
|
+
readonly scene: Scene
|
|
9
|
+
readonly camera: PerspectiveCamera
|
|
10
10
|
|
|
11
|
-
viewport: View
|
|
11
|
+
readonly viewport: View
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated
|
|
15
|
+
*/
|
|
16
|
+
readonly layers: RenderLayerManager
|
|
14
17
|
|
|
15
18
|
get domElement(): HTMLCanvasElement
|
|
16
19
|
|
|
@@ -559,10 +559,12 @@ GraphicsEngine.prototype.prepareRenderPass = function (passType) {
|
|
|
559
559
|
let j = 0;
|
|
560
560
|
|
|
561
561
|
const visible_objects = this.main_view.visible_objects;
|
|
562
|
+
|
|
562
563
|
const element_count = visible_objects.size;
|
|
564
|
+
const elements = visible_objects.elements;
|
|
563
565
|
|
|
564
566
|
for (let i = 0; i < element_count; i++) {
|
|
565
|
-
const object3D =
|
|
567
|
+
const object3D = elements[i];
|
|
566
568
|
|
|
567
569
|
const object_pass = classifyPassTypeFromObject(object3D);
|
|
568
570
|
|
|
@@ -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
|
|
|
@@ -215,9 +215,10 @@ export class MeshSystem extends System {
|
|
|
215
215
|
return;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
const
|
|
218
|
+
const visibleSet = this.renderLayer.visibleSet;
|
|
219
|
+
const visibleElements = visibleSet.elements;
|
|
219
220
|
|
|
220
|
-
const n =
|
|
221
|
+
const n = visibleSet.size;
|
|
221
222
|
|
|
222
223
|
for (let i = 0; i < n; i++) {
|
|
223
224
|
|
|
@@ -4,14 +4,53 @@ import { DrawMode } from "./DrawMode.js";
|
|
|
4
4
|
import { ShadedGeometryFlags } from "./ShadedGeometryFlags.js";
|
|
5
5
|
import { aabb3_matrix4_project } from "../../../../core/geom/3d/aabb/aabb3_matrix4_project.js";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* @readonly
|
|
9
|
+
* @type {Float32Array}
|
|
10
|
+
*/
|
|
9
11
|
const scratch_aabb3_array = new Float32Array(6);
|
|
10
12
|
|
|
11
13
|
const DEFAULT_FLAGS = ShadedGeometryFlags.CastShadow
|
|
12
14
|
| ShadedGeometryFlags.ReceiveShadow
|
|
13
15
|
;
|
|
14
16
|
|
|
17
|
+
const FLAG_SET_EQUALITY = ShadedGeometryFlags.CastShadow
|
|
18
|
+
| ShadedGeometryFlags.ReceiveShadow
|
|
19
|
+
;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @type {number}
|
|
24
|
+
*/
|
|
25
|
+
const TRANSFORM_ALLOCATOR_BUCKET_CAPACITY = 16;
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @type {ArrayBuffer|null}
|
|
29
|
+
*/
|
|
30
|
+
let transform_bucket = null;
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @type {number}
|
|
34
|
+
*/
|
|
35
|
+
let transform_bucket_cursor = TRANSFORM_ALLOCATOR_BUCKET_CAPACITY;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* custom Float32Array allocator, allocated memory in continuous chunks
|
|
39
|
+
* @returns {Float32Array}
|
|
40
|
+
*/
|
|
41
|
+
function allocate_transform_m4() {
|
|
42
|
+
if (transform_bucket_cursor >= TRANSFORM_ALLOCATOR_BUCKET_CAPACITY) {
|
|
43
|
+
transform_bucket = new ArrayBuffer(TRANSFORM_ALLOCATOR_BUCKET_CAPACITY * 4 * 16);
|
|
44
|
+
transform_bucket_cursor = 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const result = new Float32Array(transform_bucket, transform_bucket_cursor * 16 * 4, 16);
|
|
48
|
+
|
|
49
|
+
transform_bucket_cursor++;
|
|
50
|
+
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
15
54
|
export class ShadedGeometry {
|
|
16
55
|
constructor() {
|
|
17
56
|
/**
|
|
@@ -27,24 +66,23 @@ export class ShadedGeometry {
|
|
|
27
66
|
|
|
28
67
|
/**
|
|
29
68
|
*
|
|
30
|
-
* @type {
|
|
69
|
+
* @type {number}
|
|
70
|
+
* @private
|
|
31
71
|
*/
|
|
32
|
-
this.
|
|
72
|
+
this.__bvh_leaf_id = -1;
|
|
33
73
|
|
|
34
74
|
/**
|
|
35
75
|
*
|
|
36
|
-
* @type {
|
|
76
|
+
* @type {ExplicitBinaryBoundingVolumeHierarchy|null}
|
|
37
77
|
* @private
|
|
38
78
|
*/
|
|
39
|
-
this.
|
|
79
|
+
this.__bvh_tree = null;
|
|
40
80
|
|
|
41
|
-
this.__bvh_leaf_id = -1;
|
|
42
81
|
/**
|
|
43
|
-
*
|
|
44
|
-
* @type {
|
|
82
|
+
* @readonly
|
|
83
|
+
* @type {number[]|Float32Array}
|
|
45
84
|
* @private
|
|
46
85
|
*/
|
|
47
|
-
this.__bvh_tree = null;
|
|
48
86
|
this.__bvh_aabb = new Float32Array(6);
|
|
49
87
|
|
|
50
88
|
/**
|
|
@@ -65,12 +103,19 @@ export class ShadedGeometry {
|
|
|
65
103
|
* Transform matrix
|
|
66
104
|
* Transient, assigned in the system
|
|
67
105
|
* @type {number[]|null}
|
|
106
|
+
* @readonly
|
|
68
107
|
* @private
|
|
69
108
|
*/
|
|
70
|
-
this.__transform =
|
|
109
|
+
this.__transform = allocate_transform_m4();
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
* @type {DrawMode|number}
|
|
114
|
+
*/
|
|
115
|
+
this.mode = DrawMode.Triangles;
|
|
71
116
|
|
|
72
117
|
/**
|
|
73
|
-
* Transient property signifying how to render object
|
|
118
|
+
* Transient property signifying how to render object, such as using INSTANCED draw
|
|
74
119
|
* @type {number}
|
|
75
120
|
*/
|
|
76
121
|
this.draw_method = 0;
|
|
@@ -135,7 +180,12 @@ export class ShadedGeometry {
|
|
|
135
180
|
* @return {number}
|
|
136
181
|
*/
|
|
137
182
|
hash() {
|
|
138
|
-
return this.geometry.id
|
|
183
|
+
return this.geometry.id
|
|
184
|
+
^ this.material.id
|
|
185
|
+
^ (this.flags & FLAG_SET_EQUALITY)
|
|
186
|
+
^ (this.draw_method << 16)
|
|
187
|
+
^ (this.mode << 12)
|
|
188
|
+
;
|
|
139
189
|
}
|
|
140
190
|
|
|
141
191
|
/**
|
|
@@ -146,6 +196,9 @@ export class ShadedGeometry {
|
|
|
146
196
|
equals(other) {
|
|
147
197
|
return this.geometry.id === other.geometry.id
|
|
148
198
|
&& this.material.id === other.material.id
|
|
199
|
+
&& this.draw_method === other.draw_method
|
|
200
|
+
&& this.mode === other.mode
|
|
201
|
+
&& (this.flags & FLAG_SET_EQUALITY) === (other.flags & FLAG_SET_EQUALITY)
|
|
149
202
|
;
|
|
150
203
|
}
|
|
151
204
|
|
|
@@ -171,7 +224,7 @@ export class ShadedGeometry {
|
|
|
171
224
|
* @returns {LeafNode<ShadedGeometry>}
|
|
172
225
|
*/
|
|
173
226
|
get bvh() {
|
|
174
|
-
|
|
227
|
+
throw new Error('Deprecated, use #getBoundingBox method instead');
|
|
175
228
|
}
|
|
176
229
|
|
|
177
230
|
/**
|
|
@@ -217,11 +217,6 @@ export class ShadedGeometrySystem extends System {
|
|
|
217
217
|
t.toMatrix4(sg.__transform);
|
|
218
218
|
sg.update_bounds();
|
|
219
219
|
|
|
220
|
-
const leaf_node = sg.bvh;
|
|
221
|
-
|
|
222
|
-
// record entity on the BVH leaf
|
|
223
|
-
leaf_node.entity = entity;
|
|
224
|
-
|
|
225
220
|
// remember entity for lookups
|
|
226
221
|
sg.__entity = entity;
|
|
227
222
|
|
|
@@ -256,9 +251,14 @@ export class ShadedGeometrySystem extends System {
|
|
|
256
251
|
|
|
257
252
|
// disconnect BVH
|
|
258
253
|
const node_id = sg.__bvh_leaf_id;
|
|
254
|
+
|
|
259
255
|
this.__bvh_binary.remove_leaf(node_id);
|
|
260
256
|
this.__bvh_binary.release_node(node_id);
|
|
261
257
|
|
|
258
|
+
// reset BVH references
|
|
259
|
+
sg.__bvh_leaf_id = -1;
|
|
260
|
+
sg.__bvh_tree = null;
|
|
261
|
+
|
|
262
262
|
const geometry_id = sg.geometry.id;
|
|
263
263
|
const count_existing = this.__geometry_usage_counters.get(geometry_id);
|
|
264
264
|
|
|
@@ -20,8 +20,11 @@ import { randomFromArray } from "../../../../../core/math/random/randomFromArray
|
|
|
20
20
|
import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
|
|
21
21
|
import { OrbitingBehavior } from "../../../../../../model/game/util/behavior/OrbitingBehavior.js";
|
|
22
22
|
import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
MicronShadedGeometryRenderAdapter
|
|
25
|
+
} from "../../../micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js";
|
|
24
26
|
import { MicronRenderContext } from "../../../micron/render/v1/MicronRenderContext.js";
|
|
27
|
+
import Vector2 from "../../../../../core/geom/Vector2.js";
|
|
25
28
|
|
|
26
29
|
const eh = new EngineHarness();
|
|
27
30
|
|
|
@@ -65,12 +68,16 @@ async function main(engine) {
|
|
|
65
68
|
await em.addSystem(sgs);
|
|
66
69
|
await em.addSystem(new BehaviorSystem(engine));
|
|
67
70
|
|
|
71
|
+
const TERRAIN_SIZE = 200;
|
|
72
|
+
|
|
68
73
|
EngineHarness.buildBasics({
|
|
69
74
|
engine,
|
|
70
75
|
yaw: -0.8,
|
|
71
76
|
pitch: 1,
|
|
72
77
|
distance: 46,
|
|
73
|
-
|
|
78
|
+
terrainSize: new Vector2(TERRAIN_SIZE / 2, TERRAIN_SIZE / 2),
|
|
79
|
+
terrainResolution: 1,
|
|
80
|
+
focus: new Vector3(TERRAIN_SIZE / 2, 0, TERRAIN_SIZE / 2)
|
|
74
81
|
});
|
|
75
82
|
|
|
76
83
|
|
|
@@ -86,7 +93,7 @@ async function main(engine) {
|
|
|
86
93
|
|
|
87
94
|
const random = seededRandom(0);
|
|
88
95
|
|
|
89
|
-
const INSTANCE_COUNT =
|
|
96
|
+
const INSTANCE_COUNT = 100000;
|
|
90
97
|
|
|
91
98
|
const geometries = [];
|
|
92
99
|
|
|
@@ -108,12 +115,12 @@ async function main(engine) {
|
|
|
108
115
|
new EntityBuilder()
|
|
109
116
|
.add(Transform.fromJSON({
|
|
110
117
|
position: {
|
|
111
|
-
x: randomFloatBetween(random, 0,
|
|
118
|
+
x: randomFloatBetween(random, 0, TERRAIN_SIZE),
|
|
112
119
|
y: randomFloatBetween(random, 0, 10),
|
|
113
|
-
z: randomFloatBetween(random, 0,
|
|
120
|
+
z: randomFloatBetween(random, 0, TERRAIN_SIZE)
|
|
114
121
|
},
|
|
115
122
|
rotation: Quaternion.random(random),
|
|
116
|
-
scale: 0.
|
|
123
|
+
scale: 0.1
|
|
117
124
|
}))
|
|
118
125
|
.add(sg)
|
|
119
126
|
.build(ecd);
|
|
@@ -12,6 +12,7 @@ import { composeCompile } from "../../material/composeCompile.js";
|
|
|
12
12
|
import { typed_array_copy } from "../../../../core/collection/array/typed/typed_array_copy.js";
|
|
13
13
|
import { rewriteMaterial } from "./rewriteMaterial.js";
|
|
14
14
|
import { max3 } from "../../../../core/math/max3.js";
|
|
15
|
+
import { min2 } from "../../../../core/math/min2.js";
|
|
15
16
|
|
|
16
17
|
export class InstancedMeshGroup {
|
|
17
18
|
/**
|
|
@@ -217,8 +218,10 @@ export class InstancedMeshGroup {
|
|
|
217
218
|
this.setCapacity(newCapacityInteger);
|
|
218
219
|
|
|
219
220
|
} else if (
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
min2(
|
|
222
|
+
currentCapacity * this.shrinkFactor,
|
|
223
|
+
currentCapacity - this.shrinkConstant
|
|
224
|
+
) > size
|
|
222
225
|
) {
|
|
223
226
|
// shrink
|
|
224
227
|
this.setCapacity(size);
|
package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.js
CHANGED
|
@@ -61,7 +61,7 @@ export function serialize_geometry_collection(geometries, buffer) {
|
|
|
61
61
|
let geometry_occupied_count = 0;
|
|
62
62
|
const geometry_count = geometries.length;
|
|
63
63
|
for (let i = 0; i < geometry_count; i++) {
|
|
64
|
-
if (geometries[i]
|
|
64
|
+
if (geometries[i] !== undefined) {
|
|
65
65
|
geometry_occupied_count++;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -102,13 +102,14 @@ export class MicronRenderPlugin extends EnginePlugin {
|
|
|
102
102
|
* @param {THREE.WebGLRenderer} renderer
|
|
103
103
|
* @param {THREE.Camera} camera
|
|
104
104
|
* @param {THREE.Object3D[]} elements
|
|
105
|
+
* @param element_count
|
|
105
106
|
*/
|
|
106
|
-
render(renderer, camera, elements) {
|
|
107
|
+
render(renderer, camera, elements, element_count = elements.length) {
|
|
107
108
|
const vgtr = this.__vgtr;
|
|
108
109
|
|
|
109
110
|
vgtr.clear();
|
|
110
111
|
|
|
111
|
-
vgtr.attachMany(elements);
|
|
112
|
+
vgtr.attachMany(elements, element_count);
|
|
112
113
|
|
|
113
114
|
vgtr.update(renderer, camera);
|
|
114
115
|
vgtr.render();
|
|
@@ -123,7 +124,7 @@ export class MicronRenderPlugin extends EnginePlugin {
|
|
|
123
124
|
const observer = () => {
|
|
124
125
|
const ge = this.engine.graphics;
|
|
125
126
|
|
|
126
|
-
this.render(ge.renderer, view.camera, view.visible_objects.elements);
|
|
127
|
+
this.render(ge.renderer, view.camera, view.visible_objects.elements, view.visible_objects.size);
|
|
127
128
|
};
|
|
128
129
|
|
|
129
130
|
this.__view_observers.set(view, observer);
|
|
@@ -50,7 +50,7 @@ export class VGThreeRenderer {
|
|
|
50
50
|
this.ctx.builder.appendToScene([scene]);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
attachMany(objects) {
|
|
53
|
+
attachMany(objects, object_count = objects.length) {
|
|
54
54
|
array_copy(objects, 0, this.__inputs, this.__inputs.length, objects.length);
|
|
55
55
|
this.ctx.builder.appendToScene(objects);
|
|
56
56
|
}
|
|
@@ -144,9 +144,9 @@ export class ParticleEmitterSystem extends System {
|
|
|
144
144
|
self.particleEngine.shaderManager.update();
|
|
145
145
|
|
|
146
146
|
const visibleSet = renderLayer.visibleSet;
|
|
147
|
-
const
|
|
147
|
+
const visible_emitter_count = visibleSet.size;
|
|
148
148
|
|
|
149
|
-
if (
|
|
149
|
+
if (visible_emitter_count > 0) {
|
|
150
150
|
depthBuffer.referenceCount++;
|
|
151
151
|
self.graphicsEngine.on.postRender.addOne(function () {
|
|
152
152
|
depthBuffer.referenceCount--;
|
|
@@ -154,7 +154,7 @@ export class ParticleEmitterSystem extends System {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
for (let i = 0; i <
|
|
157
|
+
for (let i = 0; i < visible_emitter_count; i++) {
|
|
158
158
|
/**
|
|
159
159
|
*
|
|
160
160
|
* @type {Object3D}
|
|
@@ -724,7 +724,7 @@ export class ParticleEmitter {
|
|
|
724
724
|
|
|
725
725
|
/**
|
|
726
726
|
* PRECONDITION: no dead particles may exist in the pool. Make sure to call {@link ParticlePool#compact} before sorting
|
|
727
|
-
* @
|
|
727
|
+
* @protected
|
|
728
728
|
* @param {Camera} camera THREE.js camera object
|
|
729
729
|
*/
|
|
730
730
|
sort(camera) {
|
|
@@ -548,8 +548,10 @@ export class LightManager {
|
|
|
548
548
|
__update_visible_bvh() {
|
|
549
549
|
this.__visible_bvh_needs_update = false;
|
|
550
550
|
|
|
551
|
-
const
|
|
552
|
-
|
|
551
|
+
const visibleLights = this.__visible_lights;
|
|
552
|
+
|
|
553
|
+
const lights = visibleLights.elements;
|
|
554
|
+
const light_count = visibleLights.size;
|
|
553
555
|
|
|
554
556
|
|
|
555
557
|
const vbvh = this.__visible_bvh;
|
|
@@ -8,10 +8,14 @@ import { RenderPassType } from "../RenderPassType.js";
|
|
|
8
8
|
import { IncrementalDeltaSet } from "../visibility/IncrementalDeltaSet.js";
|
|
9
9
|
import { passThrough } from "../../../../core/function/Functions.js";
|
|
10
10
|
import { traverseBinaryNodeUsingVisitor } from "../../../../core/bvh2/traversal/traverseBinaryNodeUsingVisitor.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
ThreeClippingPlaneComputingBVHVisitor
|
|
13
|
+
} from "../../../../core/bvh2/traversal/ThreeClippingPlaneComputingBVHVisitor.js";
|
|
12
14
|
import { RenderLayerState } from "./RenderLayerState.js";
|
|
13
15
|
import { compare_three_objects } from "../../three/compare_three_objects.js";
|
|
14
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
queryBinaryNode_FrustumIntersections_Data
|
|
18
|
+
} from "../../../../core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js";
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
/**
|
|
@@ -57,6 +61,7 @@ export class RenderLayer {
|
|
|
57
61
|
/**
|
|
58
62
|
* @deprecated use CameraView's visible set instead
|
|
59
63
|
* Contains visible elements across all views, if element is in at least one view - it will be here
|
|
64
|
+
* @deprecated
|
|
60
65
|
* @type {IncrementalDeltaSet<THREE.Object3D>}
|
|
61
66
|
*/
|
|
62
67
|
this.visibleSet = new IncrementalDeltaSet(compare_three_objects);
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import Signal from "../../../../core/events/signal/Signal";
|
|
2
|
+
|
|
1
3
|
export class IncrementalDeltaSet<T> {
|
|
2
|
-
elements: T[]
|
|
3
|
-
version: number
|
|
4
|
-
size: number
|
|
4
|
+
readonly elements: T[]
|
|
5
|
+
readonly version: number
|
|
6
|
+
readonly size: number
|
|
7
|
+
|
|
8
|
+
readonly onAdded: Signal<T>
|
|
9
|
+
readonly onRemoved: Signal<T>
|
|
5
10
|
|
|
6
11
|
constructor(compare: (a: T, b: T) => number)
|
|
7
12
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { BitSet } from "../../../../core/binary/BitSet.js";
|
|
2
1
|
import Signal from "../../../../core/events/signal/Signal.js";
|
|
3
2
|
import { assert } from "../../../../core/assert.js";
|
|
4
3
|
import { IllegalStateException } from "../../../../core/fsm/exceptions/IllegalStateException.js";
|
|
5
4
|
import { binarySearchHighIndex } from "../../../../core/collection/array/binarySearchHighIndex.js";
|
|
5
|
+
import { array_shrink_to_size } from "../../../../core/collection/array/array_shrink_to_size.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @readonly
|
|
@@ -34,6 +34,26 @@ export class IncrementalDeltaSet {
|
|
|
34
34
|
*/
|
|
35
35
|
this.__elements = [];
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @type {number}
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
this.__elements_cursor = 0;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @type {V[]}
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
this.__swap_elements = [];
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* @type {number}
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
55
|
+
this.__swap_elements_count = 0;
|
|
56
|
+
|
|
37
57
|
/**
|
|
38
58
|
*
|
|
39
59
|
* @type {function(V, V): number}
|
|
@@ -47,13 +67,6 @@ export class IncrementalDeltaSet {
|
|
|
47
67
|
*/
|
|
48
68
|
this.version = 0;
|
|
49
69
|
|
|
50
|
-
/**
|
|
51
|
-
* Dirty flag set, used during the update to keep track of which elements are to be remove
|
|
52
|
-
* @type {BitSet}
|
|
53
|
-
*/
|
|
54
|
-
this.flags = new BitSet();
|
|
55
|
-
this.flags.preventShrink();
|
|
56
|
-
|
|
57
70
|
/**
|
|
58
71
|
* Number of currently held elements
|
|
59
72
|
* @type {number}
|
|
@@ -185,9 +198,7 @@ export class IncrementalDeltaSet {
|
|
|
185
198
|
return;
|
|
186
199
|
}
|
|
187
200
|
|
|
188
|
-
this.__elements.splice(0, this.size);
|
|
189
201
|
this.size = 0;
|
|
190
|
-
this.flags.reset();
|
|
191
202
|
|
|
192
203
|
this.state = IncrementalDeltaSetState.Clear;
|
|
193
204
|
|
|
@@ -195,18 +206,20 @@ export class IncrementalDeltaSet {
|
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
includeAll() {
|
|
198
|
-
|
|
199
|
-
this.flags.clearRange(0, this.size - 1);
|
|
200
|
-
}
|
|
209
|
+
throw new Error('deprecated #includeAll');
|
|
201
210
|
}
|
|
202
211
|
|
|
203
212
|
initializeUpdate() {
|
|
204
|
-
//
|
|
205
|
-
const
|
|
213
|
+
// perform swap
|
|
214
|
+
const swp_els = this.__elements;
|
|
215
|
+
this.__elements = this.__swap_elements
|
|
216
|
+
this.__swap_elements = swp_els;
|
|
206
217
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
218
|
+
const swp_count = this.size;
|
|
219
|
+
this.size = this.__swap_elements_count;
|
|
220
|
+
this.__swap_elements_count = swp_count;
|
|
221
|
+
|
|
222
|
+
this.__elements_cursor = 0;
|
|
210
223
|
|
|
211
224
|
this.state = IncrementalDeltaSetState.Building;
|
|
212
225
|
|
|
@@ -216,30 +229,41 @@ export class IncrementalDeltaSet {
|
|
|
216
229
|
finalizeUpdate() {
|
|
217
230
|
assert.equal(this.state, IncrementalDeltaSetState.Building, `Expected BUILDING state, instead got '${this.state}'`);
|
|
218
231
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
232
|
+
this.size = this.__elements_cursor;
|
|
233
|
+
|
|
234
|
+
// sort newly populated elements array
|
|
235
|
+
array_shrink_to_size(this.__elements, this.size);
|
|
236
|
+
this.__elements.sort(this.__compare);
|
|
237
|
+
|
|
238
|
+
let i0 = 0, i1 = 0;
|
|
239
|
+
while (i0 < this.size && i1 < this.__swap_elements_count) {
|
|
240
|
+
const el_0 = this.__elements[i0];
|
|
241
|
+
const el_1 = this.__swap_elements[i1];
|
|
242
|
+
|
|
243
|
+
const diff = this.__compare(el_0, el_1);
|
|
244
|
+
|
|
245
|
+
if (diff === 0) {
|
|
246
|
+
i0++;
|
|
247
|
+
i1++;
|
|
248
|
+
// found match
|
|
249
|
+
} else if (diff < 0) {
|
|
250
|
+
i0++;
|
|
251
|
+
// addition
|
|
252
|
+
this.onAdded.send1(el_0);
|
|
253
|
+
} else {
|
|
254
|
+
i1++;
|
|
255
|
+
// removal
|
|
256
|
+
this.onRemoved.send1(el_1);
|
|
257
|
+
}
|
|
237
258
|
}
|
|
238
259
|
|
|
239
|
-
//
|
|
240
|
-
this.size
|
|
241
|
-
|
|
242
|
-
|
|
260
|
+
// process remainders
|
|
261
|
+
for (; i0 < this.size; i0++) {
|
|
262
|
+
this.onAdded.send1(this.__elements[i0]);
|
|
263
|
+
}
|
|
264
|
+
for (; i1 < this.__swap_elements_count; i1++) {
|
|
265
|
+
this.onRemoved.send1(this.__swap_elements[i1]);
|
|
266
|
+
}
|
|
243
267
|
|
|
244
268
|
this.state = IncrementalDeltaSetState.Ready;
|
|
245
269
|
|
|
@@ -254,30 +278,7 @@ export class IncrementalDeltaSet {
|
|
|
254
278
|
push(element) {
|
|
255
279
|
const elements = this.__elements;
|
|
256
280
|
|
|
257
|
-
|
|
258
|
-
const existing_size = this.size;
|
|
259
|
-
const index = binarySearchHighIndex(elements, element, this.__compare, 0, existing_size - 1);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const flags = this.flags;
|
|
263
|
-
|
|
264
|
-
if (elements[index] !== element) {
|
|
265
|
-
// element is not in the set
|
|
266
|
-
|
|
267
|
-
// shift flags to make space for it
|
|
268
|
-
flags.shift_right(1, index, existing_size);
|
|
269
|
-
|
|
270
|
-
// insert
|
|
271
|
-
elements.splice(index, 0, element);
|
|
272
|
-
|
|
273
|
-
this.size = existing_size + 1;
|
|
274
|
-
|
|
275
|
-
this.onAdded.send1(element);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// clear flag for inserted element
|
|
279
|
-
flags.clear(index);
|
|
280
|
-
|
|
281
|
+
elements[this.__elements_cursor++] = element;
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
/**
|
|
@@ -44,3 +44,42 @@ test('set is sorted after insert, push 3,2,1', () => {
|
|
|
44
44
|
expect(set.elements[1]).toBe(2);
|
|
45
45
|
expect(set.elements[2]).toBe(3);
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
test('add/remove callbacks', () => {
|
|
50
|
+
const set = new IncrementalDeltaSet(compareNumbersAscending);
|
|
51
|
+
|
|
52
|
+
const add_listener = jest.fn();
|
|
53
|
+
const remove_listener = jest.fn();
|
|
54
|
+
|
|
55
|
+
set.onAdded.add(add_listener);
|
|
56
|
+
set.onRemoved.add(remove_listener);
|
|
57
|
+
|
|
58
|
+
set.initializeUpdate();
|
|
59
|
+
|
|
60
|
+
set.push(1);
|
|
61
|
+
set.push(2);
|
|
62
|
+
set.push(3);
|
|
63
|
+
|
|
64
|
+
set.finalizeUpdate();
|
|
65
|
+
|
|
66
|
+
expect(add_listener).toHaveBeenCalledWith(1);
|
|
67
|
+
expect(add_listener).toHaveBeenCalledWith(2);
|
|
68
|
+
expect(add_listener).toHaveBeenCalledWith(3);
|
|
69
|
+
|
|
70
|
+
expect(add_listener).toHaveBeenCalledTimes(3);
|
|
71
|
+
|
|
72
|
+
expect(remove_listener).not.toHaveBeenCalled();
|
|
73
|
+
|
|
74
|
+
set.initializeUpdate();
|
|
75
|
+
|
|
76
|
+
set.push(1);
|
|
77
|
+
set.push(3);
|
|
78
|
+
|
|
79
|
+
set.finalizeUpdate();
|
|
80
|
+
|
|
81
|
+
expect(add_listener).toHaveBeenCalledTimes(3);
|
|
82
|
+
|
|
83
|
+
expect(remove_listener).toHaveBeenCalledWith(2);
|
|
84
|
+
expect(remove_listener).toHaveBeenCalledTimes(1);
|
|
85
|
+
});
|
|
@@ -78,6 +78,7 @@ export class ShadowMapRenderer {
|
|
|
78
78
|
const __autoClearColor = renderer.autoClearColor;
|
|
79
79
|
const __autoClearDepth = renderer.autoClearDepth;
|
|
80
80
|
const __autoClearStencil = renderer.autoClearStencil;
|
|
81
|
+
const __sortObjects = renderer.sortObjects;
|
|
81
82
|
|
|
82
83
|
const target = shadow.map;
|
|
83
84
|
|
|
@@ -89,6 +90,9 @@ export class ShadowMapRenderer {
|
|
|
89
90
|
renderer.autoClearDepth = false;
|
|
90
91
|
renderer.autoClearStencil = false;
|
|
91
92
|
|
|
93
|
+
// disable sorting, unnecessary since fragment cost is very low and sorting is expensive for large number of objects
|
|
94
|
+
renderer.sortObjects = false;
|
|
95
|
+
|
|
92
96
|
renderer.clear(true, true, false);
|
|
93
97
|
|
|
94
98
|
shadow.updateMatrices(light, 0);
|
|
@@ -115,6 +119,7 @@ export class ShadowMapRenderer {
|
|
|
115
119
|
renderer.autoClearColor = __autoClearColor;
|
|
116
120
|
renderer.autoClearDepth = __autoClearDepth;
|
|
117
121
|
renderer.autoClearStencil = __autoClearStencil;
|
|
122
|
+
renderer.sortObjects = __sortObjects;
|
|
118
123
|
|
|
119
124
|
// increment frame index to signal to the renderer that geometries might need to be updated
|
|
120
125
|
renderer.info.render.frame++;
|
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.24",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|