@woosh/meep-engine 2.43.53 → 2.44.0

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.
@@ -9,7 +9,7 @@ import {
9
9
  LinearMipMapLinearFilter,
10
10
  NearestFilter,
11
11
  RGBAFormat,
12
- RGBIntegerFormat,
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.__light_points_bvh = new BinaryNode();
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.__light_point_data = new Map();
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(3),
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 = RGBIntegerFormat;
164
- this.__cluster_texture.internalFormat = "RGB16UI";
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(invokeObjectCompare);
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.__visible_bvh = new BinaryUint32BVH();
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.__handle_visible_added, this);
289
- this.__visible_lights.onRemoved.add(this.__handle_visible_removed, 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 = 3;
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
- __handle_visible_added(data) {
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
- if (light instanceof Decal) {
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
- const patch = ref.getValue();
478
- const patch_uv = patch.uv;
625
+ const patch = ref.getValue();
626
+ const patch_uv = patch.uv;
479
627
 
480
- patch_uv.position.onChanged.add(light.handleUvPositionChange, light);
481
- patch_uv.size.onChanged.add(light.handleUvSizeChange, light);
628
+ patch_uv.position.onChanged.add(light.handleUvPositionChange, light);
629
+ patch_uv.size.onChanged.add(light.handleUvSizeChange, light);
482
630
 
483
- light.uv[0] = patch_uv.position.x;
484
- light.uv[1] = patch_uv.position.y;
485
- light.uv[2] = patch_uv.size.x;
486
- light.uv[3] = patch_uv.size.y;
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
- this.__decal_references.set(light, ref);
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
- __handle_visible_removed(data) {
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
- if (light instanceof Decal) {
504
- const ref = this.__decal_references.get(light);
655
+ const ref = this.__decal_references.get(light);
505
656
 
506
- if (ref === undefined) {
507
- // This can occur when decal changes while being in the visible list, messing with the IncrementalDeltaSet's compare order
508
- console.warn(`Decal reference not found: ${light}, likely live decal changed`);
509
- // TODO address this case properly
510
- return;
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
- const patch = ref.getValue();
514
- const patch_uv = patch.uv;
663
+ const patch = ref.getValue();
664
+ const patch_uv = patch.uv;
515
665
 
516
- // unsubscribe
517
- patch_uv.position.onChanged.remove(light.handleUvPositionChange, light);
518
- patch_uv.size.onChanged.remove(light.handleUvSizeChange, light);
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
- ref.release();
671
+ ref.release();
522
672
 
523
- this.__decal_references.delete(light);
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
- if (sorted_lights.length > visible_light_count) {
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, sorted_lights.length - 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, visible_light_count - 1);
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
- const visibleLights = this.__visible_lights;
609
-
610
- const lights = visibleLights.elements;
611
- const light_count = visibleLights.size;
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.__light_points_bvh, scratch_frustum_planes, this.__view_frustum_points);
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
- visible_lights.push(light_data);
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 bvh = this.__visible_bvh;
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
- // construct addressed of individual corner points
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 tile_address = tile_index * 3;
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
- continue;
938
- }
939
- } else {
940
- // no hash entry exists, create one
941
- LOOKUP_CACHE[cluster_data_hash] = lookup_address_offset;
942
- }
943
-
944
- // if we get to this point, that means no hash match was found, or equality check was failed
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
- assignment_count += match_count;
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(assignment_count);
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 < assignment_count)
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
- for (let i_z = 0; i_z < z_plane_count; i_z++) {
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.__light_points_bvh.insertNode(lightData.bvh_leaf_main);
1223
+ this.__light_data_bvh.insertNode(lightData.bvh_leaf_main);
1224
1224
 
1225
- this.__light_point_data.set(light, lightData);
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.__light_point_data.get(light);
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.__light_point_data.delete(light);
1243
+ this.__light_data_map.delete(light);
1244
1244
 
1245
1245
  return true;
1246
1246
  }