@woosh/meep-engine 2.43.53 → 2.44.1
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/cache/Cache.d.ts +3 -0
- package/core/geom/3d/frustum/read_cluster_frustum_corners.js +46 -0
- package/core/geom/3d/frustum/slice_frustum_linear_to_points.js +92 -0
- package/core/model/node-graph/NodeGraph.js +16 -0
- package/core/model/node-graph/node/NodeDescription.js +29 -0
- package/core/model/object/compareObjectsByNumericId.js +9 -0
- package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +4 -2
- package/engine/graphics/render/forward_plus/LightManager.js +277 -277
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +3 -62
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_APPLY_DECALS.js +70 -0
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_LOAD_METADATA.js +11 -0
- package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +10 -3
- package/engine/graphics/render/forward_plus/materials/fp_build_vertex_lighting_shared.js +5 -2
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -3
- package/engine/intelligence/behavior/SelectorBehavior.js +36 -6
- package/engine/sound/sopra/AbstractAudioClip.js +29 -0
- package/engine/sound/sopra/ContainerAudioClip.js +13 -0
- package/engine/sound/sopra/README.md +1 -0
- package/engine/sound/sopra/RandomContainerAudioClip.js +15 -0
- package/engine/sound/sopra/SequenceContainerAudioClip.js +8 -0
- package/engine/sound/sopra/SilenceAudioClip.js +15 -0
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
LinearMipMapLinearFilter,
|
|
10
10
|
NearestFilter,
|
|
11
11
|
RGBAFormat,
|
|
12
|
-
|
|
12
|
+
RGBAIntegerFormat,
|
|
13
13
|
UnsignedByteType,
|
|
14
14
|
UnsignedShortType
|
|
15
15
|
} from "three";
|
|
@@ -31,7 +31,6 @@ import { computeDataType } from "./data/computeDataType.js";
|
|
|
31
31
|
import { computeThreeTextureTypeFromDataType } from "./data/computeThreeTextureTypeFromDataType.js";
|
|
32
32
|
import { computeThreeTextureInternalFormatFromDataType } from "./data/computeThreeTextureInternalFormatFromDataType.js";
|
|
33
33
|
import { testClusterEquality } from "./testClusterEquality.js";
|
|
34
|
-
import { read_frustum_corner } from "./read_frustum_corner.js";
|
|
35
34
|
import { BinaryUint32BVH } from "../../../../core/bvh2/binary/2/BinaryUint32BVH.js";
|
|
36
35
|
import { query_bvh_frustum_from_texture } from "./query/query_bvh_frustum_from_texture.js";
|
|
37
36
|
import { mat4 } from "gl-matrix";
|
|
@@ -44,9 +43,11 @@ import { v3_morton_encode_transformed } from "../../../../core/geom/3d/morton/v3
|
|
|
44
43
|
import { v3_distance } from "../../../../core/geom/v3_distance.js";
|
|
45
44
|
import { array_copy } from "../../../../core/collection/array/copyArray.js";
|
|
46
45
|
import { arrayQuickSort } from "../../../../core/collection/array/arrayQuickSort.js";
|
|
47
|
-
import { invokeObjectCompare } from "../../../../core/model/object/invokeObjectCompare.js";
|
|
48
46
|
import { frustum_from_camera } from "../../ecs/camera/frustum_from_camera.js";
|
|
49
47
|
import { arraySwapElements } from "../../../../core/collection/array/arraySwapElements.js";
|
|
48
|
+
import { compareObjectsByNumericId } from "../../../../core/model/object/compareObjectsByNumericId.js";
|
|
49
|
+
import { slice_frustum_linear_to_points } from "../../../../core/geom/3d/frustum/slice_frustum_linear_to_points.js";
|
|
50
|
+
import { read_cluster_frustum_corners } from "../../../../core/geom/3d/frustum/read_cluster_frustum_corners.js";
|
|
50
51
|
|
|
51
52
|
const LOOKUP_CACHE_SIZE = 1024; // must be power of two
|
|
52
53
|
|
|
@@ -120,6 +121,121 @@ function encode_light_descriptor(address, light) {
|
|
|
120
121
|
return light_type | (address << 2);
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
function assign_cluster(
|
|
125
|
+
tile_texture_data_offset,
|
|
126
|
+
light_bvh,
|
|
127
|
+
lookup_address_offset,
|
|
128
|
+
light_lookup_texture_data,
|
|
129
|
+
tile_texture_data,
|
|
130
|
+
light_source_data
|
|
131
|
+
) {
|
|
132
|
+
|
|
133
|
+
// at this point we have a fully constructed cluster, we can query lights within the cluster
|
|
134
|
+
const match_count = query_bvh_frustum_from_texture(
|
|
135
|
+
scratch_light_nodes,
|
|
136
|
+
0,
|
|
137
|
+
light_bvh,
|
|
138
|
+
light_source_data,
|
|
139
|
+
scratch_frustum_planes,
|
|
140
|
+
scratch_corners
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// construct tile data
|
|
144
|
+
|
|
145
|
+
// write number of light entries
|
|
146
|
+
tile_texture_data[tile_texture_data_offset + 1] = match_count;
|
|
147
|
+
|
|
148
|
+
if (match_count === 0) {
|
|
149
|
+
// don't waste time on assignment, there are no lights in the cluster
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
We can sort lights by index to get higher cache utilization, but it turns out quite costly.
|
|
155
|
+
Through experimentation, sorting was taking around 20% of the whole assignment time
|
|
156
|
+
while improving number of cache hits by around 40% (8000 without sorting -> 11000 with sorting)
|
|
157
|
+
*/
|
|
158
|
+
const cluster_data_hash = compute_light_data_hash_0(scratch_light_nodes, match_count) & (LOOKUP_CACHE_SIZE - 1);
|
|
159
|
+
|
|
160
|
+
const existing_lookup_address = LOOKUP_CACHE[cluster_data_hash];
|
|
161
|
+
|
|
162
|
+
if (existing_lookup_address !== 0xFFFFFFFF) {
|
|
163
|
+
// found a hash match, validate equality
|
|
164
|
+
const existing_lookup_entry_match = testClusterEquality(
|
|
165
|
+
scratch_light_nodes,
|
|
166
|
+
match_count,
|
|
167
|
+
light_lookup_texture_data,
|
|
168
|
+
existing_lookup_address
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (existing_lookup_entry_match) {
|
|
172
|
+
tile_texture_data[tile_texture_data_offset] = existing_lookup_address;
|
|
173
|
+
|
|
174
|
+
//hash_reuse_count++;
|
|
175
|
+
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
// no hash entry exists, create one
|
|
180
|
+
LOOKUP_CACHE[cluster_data_hash] = lookup_address_offset;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// if we get to this point, that means no hash match was found, or equality check was failed
|
|
184
|
+
|
|
185
|
+
// write address of the lookup table
|
|
186
|
+
tile_texture_data[tile_texture_data_offset] = lookup_address_offset;
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < match_count; i++) {
|
|
189
|
+
/**
|
|
190
|
+
*
|
|
191
|
+
* @type {number}
|
|
192
|
+
*/
|
|
193
|
+
const light_descriptor = scratch_light_nodes[i];
|
|
194
|
+
|
|
195
|
+
// construct lookup table
|
|
196
|
+
light_lookup_texture_data[lookup_address_offset + i] = light_descriptor;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return match_count;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
*
|
|
204
|
+
* @param {IncrementalDeltaSet<PointLightData>} data_set
|
|
205
|
+
* @param {BinaryUint32BVH} bvh
|
|
206
|
+
*/
|
|
207
|
+
function build_light_data_bvh(data_set, bvh) {
|
|
208
|
+
const elements = data_set.elements;
|
|
209
|
+
const element_count = data_set.size;
|
|
210
|
+
|
|
211
|
+
bvh.setLeafCount(element_count);
|
|
212
|
+
bvh.initialize_structure();
|
|
213
|
+
|
|
214
|
+
for (let i = 0; i < element_count; i++) {
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
*
|
|
218
|
+
* @type {PointLightData}
|
|
219
|
+
*/
|
|
220
|
+
const datum = elements[i];
|
|
221
|
+
|
|
222
|
+
const bb = datum.bvh_leaf_main;
|
|
223
|
+
|
|
224
|
+
const payload = encode_light_descriptor(datum.address, datum.light);
|
|
225
|
+
|
|
226
|
+
bvh.setLeafData(
|
|
227
|
+
i, payload,
|
|
228
|
+
bb.x0, bb.y0, bb.z0,
|
|
229
|
+
bb.x1, bb.y1, bb.z1
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// vbvh.sort_morton(this.__projection_matrix);
|
|
234
|
+
// vbvh.sort_bubble_sah();
|
|
235
|
+
|
|
236
|
+
bvh.build();
|
|
237
|
+
}
|
|
238
|
+
|
|
123
239
|
export class LightManager {
|
|
124
240
|
|
|
125
241
|
constructor() {
|
|
@@ -128,14 +244,14 @@ export class LightManager {
|
|
|
128
244
|
* @type {BinaryNode<PointLightData>}
|
|
129
245
|
* @private
|
|
130
246
|
*/
|
|
131
|
-
this.
|
|
247
|
+
this.__light_data_bvh = new BinaryNode();
|
|
132
248
|
|
|
133
249
|
/**
|
|
134
250
|
*
|
|
135
251
|
* @type {Map<AbstractLight, PointLightData>}
|
|
136
252
|
* @private
|
|
137
253
|
*/
|
|
138
|
-
this.
|
|
254
|
+
this.__light_data_map = new Map();
|
|
139
255
|
|
|
140
256
|
/**
|
|
141
257
|
* Number of cluster slices along each dimension
|
|
@@ -146,8 +262,18 @@ export class LightManager {
|
|
|
146
262
|
|
|
147
263
|
this.__cluster_texture_precision = 8;
|
|
148
264
|
this.__cluster_texture_needs_rebuild = false;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Texel layout:
|
|
268
|
+
* - R: offset into lookup texture for lights
|
|
269
|
+
* - G: light count
|
|
270
|
+
* - B: offset into lookup texture for decals
|
|
271
|
+
* - A: decal count
|
|
272
|
+
* @type {DataTexture3D}
|
|
273
|
+
* @private
|
|
274
|
+
*/
|
|
149
275
|
this.__cluster_texture = new DataTexture3D(
|
|
150
|
-
new Uint16Array(
|
|
276
|
+
new Uint16Array(4),
|
|
151
277
|
1,
|
|
152
278
|
1,
|
|
153
279
|
1,
|
|
@@ -160,8 +286,8 @@ export class LightManager {
|
|
|
160
286
|
this.__cluster_texture.minFilter = NearestFilter;
|
|
161
287
|
|
|
162
288
|
this.__cluster_texture.type = UnsignedShortType;
|
|
163
|
-
this.__cluster_texture.format =
|
|
164
|
-
this.__cluster_texture.internalFormat = "
|
|
289
|
+
this.__cluster_texture.format = RGBAIntegerFormat;
|
|
290
|
+
this.__cluster_texture.internalFormat = "RGBA16UI";
|
|
165
291
|
|
|
166
292
|
/**
|
|
167
293
|
*
|
|
@@ -232,7 +358,15 @@ export class LightManager {
|
|
|
232
358
|
* @readonly
|
|
233
359
|
* @private
|
|
234
360
|
*/
|
|
235
|
-
this.__visible_lights = new IncrementalDeltaSet(
|
|
361
|
+
this.__visible_lights = new IncrementalDeltaSet(compareObjectsByNumericId);
|
|
362
|
+
/**
|
|
363
|
+
*
|
|
364
|
+
* @type {IncrementalDeltaSet<PointLightData>}
|
|
365
|
+
* @readonly
|
|
366
|
+
* @private
|
|
367
|
+
*/
|
|
368
|
+
this.__visible_decals = new IncrementalDeltaSet(compareObjectsByNumericId);
|
|
369
|
+
|
|
236
370
|
/**
|
|
237
371
|
*
|
|
238
372
|
* @type {PointLightData[]}
|
|
@@ -245,7 +379,13 @@ export class LightManager {
|
|
|
245
379
|
* @type {BinaryUint32BVH}
|
|
246
380
|
* @private
|
|
247
381
|
*/
|
|
248
|
-
this.
|
|
382
|
+
this.__visible_bvh_lights = new BinaryUint32BVH();
|
|
383
|
+
/**
|
|
384
|
+
*
|
|
385
|
+
* @type {BinaryUint32BVH}
|
|
386
|
+
* @private
|
|
387
|
+
*/
|
|
388
|
+
this.__visible_bvh_decals = new BinaryUint32BVH();
|
|
249
389
|
|
|
250
390
|
/**
|
|
251
391
|
*
|
|
@@ -285,8 +425,11 @@ export class LightManager {
|
|
|
285
425
|
|
|
286
426
|
this.setTileMapResolution(32, 16, 16);
|
|
287
427
|
|
|
288
|
-
this.__visible_lights.onAdded.add(this.
|
|
289
|
-
this.__visible_lights.onRemoved.add(this.
|
|
428
|
+
this.__visible_lights.onAdded.add(this.__handle_visible_light_added, this);
|
|
429
|
+
this.__visible_lights.onRemoved.add(this.__handle_visible_light_removed, this);
|
|
430
|
+
|
|
431
|
+
this.__visible_decals.onAdded.add(this.__handle_visible_decal_added, this);
|
|
432
|
+
this.__visible_decals.onRemoved.add(this.__handle_visible_decal_removed, this);
|
|
290
433
|
|
|
291
434
|
/**
|
|
292
435
|
*
|
|
@@ -301,6 +444,8 @@ export class LightManager {
|
|
|
301
444
|
* @private
|
|
302
445
|
*/
|
|
303
446
|
this.__visible_bvh_needs_update = true;
|
|
447
|
+
|
|
448
|
+
window.light_manager = this; // DEBUG
|
|
304
449
|
}
|
|
305
450
|
|
|
306
451
|
/**
|
|
@@ -376,7 +521,7 @@ export class LightManager {
|
|
|
376
521
|
|
|
377
522
|
const texture = this.__cluster_texture;
|
|
378
523
|
|
|
379
|
-
const channelCount =
|
|
524
|
+
const channelCount = 4;
|
|
380
525
|
|
|
381
526
|
texture.dispose();
|
|
382
527
|
|
|
@@ -464,29 +609,31 @@ export class LightManager {
|
|
|
464
609
|
* @param {PointLightData} data
|
|
465
610
|
* @private
|
|
466
611
|
*/
|
|
467
|
-
|
|
612
|
+
__handle_visible_decal_added(data) {
|
|
468
613
|
this.__light_data_needs_update = true;
|
|
469
614
|
|
|
615
|
+
/**
|
|
616
|
+
*
|
|
617
|
+
* @type {Decal}
|
|
618
|
+
*/
|
|
470
619
|
const light = data.light;
|
|
471
620
|
|
|
472
621
|
light.onDimensionChanged(this.__handle_light_dimensions_change, this);
|
|
473
622
|
|
|
474
|
-
|
|
475
|
-
const ref = this.__decal_patch_references.acquire(light.texture_diffuse);
|
|
623
|
+
const ref = this.__decal_patch_references.acquire(light.texture_diffuse);
|
|
476
624
|
|
|
477
|
-
|
|
478
|
-
|
|
625
|
+
const patch = ref.getValue();
|
|
626
|
+
const patch_uv = patch.uv;
|
|
479
627
|
|
|
480
|
-
|
|
481
|
-
|
|
628
|
+
patch_uv.position.onChanged.add(light.handleUvPositionChange, light);
|
|
629
|
+
patch_uv.size.onChanged.add(light.handleUvSizeChange, light);
|
|
482
630
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
631
|
+
light.uv[0] = patch_uv.position.x;
|
|
632
|
+
light.uv[1] = patch_uv.position.y;
|
|
633
|
+
light.uv[2] = patch_uv.size.x;
|
|
634
|
+
light.uv[3] = patch_uv.size.y;
|
|
487
635
|
|
|
488
|
-
|
|
489
|
-
}
|
|
636
|
+
this.__decal_references.set(light, ref);
|
|
490
637
|
}
|
|
491
638
|
|
|
492
639
|
/**
|
|
@@ -494,34 +641,61 @@ export class LightManager {
|
|
|
494
641
|
* @param {PointLightData} data
|
|
495
642
|
* @private
|
|
496
643
|
*/
|
|
497
|
-
|
|
644
|
+
__handle_visible_decal_removed(data) {
|
|
498
645
|
this.__light_data_needs_update = true;
|
|
499
646
|
|
|
647
|
+
/**
|
|
648
|
+
*
|
|
649
|
+
* @type {Decal}
|
|
650
|
+
*/
|
|
500
651
|
const light = data.light;
|
|
652
|
+
|
|
501
653
|
light.offDimensionChanged(this.__handle_light_dimensions_change, this);
|
|
502
654
|
|
|
503
|
-
|
|
504
|
-
const ref = this.__decal_references.get(light);
|
|
655
|
+
const ref = this.__decal_references.get(light);
|
|
505
656
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
657
|
+
if (ref === undefined) {
|
|
658
|
+
// This can occur when decal changes while being in the visible list, messing with the IncrementalDeltaSet's compare order
|
|
659
|
+
console.warn(`Decal reference not found: ${light}`);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
512
662
|
|
|
513
|
-
|
|
514
|
-
|
|
663
|
+
const patch = ref.getValue();
|
|
664
|
+
const patch_uv = patch.uv;
|
|
515
665
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
666
|
+
// unsubscribe
|
|
667
|
+
patch_uv.position.onChanged.remove(light.handleUvPositionChange, light);
|
|
668
|
+
patch_uv.size.onChanged.remove(light.handleUvSizeChange, light);
|
|
519
669
|
|
|
520
670
|
|
|
521
|
-
|
|
671
|
+
ref.release();
|
|
522
672
|
|
|
523
|
-
|
|
524
|
-
|
|
673
|
+
this.__decal_references.delete(light);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
*
|
|
678
|
+
* @param {PointLightData} data
|
|
679
|
+
* @private
|
|
680
|
+
*/
|
|
681
|
+
__handle_visible_light_added(data) {
|
|
682
|
+
this.__light_data_needs_update = true;
|
|
683
|
+
|
|
684
|
+
const light = data.light;
|
|
685
|
+
|
|
686
|
+
light.onDimensionChanged(this.__handle_light_dimensions_change, this);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
*
|
|
691
|
+
* @param {PointLightData} data
|
|
692
|
+
* @private
|
|
693
|
+
*/
|
|
694
|
+
__handle_visible_light_removed(data) {
|
|
695
|
+
this.__light_data_needs_update = true;
|
|
696
|
+
|
|
697
|
+
const light = data.light;
|
|
698
|
+
light.offDimensionChanged(this.__handle_light_dimensions_change, this);
|
|
525
699
|
}
|
|
526
700
|
|
|
527
701
|
/**
|
|
@@ -589,57 +763,38 @@ export class LightManager {
|
|
|
589
763
|
*/
|
|
590
764
|
__sort_visible_light() {
|
|
591
765
|
const sorted_lights = this.__sorted_visible_lights;
|
|
766
|
+
|
|
592
767
|
const visible_light_set = this.__visible_lights;
|
|
593
768
|
const visible_light_count = visible_light_set.size;
|
|
594
769
|
|
|
595
|
-
|
|
770
|
+
const visible_decal_set = this.__visible_decals;
|
|
771
|
+
const visible_decal_count = visible_decal_set.size;
|
|
772
|
+
|
|
773
|
+
const expected_sorted_size = visible_light_count + visible_decal_count;
|
|
774
|
+
|
|
775
|
+
if (expected_sorted_size > visible_light_count) {
|
|
596
776
|
// crop array if it's too big
|
|
597
|
-
sorted_lights.splice(visible_light_count,
|
|
777
|
+
sorted_lights.splice(visible_light_count, expected_sorted_size - visible_light_count)
|
|
598
778
|
}
|
|
599
779
|
|
|
600
780
|
array_copy(visible_light_set.elements, 0, sorted_lights, 0, visible_light_count);
|
|
781
|
+
array_copy(visible_decal_set.elements, 0, sorted_lights, visible_light_count, visible_decal_count);
|
|
601
782
|
|
|
602
|
-
arrayQuickSort(sorted_lights, this.__sort_visible_light_score, this, 0,
|
|
783
|
+
arrayQuickSort(sorted_lights, this.__sort_visible_light_score, this, 0, expected_sorted_size - 1);
|
|
603
784
|
}
|
|
604
785
|
|
|
605
786
|
__update_visible_bvh() {
|
|
606
787
|
this.__visible_bvh_needs_update = false;
|
|
607
788
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const vbvh = this.__visible_bvh;
|
|
615
|
-
|
|
616
|
-
vbvh.setLeafCount(light_count);
|
|
617
|
-
vbvh.initialize_structure();
|
|
618
|
-
|
|
619
|
-
for (let i = 0; i < light_count; i++) {
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
*
|
|
623
|
-
* @type {PointLightData}
|
|
624
|
-
*/
|
|
625
|
-
const datum = lights[i];
|
|
626
|
-
|
|
627
|
-
const bb = datum.bvh_leaf_main;
|
|
628
|
-
|
|
629
|
-
const payload = encode_light_descriptor(datum.address, datum.light);
|
|
630
|
-
|
|
631
|
-
vbvh.setLeafData(
|
|
632
|
-
i, payload,
|
|
633
|
-
bb.x0, bb.y0, bb.z0,
|
|
634
|
-
bb.x1, bb.y1, bb.z1
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// vbvh.sort_morton(this.__projection_matrix);
|
|
639
|
-
// vbvh.sort_bubble_sah();
|
|
640
|
-
|
|
641
|
-
vbvh.build();
|
|
789
|
+
build_light_data_bvh(
|
|
790
|
+
this.__visible_lights,
|
|
791
|
+
this.__visible_bvh_lights
|
|
792
|
+
);
|
|
642
793
|
|
|
794
|
+
build_light_data_bvh(
|
|
795
|
+
this.__visible_decals,
|
|
796
|
+
this.__visible_bvh_decals
|
|
797
|
+
);
|
|
643
798
|
}
|
|
644
799
|
|
|
645
800
|
|
|
@@ -666,11 +821,15 @@ export class LightManager {
|
|
|
666
821
|
|
|
667
822
|
visible_lights.initializeUpdate();
|
|
668
823
|
|
|
824
|
+
const visible_decals = this.__visible_decals;
|
|
825
|
+
|
|
826
|
+
visible_decals.initializeUpdate();
|
|
827
|
+
|
|
669
828
|
const nodes = [];
|
|
670
829
|
|
|
671
830
|
read_frustum_planes_to_array(this.__view_frustum.planes, scratch_frustum_planes)
|
|
672
831
|
|
|
673
|
-
const match_count = query_bvh_frustum_from_objects(nodes, 0, this.
|
|
832
|
+
const match_count = query_bvh_frustum_from_objects(nodes, 0, this.__light_data_bvh, scratch_frustum_planes, this.__view_frustum_points);
|
|
674
833
|
|
|
675
834
|
for (let i = 0; i < match_count; i++) {
|
|
676
835
|
|
|
@@ -680,11 +839,18 @@ export class LightManager {
|
|
|
680
839
|
*/
|
|
681
840
|
const light_data = nodes[i];
|
|
682
841
|
|
|
683
|
-
|
|
842
|
+
if (light_data.light instanceof Decal) {
|
|
843
|
+
// decals go into a separate bucket
|
|
844
|
+
visible_decals.push(light_data);
|
|
845
|
+
} else {
|
|
846
|
+
visible_lights.push(light_data);
|
|
847
|
+
}
|
|
848
|
+
|
|
684
849
|
}
|
|
685
850
|
|
|
686
851
|
visible_lights.finalizeUpdate();
|
|
687
852
|
|
|
853
|
+
visible_decals.finalizeUpdate();
|
|
688
854
|
}
|
|
689
855
|
|
|
690
856
|
__write_light_data_texture() {
|
|
@@ -792,7 +958,8 @@ export class LightManager {
|
|
|
792
958
|
*/
|
|
793
959
|
const cluster_planes = this.__cluster_planes;
|
|
794
960
|
|
|
795
|
-
const
|
|
961
|
+
const bvh_lights = this.__visible_bvh_lights;
|
|
962
|
+
const bvh_decals = this.__visible_bvh_decals;
|
|
796
963
|
|
|
797
964
|
|
|
798
965
|
const cluster_planes_x_offset = 0;
|
|
@@ -817,12 +984,8 @@ export class LightManager {
|
|
|
817
984
|
|
|
818
985
|
const cluster_frustum_points = this.__cluster_frustum_points;
|
|
819
986
|
|
|
820
|
-
let assignment_count = 0;
|
|
821
|
-
let hash_reuse_count = 0;
|
|
822
|
-
|
|
823
987
|
let lookup_address_offset = 0;
|
|
824
988
|
let i_x_0, i_y_0, i_z_0;
|
|
825
|
-
let i;
|
|
826
989
|
|
|
827
990
|
// clear cache
|
|
828
991
|
LOOKUP_CACHE.fill(0xFFFFFFFF);
|
|
@@ -830,7 +993,6 @@ export class LightManager {
|
|
|
830
993
|
// console.log('Assignment Start');
|
|
831
994
|
|
|
832
995
|
for (i_z_0 = 0; i_z_0 < tr_z; i_z_0++) {
|
|
833
|
-
const i_z_1 = i_z_0 + 1;
|
|
834
996
|
|
|
835
997
|
// construct z planes
|
|
836
998
|
|
|
@@ -841,7 +1003,6 @@ export class LightManager {
|
|
|
841
1003
|
read_plane_pair(cluster_planes, z_plane_index_offset_0, scratch_frustum_planes, 16);
|
|
842
1004
|
|
|
843
1005
|
for (i_y_0 = 0; i_y_0 < tr_y; i_y_0++) {
|
|
844
|
-
const i_y_1 = i_y_0 + 1;
|
|
845
1006
|
|
|
846
1007
|
// construct y planes
|
|
847
1008
|
|
|
@@ -857,109 +1018,29 @@ export class LightManager {
|
|
|
857
1018
|
|
|
858
1019
|
read_plane_pair(cluster_planes, x_plane_index_offset_0, scratch_frustum_planes, 0);
|
|
859
1020
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const frustum_point_index_000 = i_z_0 * tr_xy_1 + i_y_0 * tr_x_1 + i_x_0;
|
|
863
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_000, scratch_corners, 0);
|
|
864
|
-
|
|
865
|
-
const i_x_1 = i_x_0 + 1;
|
|
866
|
-
|
|
867
|
-
const frustum_point_index_100 = i_z_0 * tr_xy_1 + i_y_0 * tr_x_1 + i_x_1;
|
|
868
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_100, scratch_corners, 12);
|
|
869
|
-
|
|
870
|
-
const frustum_point_index_010 = i_z_0 * tr_xy_1 + i_y_1 * tr_x_1 + i_x_0;
|
|
871
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_010, scratch_corners, 6);
|
|
872
|
-
|
|
873
|
-
const frustum_point_index_110 = i_z_0 * tr_xy_1 + i_y_1 * tr_x_1 + i_x_1;
|
|
874
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_110, scratch_corners, 18);
|
|
875
|
-
|
|
876
|
-
const frustum_point_index_001 = i_z_1 * tr_xy_1 + i_y_0 * tr_x_1 + i_x_0;
|
|
877
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_001, scratch_corners, 3);
|
|
878
|
-
|
|
879
|
-
const frustum_point_index_101 = i_z_1 * tr_xy_1 + i_y_0 * tr_x_1 + i_x_1;
|
|
880
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_101, scratch_corners, 15);
|
|
881
|
-
|
|
882
|
-
const frustum_point_index_011 = i_z_1 * tr_xy_1 + i_y_1 * tr_x_1 + i_x_0;
|
|
883
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_011, scratch_corners, 9);
|
|
884
|
-
|
|
885
|
-
const frustum_point_index_111 = i_z_1 * tr_xy_1 + i_y_1 * tr_x_1 + i_x_1;
|
|
886
|
-
read_frustum_corner(cluster_frustum_points, frustum_point_index_111, scratch_corners, 21);
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
// at this point we have a fully constructed cluster, we can query lights within the cluster
|
|
890
|
-
const match_count = query_bvh_frustum_from_texture(
|
|
891
|
-
scratch_light_nodes,
|
|
892
|
-
0,
|
|
893
|
-
bvh,
|
|
894
|
-
light_source_data,
|
|
895
|
-
scratch_frustum_planes,
|
|
896
|
-
scratch_corners
|
|
897
|
-
);
|
|
1021
|
+
read_cluster_frustum_corners(scratch_corners, i_z_0, tr_xy_1, i_y_0, tr_x_1, i_x_0, cluster_frustum_points);
|
|
898
1022
|
|
|
899
1023
|
const tile_index = slice_offset + (i_y_0 * tr_x) + i_x_0;
|
|
900
1024
|
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
// construct tile data
|
|
905
|
-
|
|
906
|
-
// write number of light entries
|
|
907
|
-
tile_texture_data[tile_address + 1] = match_count;
|
|
908
|
-
|
|
909
|
-
if (match_count === 0) {
|
|
910
|
-
// don't waste time on assignment, there are no lights in the cluster
|
|
911
|
-
continue;
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/*
|
|
915
|
-
We can sort lights by index to get higher cache utilization, but it turns out quite costly.
|
|
916
|
-
Through experimentation, sorting was taking around 20% of the whole assignment time
|
|
917
|
-
while improving number of cache hits by around 40% (8000 without sorting -> 11000 with sorting)
|
|
918
|
-
*/
|
|
919
|
-
const cluster_data_hash = compute_light_data_hash_0(scratch_light_nodes, match_count) & (LOOKUP_CACHE_SIZE - 1);
|
|
920
|
-
|
|
921
|
-
const existing_lookup_address = LOOKUP_CACHE[cluster_data_hash];
|
|
922
|
-
|
|
923
|
-
if (existing_lookup_address !== 0xFFFFFFFF) {
|
|
924
|
-
// found a hash match, validate equality
|
|
925
|
-
const existing_lookup_entry_match = testClusterEquality(
|
|
926
|
-
scratch_light_nodes,
|
|
927
|
-
match_count,
|
|
928
|
-
light_lookup_texture_data,
|
|
929
|
-
existing_lookup_address
|
|
930
|
-
);
|
|
931
|
-
|
|
932
|
-
if (existing_lookup_entry_match) {
|
|
933
|
-
tile_texture_data[tile_address] = existing_lookup_address;
|
|
934
|
-
|
|
935
|
-
hash_reuse_count++;
|
|
1025
|
+
const tile_data_offset = tile_index * 4;
|
|
936
1026
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
// write address of the lookup table
|
|
947
|
-
tile_texture_data[tile_address] = lookup_address_offset;
|
|
948
|
-
|
|
949
|
-
for (i = 0; i < match_count; i++) {
|
|
950
|
-
/**
|
|
951
|
-
*
|
|
952
|
-
* @type {number}
|
|
953
|
-
*/
|
|
954
|
-
const light_descriptor = scratch_light_nodes[i];
|
|
955
|
-
|
|
956
|
-
// construct lookup table
|
|
957
|
-
light_lookup_texture_data[lookup_address_offset] = light_descriptor;
|
|
958
|
-
|
|
959
|
-
lookup_address_offset++;
|
|
960
|
-
}
|
|
1027
|
+
lookup_address_offset += assign_cluster(
|
|
1028
|
+
tile_data_offset,
|
|
1029
|
+
bvh_lights,
|
|
1030
|
+
lookup_address_offset,
|
|
1031
|
+
light_lookup_texture_data,
|
|
1032
|
+
tile_texture_data,
|
|
1033
|
+
light_source_data
|
|
1034
|
+
);
|
|
961
1035
|
|
|
962
|
-
|
|
1036
|
+
lookup_address_offset += assign_cluster(
|
|
1037
|
+
tile_data_offset + 2,
|
|
1038
|
+
bvh_decals,
|
|
1039
|
+
lookup_address_offset,
|
|
1040
|
+
light_lookup_texture_data,
|
|
1041
|
+
tile_texture_data,
|
|
1042
|
+
light_source_data
|
|
1043
|
+
);
|
|
963
1044
|
}
|
|
964
1045
|
}
|
|
965
1046
|
}
|
|
@@ -970,14 +1051,15 @@ export class LightManager {
|
|
|
970
1051
|
// Post-fact lookup texture resize, this frame will be wrong, but next one should be ok
|
|
971
1052
|
const current_lookup_size = this.__lookup_data.size;
|
|
972
1053
|
|
|
973
|
-
const lookup_memory_resized = this.__ensure_lookup_size(
|
|
1054
|
+
const lookup_memory_resized = this.__ensure_lookup_size(lookup_address_offset);
|
|
974
1055
|
|
|
975
1056
|
const cluster_resized = this.__set_cluster_addressable_bit_range(Math.max(0, Math.log2(lookup_address_offset)));
|
|
976
1057
|
|
|
977
1058
|
if (
|
|
978
|
-
(lookup_memory_resized && current_lookup_size <
|
|
1059
|
+
(lookup_memory_resized && current_lookup_size < lookup_address_offset)
|
|
979
1060
|
|| cluster_resized
|
|
980
1061
|
) {
|
|
1062
|
+
// overflow, we ended up trying to write more lights into some clusters than what they could contain
|
|
981
1063
|
this.__update_cluster_texture();
|
|
982
1064
|
|
|
983
1065
|
// re-do assignment
|
|
@@ -1028,92 +1110,9 @@ export class LightManager {
|
|
|
1028
1110
|
const tr_x = tiles_resolution.x;
|
|
1029
1111
|
|
|
1030
1112
|
const view_frustum_points = this.__view_frustum_points;
|
|
1031
|
-
|
|
1032
|
-
// read out view frustum scratch_corners
|
|
1033
|
-
const x_000 = view_frustum_points[0];
|
|
1034
|
-
const y_000 = view_frustum_points[1];
|
|
1035
|
-
const z_000 = view_frustum_points[2];
|
|
1036
|
-
|
|
1037
|
-
const x_001 = view_frustum_points[3];
|
|
1038
|
-
const y_001 = view_frustum_points[4];
|
|
1039
|
-
const z_001 = view_frustum_points[5];
|
|
1040
|
-
|
|
1041
|
-
const x_010 = view_frustum_points[6];
|
|
1042
|
-
const y_010 = view_frustum_points[7];
|
|
1043
|
-
const z_010 = view_frustum_points[8];
|
|
1044
|
-
|
|
1045
|
-
const x_011 = view_frustum_points[9];
|
|
1046
|
-
const y_011 = view_frustum_points[10];
|
|
1047
|
-
const z_011 = view_frustum_points[11];
|
|
1048
|
-
|
|
1049
|
-
const x_100 = view_frustum_points[12];
|
|
1050
|
-
const y_100 = view_frustum_points[13];
|
|
1051
|
-
const z_100 = view_frustum_points[14];
|
|
1052
|
-
|
|
1053
|
-
const x_101 = view_frustum_points[15];
|
|
1054
|
-
const y_101 = view_frustum_points[16];
|
|
1055
|
-
const z_101 = view_frustum_points[17];
|
|
1056
|
-
|
|
1057
|
-
const x_110 = view_frustum_points[18];
|
|
1058
|
-
const y_110 = view_frustum_points[19];
|
|
1059
|
-
const z_110 = view_frustum_points[20];
|
|
1060
|
-
|
|
1061
|
-
const x_111 = view_frustum_points[21];
|
|
1062
|
-
const y_111 = view_frustum_points[22];
|
|
1063
|
-
const z_111 = view_frustum_points[23];
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const x_plane_count = tr_x + 1;
|
|
1067
|
-
const y_plane_count = tr_y + 1;
|
|
1068
|
-
const z_plane_count = tr_z + 1;
|
|
1069
|
-
|
|
1070
|
-
// TODO compute slices to minimize world-space volume difference between clusters
|
|
1071
|
-
const z_slice_stride = x_plane_count * y_plane_count;
|
|
1072
|
-
|
|
1073
1113
|
const points_data = this.__cluster_frustum_points;
|
|
1074
1114
|
|
|
1075
|
-
|
|
1076
|
-
const r_0 = i_z / (tr_z);
|
|
1077
|
-
const r_1 = 1 - r_0;
|
|
1078
|
-
|
|
1079
|
-
const z_offset = i_z * z_slice_stride;
|
|
1080
|
-
|
|
1081
|
-
for (let i_y = 0; i_y < y_plane_count; i_y++) {
|
|
1082
|
-
|
|
1083
|
-
const v_0 = i_y / tr_y;
|
|
1084
|
-
const v_1 = 1 - v_0;
|
|
1085
|
-
|
|
1086
|
-
const y_offset = z_offset + i_y * x_plane_count;
|
|
1087
|
-
|
|
1088
|
-
for (let i_x = 0; i_x < x_plane_count; i_x++) {
|
|
1089
|
-
const u_0 = i_x / tr_x;
|
|
1090
|
-
const u_1 = 1 - u_0;
|
|
1091
|
-
|
|
1092
|
-
const m_000 = u_1 * v_1 * r_1;
|
|
1093
|
-
const m_001 = u_1 * v_1 * r_0;
|
|
1094
|
-
const m_010 = u_1 * v_0 * r_1;
|
|
1095
|
-
const m_011 = u_1 * v_0 * r_0;
|
|
1096
|
-
const m_100 = u_0 * v_1 * r_1;
|
|
1097
|
-
const m_101 = u_0 * v_1 * r_0;
|
|
1098
|
-
const m_110 = u_0 * v_0 * r_1;
|
|
1099
|
-
const m_111 = u_0 * v_0 * r_0;
|
|
1100
|
-
|
|
1101
|
-
// interpolate view frustum scratch_corners
|
|
1102
|
-
const x = x_000 * m_000 + x_001 * m_001 + x_010 * m_010 + x_011 * m_011 + x_100 * m_100 + x_101 * m_101 + x_110 * m_110 + x_111 * m_111;
|
|
1103
|
-
const y = y_000 * m_000 + y_001 * m_001 + y_010 * m_010 + y_011 * m_011 + y_100 * m_100 + y_101 * m_101 + y_110 * m_110 + y_111 * m_111;
|
|
1104
|
-
const z = z_000 * m_000 + z_001 * m_001 + z_010 * m_010 + z_011 * m_011 + z_100 * m_100 + z_101 * m_101 + z_110 * m_110 + z_111 * m_111;
|
|
1105
|
-
|
|
1106
|
-
// write out cluster corner position
|
|
1107
|
-
const index = y_offset + i_x
|
|
1108
|
-
const address = index * 3;
|
|
1109
|
-
|
|
1110
|
-
points_data[address] = x;
|
|
1111
|
-
points_data[address + 1] = y;
|
|
1112
|
-
points_data[address + 2] = z;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1115
|
+
slice_frustum_linear_to_points(view_frustum_points, tr_x, tr_y, tr_z, points_data);
|
|
1117
1116
|
}
|
|
1118
1117
|
|
|
1119
1118
|
/**
|
|
@@ -1175,6 +1174,7 @@ export class LightManager {
|
|
|
1175
1174
|
this.__cluster_texture.dispose();
|
|
1176
1175
|
this.__light_data.dispose();
|
|
1177
1176
|
this.__lookup_data.dispose();
|
|
1177
|
+
this.__decal_atlas_texture.dispose();
|
|
1178
1178
|
}
|
|
1179
1179
|
|
|
1180
1180
|
/**
|
|
@@ -1220,9 +1220,9 @@ export class LightManager {
|
|
|
1220
1220
|
|
|
1221
1221
|
lightData.link();
|
|
1222
1222
|
|
|
1223
|
-
this.
|
|
1223
|
+
this.__light_data_bvh.insertNode(lightData.bvh_leaf_main);
|
|
1224
1224
|
|
|
1225
|
-
this.
|
|
1225
|
+
this.__light_data_map.set(light, lightData);
|
|
1226
1226
|
}
|
|
1227
1227
|
|
|
1228
1228
|
/**
|
|
@@ -1232,7 +1232,7 @@ export class LightManager {
|
|
|
1232
1232
|
*/
|
|
1233
1233
|
removeLight(light) {
|
|
1234
1234
|
|
|
1235
|
-
const data = this.
|
|
1235
|
+
const data = this.__light_data_map.get(light);
|
|
1236
1236
|
|
|
1237
1237
|
if (data === undefined) {
|
|
1238
1238
|
return false;
|
|
@@ -1240,7 +1240,7 @@ export class LightManager {
|
|
|
1240
1240
|
|
|
1241
1241
|
data.unlink();
|
|
1242
1242
|
|
|
1243
|
-
this.
|
|
1243
|
+
this.__light_data_map.delete(light);
|
|
1244
1244
|
|
|
1245
1245
|
return true;
|
|
1246
1246
|
}
|