@woosh/meep-engine 2.39.38 → 2.39.41

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.
Files changed (69) hide show
  1. package/core/binary/Base64.js +58 -31
  2. package/core/binary/Base64.spec.js +14 -0
  3. package/core/binary/operations/ceilPowerOfTwo.spec.js +17 -0
  4. package/core/bvh2/binary/2/BinaryUint32BVH.js +7 -1
  5. package/core/bvh2/binary/2/BinaryUint32BVH.spec.js +31 -1
  6. package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.js +5 -5
  7. package/core/bvh2/bvh3/bvh_query_user_data_nearest_to_point.spec.js +70 -0
  8. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.spec.js +2 -2
  9. package/core/collection/IteratorUtils.js +1 -0
  10. package/core/collection/queue/Deque.js +5 -2
  11. package/core/color/Color.js +29 -7
  12. package/core/color/hsluv/HSLuv.js +187 -0
  13. package/core/geom/3d/aabb/aabb3_matrix4_project.js +1 -0
  14. package/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +1 -1
  15. package/core/geom/Quaternion.js +52 -38
  16. package/core/model/ModuleRegistry.js +1 -1
  17. package/core/model/reactive/model/terminal/ReactiveReference.js +1 -1
  18. package/core/model/reactive/trigger/ReactiveTrigger.js +14 -4
  19. package/core/primitives/strings/compareStrings.js +22 -0
  20. package/editor/Editor.js +2 -0
  21. package/editor/ecs/component/editors/ImagePathEditor.js +83 -0
  22. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +12 -6
  23. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +22 -11
  24. package/engine/EngineHarness.js +7 -0
  25. package/engine/asset/loaders/geometry/geometry/computeBufferAttributeHash.js +5 -6
  26. package/engine/asset/loaders/image/codec/Codec.js +9 -0
  27. package/engine/asset/loaders/image/codec/CodecWithFallback.js +52 -11
  28. package/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +12 -0
  29. package/engine/asset/loaders/image/png/PNGReader.js +2 -1
  30. package/engine/asset/loaders/image/png/PNG_HEADER_BYTES.js +1 -0
  31. package/engine/computeStridedIntegerArrayHash.js +19 -0
  32. package/engine/ecs/EntityBuilder.d.ts +2 -0
  33. package/engine/ecs/gui/GUIElement.d.ts +3 -1
  34. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +5 -3
  35. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +27 -4
  36. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +11 -2
  37. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +14 -6
  38. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -8
  39. package/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +2 -1
  40. package/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js +17 -2
  41. package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +336 -0
  42. package/engine/graphics/geometry/optimization/merge/prototypeGeometryMerge.js +176 -0
  43. package/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +7 -3
  44. package/engine/graphics/micron/build/hierarchy/merge_patches.js +0 -1
  45. package/engine/graphics/micron/prototypeVirtualGeometry.js +3 -3
  46. package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +32 -1
  47. package/engine/graphics/render/forward_plus/LightManager.js +58 -13
  48. package/engine/graphics/render/forward_plus/debug/createScreenGrid.js +192 -9
  49. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +42 -1
  50. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +1 -0
  51. package/engine/graphics/render/forward_plus/materials/ForwardPlusThreeMaterial.js +5 -0
  52. package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -4
  53. package/engine/graphics/render/forward_plus/model/Decal.js +33 -5
  54. package/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  55. package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +4 -0
  56. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +480 -12
  57. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +49 -29
  58. package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +11 -0
  59. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +34 -0
  60. package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +9 -1
  61. package/engine/graphics/texture/atlas/TextureAtlas.js +29 -16
  62. package/engine/graphics/texture/atlas/TextureAtlasDebugger.js +75 -19
  63. package/engine/graphics/texture/sampler/Sampler2D.d.ts +2 -0
  64. package/engine/graphics/texture/sampler/Sampler2D.js +72 -11
  65. package/engine/sound/ecs/SoundListenerSystem.js +5 -0
  66. package/engine/sound/ecs/emitter/SoundEmitter.js +12 -1
  67. package/package.json +1 -1
  68. package/view/elements/button/ButtonView.js +12 -1
  69. package/view/tooltip/TooltipManager.js +0 -95
@@ -0,0 +1,176 @@
1
+ import { EngineHarness } from "../../../../EngineHarness.js";
2
+ import RenderSystem from "../../../../ecs/systems/RenderSystem.js";
3
+
4
+ import '../../../../../../../../css/main.scss';
5
+ import { GameAssetType } from "../../../../asset/GameAssetType.js";
6
+ import { TextureAssetLoader } from "../../../../asset/loaders/texture/TextureAssetLoader.js";
7
+ import Vector3 from "../../../../../core/geom/Vector3.js";
8
+ import Vector2 from "../../../../../core/geom/Vector2.js";
9
+ import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
10
+ import EntityBuilder from "../../../../ecs/EntityBuilder.js";
11
+ import Renderable from "../../../../ecs/components/Renderable.js";
12
+ import { Transform } from "../../../../ecs/transform/Transform.js";
13
+ import { merge_geometry_hierarchy } from "./merge_geometry_hierarchy.js";
14
+ import {
15
+ AmbientOcclusionPostProcessEffect
16
+ } from "../../../render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js";
17
+ import GUIElementSystem from "../../../../ecs/gui/GUIElementSystem.js";
18
+ import ViewportPositionSystem from "../../../../ecs/gui/position/ViewportPositionSystem.js";
19
+ import HeadsUpDisplaySystem from "../../../../ecs/gui/hud/HeadsUpDisplaySystem.js";
20
+ import HeadsUpDisplay from "../../../../ecs/gui/hud/HeadsUpDisplay.js";
21
+ import ViewportPosition from "../../../../ecs/gui/position/ViewportPosition.js";
22
+ import GUIElement from "../../../../ecs/gui/GUIElement.js";
23
+ import LabelView from "../../../../../view/common/LabelView.js";
24
+ import { halton_sequence } from "../../../../../core/math/statistics/halton_sequence.js";
25
+ import { Color } from "../../../../../core/color/Color.js";
26
+ import { MeshStandardMaterial } from 'three';
27
+ import { lerp } from "../../../../../core/math/lerp.js";
28
+ import { cloneObject3D } from "../../../three/cloneObject3D.js";
29
+
30
+ function apply_random_coloring(object) {
31
+ const result = cloneObject3D(object);
32
+
33
+ const material_set = new Map();
34
+
35
+ result.traverse(o => {
36
+ if (o.isMesh) {
37
+ let objects = material_set.get(o.geometry);
38
+
39
+ if (objects === undefined) {
40
+ objects = [];
41
+ material_set.set(o.geometry, objects);
42
+ }
43
+
44
+ objects.push(o);
45
+ }
46
+ });
47
+
48
+ const material_array = Array.from(material_set.keys());
49
+
50
+ const material_count = material_array.length;
51
+ for (let i = 0; i < material_count; i++) {
52
+
53
+ const key_0 = i % 32;
54
+ const key_1 = Math.ceil(i / 64)
55
+
56
+ const h = halton_sequence(32, key_0);
57
+ const s = halton_sequence(Math.ceil(material_count / 64), key_1)
58
+
59
+ const material = material_array[i];
60
+
61
+ const objects = material_set.get(material);
62
+
63
+ const m = new MeshStandardMaterial({ color: Color.fromHSV(h, lerp(0.6, 1, s), 1).toUint() });
64
+
65
+
66
+ objects.forEach(o => {
67
+ o.material = m;
68
+ });
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ function compute_meshes(o) {
75
+ let r = 0;
76
+ o.traverse(obj => {
77
+ if (obj.isMesh) {
78
+
79
+ r++;
80
+
81
+ }
82
+ });
83
+
84
+ return r;
85
+ }
86
+
87
+ /**
88
+ *
89
+ * @param {Engine} engine
90
+ */
91
+ async function main(engine) {
92
+ await EngineHarness.buildBasics({
93
+ engine,
94
+ yaw: 0,
95
+ pitch: 1,
96
+ focus: new Vector3(138, 0, 125),
97
+ distance: 30,
98
+ terrainResolution: 1,
99
+ terrainSize: new Vector2(128, 128),
100
+ enableWater: false
101
+ });
102
+
103
+ const gltf = await engine.assetManager.promise("moicon/gnutti_not_optimized/model.gltf", GameAssetType.ModelGLTF_JSON);
104
+
105
+ const hierarchy = gltf.create();
106
+
107
+ const optimized_hierarchy = merge_geometry_hierarchy(hierarchy);
108
+
109
+ const css = {
110
+ background: "rgba(0,0,0,0.5)",
111
+ color: "white",
112
+ border: "1px solid black",
113
+ width: "min-content",
114
+ padding: "4px",
115
+ borderRadius: "4px",
116
+ whiteSpace: "pre"
117
+ };
118
+
119
+ new EntityBuilder()
120
+ .add(new Renderable(apply_random_coloring(optimized_hierarchy)))
121
+ .add(Transform.fromJSON({
122
+ position: new Vector3(128, 2, 128)
123
+ }))
124
+ .add(HeadsUpDisplay.fromJSON({
125
+ worldOffset: new Vector3(0, 2, 0)
126
+ }))
127
+ .add(new ViewportPosition())
128
+ .add(GUIElement.fromView(new LabelView(`optimized\ninstances: ${compute_meshes(optimized_hierarchy)}`, {
129
+ css
130
+ })))
131
+ .build(engine.entityManager.dataset);
132
+
133
+ new EntityBuilder()
134
+ .add(new Renderable(apply_random_coloring(hierarchy)))
135
+ .add(Transform.fromJSON({
136
+ position: new Vector3(138, 2, 128)
137
+ }))
138
+ .add(HeadsUpDisplay.fromJSON({
139
+ worldOffset: new Vector3(0, 2, 0)
140
+ }))
141
+ .add(new ViewportPosition())
142
+ .add(GUIElement.fromView(new LabelView(`original\ninstances: ${compute_meshes(hierarchy)}`, {
143
+ css
144
+ })))
145
+ .build(engine.entityManager.dataset);
146
+
147
+
148
+ new EntityBuilder()
149
+ .add(new Renderable(optimized_hierarchy))
150
+ .add(Transform.fromJSON({
151
+ position: new Vector3(148, 2, 128)
152
+ }))
153
+ .add(HeadsUpDisplay.fromJSON({
154
+ worldOffset: new Vector3(0, 2, 0)
155
+ }))
156
+ .add(new ViewportPosition())
157
+ .add(GUIElement.fromView(new LabelView(`original look`, {
158
+ css
159
+ })))
160
+ .build(engine.entityManager.dataset);
161
+
162
+ }
163
+
164
+ new EngineHarness().initialize({
165
+ configuration(config, engine) {
166
+ config.addSystem(new RenderSystem(engine.graphics));
167
+ config.addSystem(new GUIElementSystem(engine.gameView, engine));
168
+ config.addSystem(new ViewportPositionSystem(engine.graphics.viewport.size));
169
+ config.addSystem(new HeadsUpDisplaySystem(engine.graphics));
170
+
171
+ config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
172
+ config.addLoader(GameAssetType.Texture, new TextureAssetLoader());
173
+
174
+ config.addPlugin(AmbientOcclusionPostProcessEffect);
175
+ }
176
+ }).then(main)
@@ -86,8 +86,7 @@ async function assemble_graph(nodes) {
86
86
  build_vertex_quadratics({ mesh: patch.topology_snapshot, quadratics: vertex_quadratics });
87
87
  }
88
88
 
89
-
90
- main_loop: while (__node_count.value > 1) {
89
+ while (__node_count.value > 1) {
91
90
 
92
91
  // assemble nodes into a graph
93
92
  const leaf_cluster_weight_graph = build_merge_graph(__nodes);
@@ -121,6 +120,7 @@ async function assemble_graph(nodes) {
121
120
  await Promise.all(__promises);
122
121
 
123
122
  if (__node_count.value >= old_node_count) {
123
+ // new graph has the same number of nodes as the old one
124
124
 
125
125
  if (leaf_cluster_weight_graph.getNodeCount() <= 1) {
126
126
  // graph is already merged to the maximum level
@@ -131,10 +131,14 @@ async function assemble_graph(nodes) {
131
131
  throw new Error(`Exceeded merge limit(=${LIMIT_MERGE_COUNT}), attempting to merge too many patches together`);
132
132
  }
133
133
 
134
+ if (old_node_count <= merge_count) {
135
+ throw new Error(`Already allowing to merge all of the nodes, yet merge still fails. Allowed patch count = ${merge_count}, Actual patches = ${old_node_count})`);
136
+ }
137
+
134
138
  // failed to do any merging, increase merge scope
135
139
  merge_count *= 2;
136
140
 
137
- continue main_loop;
141
+ continue;
138
142
  }
139
143
 
140
144
  // clear existing neighbours for this level
@@ -415,7 +415,6 @@ async function many_to_many_merge(
415
415
  merged_mesh._addWithVertexMap(child.topology_snapshot, vertex_map);
416
416
  }
417
417
 
418
-
419
418
  /**
420
419
  *
421
420
  * @type {Set<number>}
@@ -518,11 +518,11 @@ function init() {
518
518
  // gltf_grid('moicon/ztest_object_many_pieces/model.gltf', 3, 3);
519
519
  // gltf_sample('moicon/ztest_object_many_pieces/model.gltf');
520
520
  // gltf_sample('moicon/62cf22227f7b8f0012ca5988/model.gltf');
521
- gltf_sample('moicon/gnutti_not_optimized/model.gltf');
521
+ // gltf_sample('moicon/gnutti_not_optimized/model.gltf');
522
522
  // gltf_grid('data/models/moicon/Kople/EVCharger1.gltf');
523
523
  // gltf_sample('data/models/moicon/Kople/EVCharger1.gltf');
524
524
  // gltf_sample('data/models/moicon/isiflow_Oct_15_21/0/model.gltf');
525
- // sample_lucy();
525
+ sample_lucy();
526
526
  // sample_square();
527
527
  // sample_isiflow_floor();
528
528
 
@@ -596,7 +596,7 @@ function createScene(geometry, m1, scale) {
596
596
  geometry.computeVertexNormals();
597
597
 
598
598
 
599
- const grid_size = 1;
599
+ const grid_size = 100;
600
600
  const grid_spacing = 2000;
601
601
 
602
602
  for (let i = 0; i < grid_size; i++) {
@@ -193,7 +193,7 @@ export class ParameterLookupTable {
193
193
 
194
194
  /**
195
195
  *
196
- * @param {number[]|Float64Array|Float32Array} values
196
+ * @param {number[]|Float64Array|Float32Array|Uint8Array} values
197
197
  * @param {number[]|Float64Array|Float32Array} [positions]
198
198
  */
199
199
  write(values, positions) {
@@ -412,6 +412,37 @@ export class ParameterLookupTable {
412
412
 
413
413
  return r;
414
414
  }
415
+
416
+ /**
417
+ *
418
+ * @param {Color} colors
419
+ * @returns {ParameterLookupTable}
420
+ */
421
+ static color_uint8_uniform(...colors) {
422
+ assert.defined(colors, 'colors');
423
+
424
+ const r = new ParameterLookupTable(4);
425
+
426
+ const length = colors.length;
427
+ const values = new Uint8Array(length * 4);
428
+
429
+ for (let i = 0; i < length; i++) {
430
+ const color = colors[i];
431
+
432
+ const i4 = i * 4;
433
+
434
+ values[i4] = Math.round(color.r * 255);
435
+ values[i4 + 1] = Math.round(color.g * 255);
436
+ values[i4 + 2] = Math.round(color.b * 255);
437
+ values[i4 + 3] = Math.round(color.a * 255);
438
+ }
439
+
440
+ r.write(values);
441
+
442
+ r.computeUniformPositions();
443
+
444
+ return r;
445
+ }
415
446
  }
416
447
 
417
448
  /**
@@ -111,13 +111,13 @@ function encode_light_type(light) {
111
111
 
112
112
  /**
113
113
  *
114
- * @param {number} index
114
+ * @param {number} address
115
115
  * @param {AbstractLight} light
116
116
  * @returns {number}
117
117
  */
118
- function encode_light_descriptor(index, light) {
118
+ function encode_light_descriptor(address, light) {
119
119
  const light_type = encode_light_type(light);
120
- return light_type | (index << 2);
120
+ return light_type | (address << 2);
121
121
  }
122
122
 
123
123
  export class LightManager {
@@ -147,7 +147,7 @@ export class LightManager {
147
147
  this.__cluster_texture_precision = 8;
148
148
  this.__cluster_texture_needs_rebuild = false;
149
149
  this.__cluster_texture = new DataTexture3D(
150
- new Uint16Array(1),
150
+ new Uint16Array(3),
151
151
  1,
152
152
  1,
153
153
  1,
@@ -204,12 +204,12 @@ export class LightManager {
204
204
 
205
205
  /**
206
206
  *
207
- * @type {CachingTextureAtlas}
207
+ * @type {AbstractTextureAtlas}
208
208
  * @readonly
209
209
  * @private
210
210
  */
211
211
  this.__decal_atlas = new CachingTextureAtlas({
212
- atlas: new TextureAtlas(16)
212
+ atlas: new TextureAtlas(512)
213
213
  });
214
214
 
215
215
  /**
@@ -221,7 +221,7 @@ export class LightManager {
221
221
 
222
222
  /**
223
223
  *
224
- * @type {Map<Decal, Reference>}
224
+ * @type {Map<Decal, Reference<AtlasPatch>>}
225
225
  * @private
226
226
  */
227
227
  this.__decal_references = new Map();
@@ -301,14 +301,16 @@ export class LightManager {
301
301
  * @private
302
302
  */
303
303
  this.__visible_bvh_needs_update = true;
304
-
305
- this.__decal_atlas.atlas.on.painted.add(this.__update_decal_atlas_texture, this);
306
304
  }
307
305
 
308
306
  __update_decal_atlas_texture() {
309
- const sampler = this.__decal_atlas.atlas.sampler;
307
+ const sampler = this.__decal_atlas.sampler;
308
+
309
+ if (sampler.version !== this.__decal_atlas_texture.version) {
310
+ writeSample2DDataToDataTexture(sampler, this.__decal_atlas_texture);
310
311
 
311
- writeSample2DDataToDataTexture(sampler, this.__decal_atlas_texture);
312
+ this.__decal_atlas_texture.version = sampler.version;
313
+ }
312
314
  }
313
315
 
314
316
  /**
@@ -408,7 +410,7 @@ export class LightManager {
408
410
  /**
409
411
  * @returns {DataTexture}
410
412
  */
411
- getDecalTexture() {
413
+ getTextureDecalAtlas() {
412
414
  return this.__decal_atlas_texture;
413
415
  }
414
416
 
@@ -443,6 +445,17 @@ export class LightManager {
443
445
  if (light instanceof Decal) {
444
446
  const ref = this.__decal_patch_references.acquire(light.texture_diffuse);
445
447
 
448
+ const patch = ref.getValue();
449
+ const patch_uv = patch.uv;
450
+
451
+ patch_uv.position.onChanged.add(light.handleUvPositionChange, light);
452
+ patch_uv.size.onChanged.add(light.handleUvSizeChange, light);
453
+
454
+ light.uv[0] = patch_uv.position.x;
455
+ light.uv[1] = patch_uv.position.y;
456
+ light.uv[2] = patch_uv.size.x;
457
+ light.uv[3] = patch_uv.size.y;
458
+
446
459
  this.__decal_references.set(light, ref);
447
460
  }
448
461
  }
@@ -461,6 +474,14 @@ export class LightManager {
461
474
  if (light instanceof Decal) {
462
475
  const ref = this.__decal_references.get(light);
463
476
 
477
+ const patch = ref.getValue();
478
+ const patch_uv = patch.uv;
479
+
480
+ // unsubscribe
481
+ patch_uv.position.onChanged.remove(light.handleUvPositionChange, light);
482
+ patch_uv.size.onChanged.remove(light.handleUvSizeChange, light);
483
+
484
+
464
485
  ref.release();
465
486
 
466
487
  this.__decal_references.delete(light);
@@ -655,6 +676,8 @@ export class LightManager {
655
676
  address += light.ENCODED_SLOT_COUNT;
656
677
  }
657
678
 
679
+ const expected_light_data_size = address;
680
+
658
681
 
659
682
  const light_data = this.__light_data;
660
683
  light_data.resize(Math.ceil(address / 4));
@@ -696,15 +719,24 @@ export class LightManager {
696
719
 
697
720
  // align address to 4 slot boundary
698
721
  if ((address & 3) !== 0) {
722
+ // not on 4 slot boundary
699
723
  address = ((address >> 2) + 1) << 2;
700
724
  }
701
725
  }
702
726
 
727
+ if (address !== expected_light_data_size) {
728
+ throw new Error(`Expected light data size is ${expected_light_data_size}, actual written data is size is ${address}`);
729
+ }
730
+
703
731
  tx_light.needsUpdate = true;
704
732
 
705
733
  this.__visible_bvh_needs_update = true;
706
734
  }
707
735
 
736
+ /**
737
+ * Perform cluster assignment where each cluster is filled with overlapping lights
738
+ * @private
739
+ */
708
740
  __assign_lights_to_clusters() {
709
741
 
710
742
  const tiles_resolution = this.__tiles_resolution;
@@ -916,6 +948,10 @@ export class LightManager {
916
948
  // console.log('Unique assignment count: ', assignment_count, ', Hash reuse:', hash_reuse_count);
917
949
  }
918
950
 
951
+ /**
952
+ *
953
+ * @private
954
+ */
919
955
  __build_cluster_frustum_planes() {
920
956
  const tiles_resolution = this.__tiles_resolution;
921
957
 
@@ -928,6 +964,10 @@ export class LightManager {
928
964
  compute_cluster_planes_from_points(destination, this.__cluster_frustum_points, tr_x, tr_y, tr_z);
929
965
  }
930
966
 
967
+ /**
968
+ *
969
+ * @private
970
+ */
931
971
  __build_view_frustum_points() {
932
972
 
933
973
  const view_frustum = this.__view_frustum;
@@ -937,6 +977,10 @@ export class LightManager {
937
977
  computeFrustumCorners(points, view_frustum.planes);
938
978
  }
939
979
 
980
+ /**
981
+ *
982
+ * @private
983
+ */
940
984
  __build_cluster_frustum_points() {
941
985
  const tiles_resolution = this.__tiles_resolution;
942
986
 
@@ -1059,7 +1103,8 @@ export class LightManager {
1059
1103
  // console.timeEnd('__build_visible_light_list');
1060
1104
 
1061
1105
  // update decal atlas
1062
- this.__decal_atlas.atlas.update();
1106
+ this.__decal_atlas.update();
1107
+ this.__update_decal_atlas_texture();
1063
1108
 
1064
1109
  // console.time('__write_light_data_texture');
1065
1110
  if (this.__light_data_needs_update) {