@woosh/meep-engine 2.165.7 → 2.167.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.
Files changed (146) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/package.json +1 -1
  3. package/src/CORRECTNESS_HUNT_2026_07_24.md +142 -0
  4. package/src/core/binary/BinaryBuffer.js +1 -1
  5. package/src/core/binary/EncodingBinaryBuffer.d.ts.map +1 -1
  6. package/src/core/binary/EncodingBinaryBuffer.js +3 -1
  7. package/src/core/cache/Cache.d.ts.map +1 -1
  8. package/src/core/cache/Cache.js +0 -4
  9. package/src/core/cache/CacheElement.d.ts.map +1 -1
  10. package/src/core/cache/CacheElement.js +4 -0
  11. package/src/core/cache/FrequencySketch.d.ts +0 -21
  12. package/src/core/cache/FrequencySketch.d.ts.map +1 -1
  13. package/src/core/cache/FrequencySketch.js +66 -65
  14. package/src/core/cache/wtinylfu/CacheElementWTinylfu.d.ts +17 -0
  15. package/src/core/cache/wtinylfu/CacheElementWTinylfu.d.ts.map +1 -0
  16. package/src/core/cache/wtinylfu/CacheElementWTinylfu.js +18 -0
  17. package/src/core/cache/wtinylfu/CacheRegionType.d.ts +10 -0
  18. package/src/core/cache/wtinylfu/CacheRegionType.d.ts.map +1 -0
  19. package/src/core/cache/wtinylfu/CacheRegionType.js +10 -0
  20. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.d.ts +66 -0
  21. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.d.ts.map +1 -0
  22. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.js +183 -0
  23. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts +279 -0
  24. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -0
  25. package/src/core/cache/wtinylfu/CacheWTinylfu.js +807 -0
  26. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +243 -0
  27. package/src/core/cache/wtinylfu/wtinylfuDecisions.d.ts +80 -0
  28. package/src/core/cache/wtinylfu/wtinylfuDecisions.d.ts.map +1 -0
  29. package/src/core/cache/wtinylfu/wtinylfuDecisions.js +102 -0
  30. package/src/core/collection/RingBuffer.d.ts.map +1 -1
  31. package/src/core/collection/RingBuffer.js +15 -3
  32. package/src/core/collection/map/HashMap.d.ts +1 -1
  33. package/src/core/collection/map/HashMap.d.ts.map +1 -1
  34. package/src/core/collection/map/HashMap.js +5 -17
  35. package/src/core/geom/2d/line/line2_compute_segment_point_distance_sqr.d.ts.map +1 -1
  36. package/src/core/geom/2d/line/line2_compute_segment_point_distance_sqr.js +5 -0
  37. package/src/core/geom/ConicRay.d.ts.map +1 -1
  38. package/src/core/geom/ConicRay.js +9 -2
  39. package/src/core/geom/Quaternion.d.ts.map +1 -1
  40. package/src/core/geom/Quaternion.js +7 -3
  41. package/src/core/math/build_gaussian_kernel_1d.d.ts +2 -2
  42. package/src/core/math/build_gaussian_kernel_1d.d.ts.map +1 -1
  43. package/src/core/math/build_gaussian_kernel_1d.js +4 -2
  44. package/src/core/math/build_gaussian_kernel_2d.d.ts +2 -2
  45. package/src/core/math/build_gaussian_kernel_2d.d.ts.map +1 -1
  46. package/src/core/math/build_gaussian_kernel_2d.js +4 -2
  47. package/src/core/math/gaussian.d.ts +2 -2
  48. package/src/core/math/gaussian.js +2 -2
  49. package/src/core/math/spline/spline3_bezier_bounds.d.ts.map +1 -1
  50. package/src/core/math/spline/spline3_bezier_bounds.js +10 -3
  51. package/src/core/math/spline/spline3_bezier_to_hermite.d.ts +8 -7
  52. package/src/core/math/spline/spline3_bezier_to_hermite.d.ts.map +1 -1
  53. package/src/core/math/spline/spline3_bezier_to_hermite.js +13 -12
  54. package/src/core/math/spline/spline3_hermite_bounds.d.ts.map +1 -1
  55. package/src/core/math/spline/spline3_hermite_bounds.js +10 -3
  56. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts.map +1 -1
  57. package/src/core/math/spline/spline3_hermite_bounds_t.js +115 -109
  58. package/src/core/math/spline/spline3_hermite_to_bezier.d.ts +2 -0
  59. package/src/core/math/spline/spline3_hermite_to_bezier.d.ts.map +1 -1
  60. package/src/core/math/spline/spline3_hermite_to_bezier.js +2 -0
  61. package/src/core/math/statistics/computeStatisticalMean.js +1 -1
  62. package/src/core/math/statistics/gaussian_amplitude.d.ts.map +1 -1
  63. package/src/core/math/statistics/gaussian_amplitude.js +3 -1
  64. package/src/engine/animation/clip/ecd_bind_animation_curve.d.ts.map +1 -1
  65. package/src/engine/animation/clip/ecd_bind_animation_curve.js +10 -1
  66. package/src/engine/animation/clip/hemisphere_align_quaternion_values.d.ts +20 -0
  67. package/src/engine/animation/clip/hemisphere_align_quaternion_values.d.ts.map +1 -0
  68. package/src/engine/animation/clip/hemisphere_align_quaternion_values.js +37 -0
  69. package/src/engine/animation/keyed2/AnimationTrackPlayback.d.ts.map +1 -1
  70. package/src/engine/animation/keyed2/AnimationTrackPlayback.js +5 -1
  71. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  72. package/src/engine/ecs/EntityComponentDataset.js +5 -21
  73. package/src/engine/ecs/fow/FogOfWar.d.ts +22 -0
  74. package/src/engine/ecs/fow/FogOfWar.d.ts.map +1 -1
  75. package/src/engine/ecs/fow/FogOfWar.js +202 -54
  76. package/src/engine/ecs/fow/FogOfWarEdgeMode.d.ts +16 -0
  77. package/src/engine/ecs/fow/FogOfWarEdgeMode.d.ts.map +1 -0
  78. package/src/engine/ecs/fow/FogOfWarEdgeMode.js +39 -0
  79. package/src/engine/ecs/fow/FogOfWarSystem.d.ts.map +1 -1
  80. package/src/engine/ecs/fow/FogOfWarSystem.js +1 -0
  81. package/src/engine/ecs/fow/serialization/FogOfWarSerializationAdapter.d.ts.map +1 -1
  82. package/src/engine/ecs/fow/serialization/FogOfWarSerializationAdapter.js +3 -0
  83. package/src/engine/ecs/fow/shader/FogOfWarRenderer.d.ts +7 -0
  84. package/src/engine/ecs/fow/shader/FogOfWarRenderer.d.ts.map +1 -1
  85. package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +155 -145
  86. package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.d.ts.map +1 -1
  87. package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js +33 -8
  88. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts.map +1 -1
  89. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.js +3 -1
  90. package/src/engine/graphics/GraphicsEngine.d.ts +5 -1
  91. package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
  92. package/src/engine/graphics/GraphicsEngine.js +14 -3
  93. package/src/engine/graphics/composit/CompositLayer.d.ts.map +1 -1
  94. package/src/engine/graphics/composit/CompositLayer.js +258 -255
  95. package/src/engine/graphics/composit/LayerCompositer.d.ts +12 -0
  96. package/src/engine/graphics/composit/LayerCompositer.d.ts.map +1 -1
  97. package/src/engine/graphics/composit/LayerCompositer.js +19 -10
  98. package/src/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.d.ts +1 -1
  99. package/src/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.d.ts.map +1 -1
  100. package/src/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.js +10 -4
  101. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.d.ts +6 -0
  102. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.d.ts.map +1 -1
  103. package/src/engine/graphics/ecs/highlight/renderer/OutlineRenderer.js +21 -0
  104. package/src/engine/graphics/ecs/path/ribbon/RibbonPathBuilder.d.ts.map +1 -1
  105. package/src/engine/graphics/ecs/path/ribbon/RibbonPathBuilder.js +5 -23
  106. package/src/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.d.ts.map +1 -1
  107. package/src/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.js +6 -4
  108. package/src/engine/graphics/ecs/water/WaterSystem.d.ts.map +1 -1
  109. package/src/engine/graphics/ecs/water/WaterSystem.js +9 -3
  110. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.d.ts +12 -0
  111. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.d.ts.map +1 -1
  112. package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.js +46 -9
  113. package/src/engine/graphics/particles/particular/engine/ParticularEngine.d.ts +5 -0
  114. package/src/engine/graphics/particles/particular/engine/ParticularEngine.d.ts.map +1 -1
  115. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +8 -0
  116. package/src/engine/graphics/particles/particular/engine/shader/MaterialRecord.d.ts +7 -0
  117. package/src/engine/graphics/particles/particular/engine/shader/MaterialRecord.d.ts.map +1 -1
  118. package/src/engine/graphics/particles/particular/engine/shader/MaterialRecord.js +8 -0
  119. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.d.ts +10 -2
  120. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.d.ts.map +1 -1
  121. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +42 -10
  122. package/src/engine/graphics/render/buffer/FrameBuffer.d.ts.map +1 -1
  123. package/src/engine/graphics/render/buffer/FrameBuffer.js +5 -2
  124. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts +13 -1
  125. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
  126. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +40 -7
  127. package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.d.ts.map +1 -1
  128. package/src/engine/graphics/render/buffer/simple-fx/taa/TemporalSupersamplingRenderPlugin.js +10 -4
  129. package/src/engine/graphics/render/forward_plus/LightManager.d.ts.map +1 -1
  130. package/src/engine/graphics/render/forward_plus/LightManager.js +1235 -1232
  131. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.d.ts +5 -0
  132. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.d.ts.map +1 -1
  133. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.js +11 -10
  134. package/src/engine/graphics/trail/tube/TubeX.d.ts.map +1 -1
  135. package/src/engine/graphics/trail/tube/TubeX.js +557 -555
  136. package/src/engine/graphics/trail/x/RibbonXPlugin.d.ts +12 -1
  137. package/src/engine/graphics/trail/x/RibbonXPlugin.d.ts.map +1 -1
  138. package/src/engine/graphics/trail/x/RibbonXPlugin.js +15 -8
  139. package/src/engine/input/ecs/systems/InputControllerSystem.d.ts.map +1 -1
  140. package/src/engine/input/ecs/systems/InputControllerSystem.js +9 -6
  141. package/src/engine/navigation/ecs/components/Path.d.ts +5 -2
  142. package/src/engine/navigation/ecs/components/Path.d.ts.map +1 -1
  143. package/src/engine/navigation/ecs/components/Path.js +10 -4
  144. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +1 -1
  145. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  146. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"AnimationTrackPlayback.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/keyed2/AnimationTrackPlayback.js"],"names":[],"mappings":";AAGA;IACI;;;;;;;;OAQG;IACH,4EAuBC;IAtBG,sBAAkB;IAClB,cAA+B;IAE/B,oBAAoC;IACpC,kBAAgC;IAEhC,uBAAuB;IACvB,8BAA8B;IAE9B,uBAAuB;IACvB,sBAAsB;IAEtB,gBAAmB;IAEnB,4BAA0D;IAG1D;;MAEC;IAKL;;;OAGG;IACH,oBAEC;IAED;;;OAGG;IACH,gBAFa,QAAQ,IAAI,CAAC,CAUzB;IAED;;;OAGG;IACH,WAFW,OAAO,QAIjB;IAED,cAEC;IAED;;;OAGG;IACH,kBAFW,MAAM,QA2BhB;IAED;;;OAGG;IACH,cAFa,OAAO,CA2CnB;IAED;;;OAGG;IACH,mBAFW,MAAM,QA+BhB;IAED,eAOC;IAED;;;OAGG;IACH,0BAFW,MAAM,EAAE,QA2ClB;CACJ;mBAhPkB,uCAAuC"}
1
+ {"version":3,"file":"AnimationTrackPlayback.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/keyed2/AnimationTrackPlayback.js"],"names":[],"mappings":";AAGA;IACI;;;;;;;;OAQG;IACH,4EAuBC;IAtBG,sBAAkB;IAClB,cAA+B;IAE/B,oBAAoC;IACpC,kBAAgC;IAEhC,uBAAuB;IACvB,8BAA8B;IAE9B,uBAAuB;IACvB,sBAAsB;IAEtB,gBAAmB;IAEnB,4BAA0D;IAG1D;;MAEC;IAKL;;;OAGG;IACH,oBAEC;IAED;;;OAGG;IACH,gBAFa,QAAQ,IAAI,CAAC,CAUzB;IAED;;;OAGG;IACH,WAFW,OAAO,QAIjB;IAED,cAEC;IAED;;;OAGG;IACH,kBAFW,MAAM,QA2BhB;IAED;;;OAGG;IACH,cAFa,OAAO,CA2CnB;IAED;;;OAGG;IACH,mBAFW,MAAM,QAmChB;IAED,eAOC;IAED;;;OAGG;IACH,0BAFW,MAAM,EAAE,QA2ClB;CACJ;mBApPkB,uCAAuC"}
@@ -159,7 +159,11 @@ class AnimationTrackPlayback {
159
159
  if (timeDelta < 0) {
160
160
  //negative delta, going back in time
161
161
  this.setPosition(newPosition);
162
- } else if (newPosition > this.__nextKeyTime) {
162
+ // the `>= timeEnd` term catches a frame that lands EXACTLY on the end: __nextKeyTime equals
163
+ // timeEnd on the final segment, so the strict `> __nextKeyTime` test would skip it, and the
164
+ // one-shot `ended` (guarded by position < timeEnd) would then never fire. Mid-track exact
165
+ // landings do not reach timeEnd, so they stay on the plain else branch as before.
166
+ } else if (newPosition > this.__nextKeyTime || newPosition >= this.track.timeEnd) {
163
167
 
164
168
  if (newPosition >= this.track.timeEnd) {
165
169
  if (this.__loop) {
@@ -1 +1 @@
1
- {"version":3,"file":"EntityComponentDataset.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/EntityComponentDataset.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EntityComponentDataset.d.ts","sourceRoot":"","sources":["../../../../src/engine/ecs/EntityComponentDataset.js"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { assert } from "../../core/assert.js";
2
2
  import { BitSet } from "../../core/binary/BitSet.js";
3
3
  import { array_set_diff } from "../../core/collection/array/array_set_diff.js";
4
- import { array_shrink_to_size } from "../../core/collection/array/array_shrink_to_size.js";
5
4
  import { findSignalHandlerIndexByHandle } from "../../core/events/signal/findSignalHandlerIndexByHandle.js";
6
5
  import {
7
6
  findSignalHandlerIndexByHandleAndContext
@@ -107,18 +106,6 @@ function buildObserverCallbackArgs(entityIndex, mask, componentIndexMap, compone
107
106
  }
108
107
  }
109
108
 
110
- /**
111
- *
112
- * @type {number[]}
113
- */
114
- const scratch_indices = [];
115
-
116
- /**
117
- * Used for constructing arguments prior to traversal visitor call
118
- * @type {*[]}
119
- */
120
- const scratch_args = [];
121
-
122
109
  /**
123
110
  * Represents a storage for entities and their associated components.
124
111
  * Entities are just integer IDs and components are stored in a virtual table, where each component type has a separate column and each entity is a row in that table.
@@ -1327,8 +1314,10 @@ export class EntityComponentDataset {
1327
1314
 
1328
1315
  let entityIndex, i;
1329
1316
 
1330
- //map classes to indices
1331
- const indices = scratch_indices;
1317
+ // Local arrays rather than the module-shared scratch pools: the visitor may itself call
1318
+ // traverseEntities (an ordinary "for each A, scan the B entities" pattern), and a shared pool
1319
+ // would let the inner call clobber this call's indices/args mid-scan. Matches traverseEntitiesExact.
1320
+ const indices = [];
1332
1321
 
1333
1322
  // prepare sorted array of component indices
1334
1323
  const numClasses = classes.length;
@@ -1348,12 +1337,7 @@ export class EntityComponentDataset {
1348
1337
  indices[i] = componentIndex;
1349
1338
  }
1350
1339
 
1351
- const args = scratch_args;
1352
-
1353
- // crop args array to the right length (since we're re-using a scratch array)
1354
- const args_length_intended = numClasses + 1;
1355
-
1356
- array_shrink_to_size(scratch_args, args_length_intended);
1340
+ const args = [];
1357
1341
 
1358
1342
  const entityOccupancy = this.entityOccupancy;
1359
1343
 
@@ -28,6 +28,18 @@ export class FogOfWar {
28
28
  */
29
29
  size: Vector2;
30
30
  color: Vector4;
31
+ /**
32
+ * What the fog does outside the fogged area — see {@link FogOfWarEdgeMode}.
33
+ *
34
+ * NOT serialized: it is configuration the owner sets when it builds the
35
+ * component, not accumulated reveal state, and adding it to the binary format
36
+ * would cost a version bump plus an updater for every existing save. A
37
+ * deserialized FogOfWar therefore comes back as Conceal; a consumer that
38
+ * wants Extend sets it after loading, in the same place it sets {@link color}.
39
+ *
40
+ * @type {FogOfWarEdgeMode}
41
+ */
42
+ edge_mode: FogOfWarEdgeMode;
31
43
  /**
32
44
  * Contains indices of pixes that are currently being updated
33
45
  * @type {RowFirstTable}
@@ -74,6 +86,14 @@ export class FogOfWar {
74
86
  * @param {number} radius
75
87
  */
76
88
  conceal(x: number, y: number, radius: number): void;
89
+ /**
90
+ * Rebuild the cell -> fade row index from the current {@link fadeMask}
91
+ * contents. Must be invoked whenever the fade mask is populated without
92
+ * going through {@link reveal}/{@link conceal}/{@link revealPoint} (e.g.
93
+ * after deserialization), otherwise those methods could stack duplicate
94
+ * fade rows for a cell.
95
+ */
96
+ rebuildFadeIndex(): void;
77
97
  dispose(): void;
78
98
  updateTexture(): void;
79
99
  /**
@@ -117,6 +137,7 @@ export class FogOfWar {
117
137
  * @param scale
118
138
  */
119
139
  resize(w: number, h: number, scale: any): void;
140
+ #private;
120
141
  }
121
142
  export namespace FogOfWar {
122
143
  let typeName: string;
@@ -125,6 +146,7 @@ import { DataTexture } from "three";
125
146
  import Vector1 from "../../../core/geom/Vector1.js";
126
147
  import Vector2 from "../../../core/geom/Vector2.js";
127
148
  import Vector4 from "../../../core/geom/Vector4.js";
149
+ import { FogOfWarEdgeMode } from "./FogOfWarEdgeMode.js";
128
150
  import { RowFirstTable } from "../../../core/collection/table/RowFirstTable.js";
129
151
  import Signal from "../../../core/events/signal/Signal.js";
130
152
  import { Sampler2D } from "../../graphics/texture/sampler/Sampler2D.js";
@@ -1 +1 @@
1
- {"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWar.js"],"names":[],"mappings":"AA+CA;;GAEG;AACH;IAEI;;;OAGG;IACH,oBAFU,OAAO,CAES;IAC1B;;;OAGG;IACH,0BAFU,OAAO,CAEgB;IAEjC;;;OAGG;IACH,SAFU,WAAW,GAAC,IAAI,CAEX;IAEf;;;OAGG;IACH,OAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,OAAO,CAEQ;IAEzB,eAAsC;IAEtC;;;OAGG;IACH,UAFU,aAAa,CAE2B;IAElD;;MAEE;IAEF;;;OAGG;IACH,SAFU,SAAS,CAEgB;IAEnC;;;OAGG;IACH,iBAFU,SAAS,CAEwB;IAE3C;;;OAGG;IACH,sBAFW,MAAM,QAqDhB;IAED;;;;OAIG;IACH,eAHW,MAAM,KACN,MAAM,QAyBhB;IAED,cAOC;IAED,kBAOC;IAED,mBAEC;IAED;;;;;OAKG;IACH,UAJW,MAAM,KACN,MAAM,UACN,MAAM,QA+ChB;IAED;;;;;OAKG;IACH,WAJW,MAAM,KACN,MAAM,UACN,MAAM,QAgDhB;IAED,gBAIC;IAED,sBAqCC;IAED;;;OAGG;IACH,kBAFW,MAAM,QAYhB;IAED;;;OAGG;IACH,0DA+BC;IAED;;;;;OAKG;IACH,yBAJW,MAAM,KACN,MAAM,UACN,MAAM,EAAE,QAelB;IAED;;;;;OAKG;IACH,yEAFW,MAAM,WA+EhB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,KACN,MAAM,GACJ,MAAM,CASlB;IAED,6BAKC;IAED,+BASC;IAED,uBAyBC;IAGD;;;;;OAKG;IACH,UAJW,MAAM,KACN,MAAM,oBAUhB;CACJ;;;;4BAxlB2F,OAAO;oBAM/E,+BAA+B;oBAC/B,+BAA+B;oBAC/B,+BAA+B;8BALrB,iDAAiD;mBAE5D,uCAAuC;0BAQhC,6CAA6C"}
1
+ {"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWar.js"],"names":[],"mappings":"AAgDA;;GAEG;AACH;IAEI;;;OAGG;IACH,oBAFU,OAAO,CAES;IAC1B;;;OAGG;IACH,0BAFU,OAAO,CAEgB;IAEjC;;;OAGG;IACH,SAFU,WAAW,GAAC,IAAI,CAEX;IAEf;;;OAGG;IACH,OAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,OAAO,CAEQ;IAEzB,eAAsC;IAEtC;;;;;;;;;;OAUG;IACH,WAFU,gBAAgB,CAEW;IAErC;;;OAGG;IACH,UAFU,aAAa,CAE2B;IAWlD;;MAEE;IAEF;;;OAGG;IACH,SAFU,SAAS,CAEgB;IAEnC;;;OAGG;IACH,iBAFU,SAAS,CAEwB;IAE3C;;;OAGG;IACH,sBAFW,MAAM,QA6EhB;IA4DD;;;;OAIG;IACH,eAHW,MAAM,KACN,MAAM,QAUhB;IAED,cAQC;IAED,kBAQC;IAED,mBAEC;IAED;;;;;OAKG;IACH,UAJW,MAAM,KACN,MAAM,UACN,MAAM,QAmChB;IAED;;;;;OAKG;IACH,WAJW,MAAM,KACN,MAAM,UACN,MAAM,QAmChB;IAED;;;;;;OAMG;IACH,yBAcC;IAED,gBAIC;IAmDD,sBA2CC;IAED;;;OAGG;IACH,kBAFW,MAAM,QAYhB;IAED;;;OAGG;IACH,0DA+BC;IAED;;;;;OAKG;IACH,yBAJW,MAAM,KACN,MAAM,UACN,MAAM,EAAE,QAelB;IAED;;;;;OAKG;IACH,yEAFW,MAAM,WA+EhB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,KACN,MAAM,GACJ,MAAM,CASlB;IAED,6BAKC;IAED,+BASC;IAED,uBAyBC;IAGD;;;;;OAKG;IACH,UAJW,MAAM,KACN,MAAM,oBAahB;;CACJ;;;;4BA5uB2F,OAAO;oBAM/E,+BAA+B;oBAC/B,+BAA+B;oBAC/B,+BAA+B;iCAOlB,uBAAuB;8BAZ1B,iDAAiD;mBAE5D,uCAAuC;0BAQhC,6CAA6C"}
@@ -13,6 +13,7 @@ import { min2 } from "../../../core/math/min2.js";
13
13
  import { computeUnsignedDistanceField } from "../../graphics/texture/sampler/distance/computeUnsignedDistanceField.js";
14
14
  import { Sampler2D } from "../../graphics/texture/sampler/Sampler2D.js";
15
15
  import { writeSample2DDataToDataTexture } from "../../graphics/texture/sampler/writeSampler2DDataToDataTexture.js";
16
+ import { FogOfWarEdgeMode } from "./FogOfWarEdgeMode.js";
16
17
 
17
18
  const samplePosition = [];
18
19
 
@@ -81,12 +82,34 @@ export class FogOfWar {
81
82
 
82
83
  color = new Vector4(0.1, 0.1, 0.1, 1);
83
84
 
85
+ /**
86
+ * What the fog does outside the fogged area — see {@link FogOfWarEdgeMode}.
87
+ *
88
+ * NOT serialized: it is configuration the owner sets when it builds the
89
+ * component, not accumulated reveal state, and adding it to the binary format
90
+ * would cost a version bump plus an updater for every existing save. A
91
+ * deserialized FogOfWar therefore comes back as Conceal; a consumer that
92
+ * wants Extend sets it after loading, in the same place it sets {@link color}.
93
+ *
94
+ * @type {FogOfWarEdgeMode}
95
+ */
96
+ edge_mode = FogOfWarEdgeMode.Conceal;
97
+
84
98
  /**
85
99
  * Contains indices of pixes that are currently being updated
86
100
  * @type {RowFirstTable}
87
101
  */
88
102
  fadeMask = new RowFirstTable(revealMaskTableSpec);
89
103
 
104
+ /**
105
+ * Maps a sampler cell index to the row index of its in-progress fade in
106
+ * {@link fadeMask}. Enforces at most one fade row per cell, so that the
107
+ * latest reveal/conceal command for a cell re-aims its existing fade
108
+ * instead of stacking a second, opposing row.
109
+ * @type {Map<number, number>}
110
+ */
111
+ #fadeRowByCell = new Map();
112
+
90
113
  on = {
91
114
  textureChanged: new Signal()
92
115
  };
@@ -112,6 +135,8 @@ export class FogOfWar {
112
135
 
113
136
  const fadeMask = this.fadeMask;
114
137
 
138
+ const rowByCell = this.#fadeRowByCell;
139
+
115
140
  let numRows = fadeMask.length;
116
141
 
117
142
  let i = 0;
@@ -130,25 +155,24 @@ export class FogOfWar {
130
155
 
131
156
  let writeValue = value | 0;
132
157
 
133
- //write new value
158
+ //store the advanced precise value
134
159
  fadeMask.writeCellValue(i, 1, value);
135
160
 
161
+ let completed = false;
162
+
136
163
  if (value <= 0) {
164
+ writeValue = 0;
165
+
137
166
  if (speed < 0) {
138
- //we were going down and reached the end, clear row
139
- fadeMask.removeRows(i, 1);
140
- i--;
141
- numRows--;
167
+ //revealing fade reached the bottom
168
+ completed = true;
142
169
  }
143
- writeValue = 0;
144
170
  } else if (value >= 255) {
145
171
  writeValue = 255;
146
172
 
147
173
  if (speed > 0) {
148
- //we were going down and reached the end, clear row
149
- fadeMask.removeRows(i, 1);
150
- i--;
151
- numRows--;
174
+ //concealing fade reached the top
175
+ completed = true;
152
176
  }
153
177
  }
154
178
 
@@ -157,41 +181,108 @@ export class FogOfWar {
157
181
  samplerData[index] = writeValue;
158
182
  this.textureNeedsUpdate = true;
159
183
  }
184
+
185
+ if (completed) {
186
+ //Drop the finished row while keeping the cell->row index
187
+ //consistent. Swap the last row into this slot (O(1)) instead of
188
+ //shifting the whole tail forward, so only one other row's index
189
+ //needs to be patched in the map.
190
+ rowByCell.delete(index);
191
+
192
+ const lastRow = numRows - 1;
193
+
194
+ if (i !== lastRow) {
195
+ const movedCell = fadeMask.readCellValue(lastRow, 0);
196
+
197
+ fadeMask.copyRow(lastRow, i);
198
+
199
+ rowByCell.set(movedCell, i);
200
+ }
201
+
202
+ fadeMask.removeRows(lastRow, 1);
203
+
204
+ i--;
205
+ numRows--;
206
+ }
160
207
  }
161
208
  }
162
209
 
163
210
  /**
164
- *
165
- * @param {number} x
166
- * @param {number} y
211
+ * Schedule (or re-aim) a fade for a single sampler cell so that it converges
212
+ * to `target`. Guarantees at most one fade row per cell: if the cell already
213
+ * has an in-progress fade its direction is updated in place, so the latest
214
+ * command wins instead of stacking a second, opposing row.
215
+ * @param {number} index sampler cell index
216
+ * @param {number} target target sampler value, either 0 (revealed) or 255 (concealed)
217
+ * @returns {boolean} whether the visible sampler value was changed (i.e. the distance field may need a rebuild)
167
218
  */
168
- revealPoint(x, y) {
219
+ #scheduleCellFade(index, target) {
169
220
  const fadeMask = this.fadeMask;
221
+ const rowByCell = this.#fadeRowByCell;
170
222
 
171
- const sampler = this.sampler;
223
+ const speed = target === 0 ? -FADE_SPEED : FADE_SPEED;
172
224
 
173
- const samplerData = sampler.data;
225
+ const existingRow = rowByCell.get(index);
174
226
 
175
- const samplerWidth = sampler.width;
227
+ if (existingRow !== undefined) {
228
+ //A fade is already in progress for this cell, re-aim it in place.
229
+ //The precise value column is left untouched so the fade resumes from
230
+ //its current progress and never snaps back through the far end.
231
+ fadeMask.writeCellValue(existingRow, 2, speed);
176
232
 
177
- const index = (y + 1) * samplerWidth + (x + 1);
233
+ return false;
234
+ }
235
+
236
+ const samplerData = this.sampler.data;
237
+
238
+ const current = samplerData[index];
239
+
240
+ if (current === target) {
241
+ //already fully at the target, nothing to fade
242
+ return false;
243
+ }
244
+
245
+ let seedValue = current;
246
+ let visibleChanged = false;
178
247
 
179
- if (samplerData[index] === 255) {
248
+ if (target === 0 && current === 255) {
249
+ //pre-fade off the fully-concealed value so the distance field
250
+ //immediately treats this cell as being revealed
251
+ seedValue = INITIAL_FADE_VALUE;
180
252
  samplerData[index] = INITIAL_FADE_VALUE;
253
+ visibleChanged = true;
254
+ }
181
255
 
182
- //add record to fade mask
183
- fadeRow[0] = index;
184
- fadeRow[1] = INITIAL_FADE_VALUE;
185
- fadeRow[2] = -FADE_SPEED;
256
+ //add record to fade mask
257
+ fadeRow[0] = index;
258
+ fadeRow[1] = seedValue;
259
+ fadeRow[2] = speed;
186
260
 
187
- fadeMask.addRow(fadeRow);
261
+ const rowIndex = fadeMask.addRow(fadeRow);
262
+
263
+ rowByCell.set(index, rowIndex);
264
+
265
+ return visibleChanged;
266
+ }
188
267
 
268
+ /**
269
+ *
270
+ * @param {number} x
271
+ * @param {number} y
272
+ */
273
+ revealPoint(x, y) {
274
+ const samplerWidth = this.sampler.width;
275
+
276
+ const index = (y + 1) * samplerWidth + (x + 1);
277
+
278
+ if (this.#scheduleCellFade(index, 0)) {
189
279
  this.distanceFieldNeedsUpdate = true;
190
280
  }
191
281
  }
192
282
 
193
283
  clear() {
194
284
  this.fadeMask.clear();
285
+ this.#fadeRowByCell.clear();
195
286
 
196
287
  this.sampler.data.fill(255);
197
288
 
@@ -201,6 +292,7 @@ export class FogOfWar {
201
292
 
202
293
  revealAll() {
203
294
  this.fadeMask.clear();
295
+ this.#fadeRowByCell.clear();
204
296
 
205
297
  this.sampler.data.fill(0);
206
298
 
@@ -230,11 +322,8 @@ export class FogOfWar {
230
322
  const lastPixelX = samplerWidth - 1;
231
323
 
232
324
  let changeFlag = false;
233
- const samplerData = sampler.data;
234
325
 
235
- const fadeMask = this.fadeMask;
236
-
237
- sampler.traverseCircle(x + 1, y + 1, radius, function (x, y, sampler) {
326
+ sampler.traverseCircle(x + 1, y + 1, radius, (x, y) => {
238
327
 
239
328
  if (x <= 0 || x >= lastPixelX || y <= 0 || y >= lastPixelY) {
240
329
  //edges are reserved to preserve cover outside of the working area
@@ -243,16 +332,7 @@ export class FogOfWar {
243
332
 
244
333
  const index = y * samplerWidth + x;
245
334
 
246
- if (samplerData[index] === 255) {
247
- samplerData[index] = INITIAL_FADE_VALUE;
248
-
249
- //add record to fade mask
250
- fadeRow[0] = index;
251
- fadeRow[1] = INITIAL_FADE_VALUE;
252
- fadeRow[2] = -FADE_SPEED;
253
-
254
- fadeMask.addRow(fadeRow);
255
-
335
+ if (this.#scheduleCellFade(index, 0)) {
256
336
  changeFlag = true;
257
337
  }
258
338
 
@@ -283,11 +363,8 @@ export class FogOfWar {
283
363
  const lastPixelX = samplerWidth - 1;
284
364
 
285
365
  let changeFlag = false;
286
- const samplerData = sampler.data;
287
-
288
- const fadeMask = this.fadeMask;
289
366
 
290
- sampler.traverseCircle(x + 1, y + 1, radius, function (x, y, sampler) {
367
+ sampler.traverseCircle(x + 1, y + 1, radius, (x, y) => {
291
368
 
292
369
  if (x <= 0 || x >= lastPixelX || y <= 0 || y >= lastPixelY) {
293
370
  //edges are reserved to preserve cover outside of the working area
@@ -296,17 +373,7 @@ export class FogOfWar {
296
373
 
297
374
  const index = y * samplerWidth + x;
298
375
 
299
- const value = samplerData[index];
300
-
301
- if (value !== 255) {
302
-
303
- //add record to fade mask
304
- fadeRow[0] = index;
305
- fadeRow[1] = value;
306
- fadeRow[2] = FADE_SPEED;
307
-
308
- fadeMask.addRow(fadeRow);
309
-
376
+ if (this.#scheduleCellFade(index, 255)) {
310
377
  changeFlag = true;
311
378
  }
312
379
 
@@ -319,13 +386,91 @@ export class FogOfWar {
319
386
  }
320
387
  }
321
388
 
389
+ /**
390
+ * Rebuild the cell -> fade row index from the current {@link fadeMask}
391
+ * contents. Must be invoked whenever the fade mask is populated without
392
+ * going through {@link reveal}/{@link conceal}/{@link revealPoint} (e.g.
393
+ * after deserialization), otherwise those methods could stack duplicate
394
+ * fade rows for a cell.
395
+ */
396
+ rebuildFadeIndex() {
397
+ const rowByCell = this.#fadeRowByCell;
398
+
399
+ rowByCell.clear();
400
+
401
+ const fadeMask = this.fadeMask;
402
+
403
+ const numRows = fadeMask.length;
404
+
405
+ for (let i = 0; i < numRows; i++) {
406
+ const index = fadeMask.readCellValue(i, 0);
407
+
408
+ rowByCell.set(index, i);
409
+ }
410
+ }
411
+
322
412
  dispose() {
323
413
  if (this.texture !== null) {
324
414
  this.texture.dispose();
325
415
  }
326
416
  }
327
417
 
418
+ /**
419
+ * Copy the sampler's outermost REAL cells out into the 1-cell ring that
420
+ * {@link rebuildSampler} pads it with.
421
+ *
422
+ * That ring is padding — it exists so the shader's 5x5 blur and the distance
423
+ * field have something to read past the edge — and it is filled with full
424
+ * concealment. The blur then drags that concealment ~2 cells back INTO the
425
+ * fogged area, so the outermost cells of the world can never read as fully
426
+ * revealed however thoroughly they are explored; and every lookup from outside
427
+ * the area clamps onto it, which is what paints the void. Mirroring makes the
428
+ * padding agree with the edge it pads, on both counts.
429
+ *
430
+ * O(perimeter), and only on frames that upload a new texture.
431
+ *
432
+ * @private
433
+ */
434
+ #mirrorBorderRing() {
435
+ const s = this.sampler;
436
+
437
+ const w = s.width;
438
+ const h = s.height;
439
+
440
+ if (w < 3 || h < 3) {
441
+ // no interior to mirror from
442
+ return;
443
+ }
444
+
445
+ const data = s.data;
446
+
447
+ const last_row = (h - 1) * w;
448
+ const second_last_row = (h - 2) * w;
449
+
450
+ // top and bottom edges, corners excluded — those come from the sides below
451
+ for (let x = 1; x < w - 1; x++) {
452
+ data[x] = data[w + x];
453
+ data[last_row + x] = data[second_last_row + x];
454
+ }
455
+
456
+ // left and right edges, corners INCLUDED: the row above and below have
457
+ // already been mirrored, so a corner ends up with its diagonal neighbour's
458
+ // value rather than an arbitrary one
459
+ for (let y = 0; y < h; y++) {
460
+ const row = y * w;
461
+
462
+ data[row] = data[row + 1];
463
+ data[row + w - 1] = data[row + w - 2];
464
+ }
465
+ }
466
+
328
467
  updateTexture() {
468
+ if (this.edge_mode === FogOfWarEdgeMode.Extend) {
469
+ // do this HERE, at the single point every reveal path funnels through
470
+ // on its way to the GPU, rather than in each of them
471
+ this.#mirrorBorderRing();
472
+ }
473
+
329
474
  const s = this.sampler;
330
475
 
331
476
  /**
@@ -594,7 +739,10 @@ export class FogOfWar {
594
739
  this.size.set(w, h);
595
740
  this.scale.set(scale);
596
741
 
597
- //TODO update fade mask as necessary
742
+ //TODO update fade mask as necessary - the sampler indices change with the
743
+ //new width, so any in-progress fade rows (and the #fadeRowByCell index that
744
+ //mirrors them) would need remapping or clearing. Only reachable at link time
745
+ //today, when both are empty.
598
746
 
599
747
  this.rebuildSampler();
600
748
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * What the fog of war does AT, and beyond, the edge of the fogged area.
3
+ *
4
+ * The fog is a screen-space effect (see screenSpaceFogOfWarShader): per pixel it
5
+ * reconstructs a world position from the depth buffer and reads that position out
6
+ * of the fog sampler. Neither end of that pipeline is defined outside the fogged
7
+ * rectangle — a pixel with no geometry has no world position to reconstruct, and
8
+ * a world position outside the sampler has no cell to read — so what happens
9
+ * there is a POLICY rather than a fact, and this is the knob for it.
10
+ */
11
+ export type FogOfWarEdgeMode = number;
12
+ export namespace FogOfWarEdgeMode {
13
+ let Conceal: number;
14
+ let Extend: number;
15
+ }
16
+ //# sourceMappingURL=FogOfWarEdgeMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FogOfWarEdgeMode.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWarEdgeMode.js"],"names":[],"mappings":";;;;;;;;;;+BAUU,MAAM"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What the fog of war does AT, and beyond, the edge of the fogged area.
3
+ *
4
+ * The fog is a screen-space effect (see screenSpaceFogOfWarShader): per pixel it
5
+ * reconstructs a world position from the depth buffer and reads that position out
6
+ * of the fog sampler. Neither end of that pipeline is defined outside the fogged
7
+ * rectangle — a pixel with no geometry has no world position to reconstruct, and
8
+ * a world position outside the sampler has no cell to read — so what happens
9
+ * there is a POLICY rather than a fact, and this is the knob for it.
10
+ *
11
+ * @enum {number}
12
+ */
13
+ export const FogOfWarEdgeMode = {
14
+ /**
15
+ * Everything outside the fogged area is CONCEALED: the sampler's border ring
16
+ * is left at full concealment, and a pixel with no geometry takes whatever
17
+ * the clamped lookup returns — which is that same border.
18
+ *
19
+ * The default, and the behaviour of every release before this option existed.
20
+ * Right for a world that fills the screen, where "outside" is somewhere the
21
+ * player is never shown.
22
+ */
23
+ Conceal: 0,
24
+
25
+ /**
26
+ * The fog EXTENDS outward instead: the border ring mirrors the cells just
27
+ * inside it, and pixels with no geometry are left alone entirely.
28
+ *
29
+ * For a world with a VISIBLE boundary — a finite arena the camera can see
30
+ * past. Under {@link FogOfWarEdgeMode.Conceal} such a scene gets a hard black
31
+ * rectangle drawn exactly on its boundary (the concealed border ring, clamped
32
+ * across the whole void), which both advertises where the map stops and paints
33
+ * over anything drawn earlier to hide it — this layer composites last, over
34
+ * every other effect. Under Extend the fog agrees with the ground it borders:
35
+ * explored terrain stays clear right out past the boundary, unexplored terrain
36
+ * stays dark, and nothing marks where the geometry ends.
37
+ */
38
+ Extend: 1,
39
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"FogOfWarSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWarSystem.js"],"names":[],"mappings":"AAwBA;IACI;;;OAGG;IACH,sCA0BC;IAvBG;;;OAGG;IACH,yBAAwB;IAExB,mDAAuC;IAEvC,gEAEC;IAED,mCAAoD;IAIpD;;;OAGG;IACH,eAFU,aAAa,CAEE;IAEzB,uBAAuB;IAG3B;;;OAGG;IACH,yBAFa,gBAAgB,CAuE5B;IAsBG,kBAAgC;IA+DpC,4CAEC;IAED;;;;;OAKG;IACH,UAJW,QAAQ,WACR,OAAO,UACP,MAAM,QAmBhB;IAED,kDAQC;IAED,6BAUC;CACJ;uBAzPsB,cAAc;oBACjB,2BAA2B;yBACtB,eAAe;4CAdI,oDAAoD;iCAS/D,sDAAsD;8BAPzD,0CAA0C"}
1
+ {"version":3,"file":"FogOfWarSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWarSystem.js"],"names":[],"mappings":"AAwBA;IACI;;;OAGG;IACH,sCA0BC;IAvBG;;;OAGG;IACH,yBAAwB;IAExB,mDAAuC;IAEvC,gEAEC;IAED,mCAAoD;IAIpD;;;OAGG;IACH,eAFU,aAAa,CAEE;IAEzB,uBAAuB;IAG3B;;;OAGG;IACH,yBAFa,gBAAgB,CAuE5B;IAsBG,kBAAgC;IAgEpC,4CAEC;IAED;;;;;OAKG;IACH,UAJW,QAAQ,WACR,OAAO,UACP,MAAM,QAmBhB;IAED,kDAQC;IAED,6BAUC;CACJ;uBA1PsB,cAAc;oBACjB,2BAA2B;yBACtB,eAAe;4CAdI,oDAAoD;iCAS/D,sDAAsD;8BAPzD,0CAA0C"}
@@ -178,6 +178,7 @@ export class FogOfWarSystem extends System {
178
178
  fogOfWarRenderer.setResolution(fow.size.x, fow.size.y);
179
179
  fogOfWarRenderer.setUvTransformFromFog(fow);
180
180
  fogOfWarRenderer.setFogColor(fow.color);
181
+ fogOfWarRenderer.setEdgeMode(fow.edge_mode);
181
182
 
182
183
 
183
184
  //stop traversal
@@ -1 +1 @@
1
- {"version":3,"file":"FogOfWarSerializationAdapter.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/serialization/FogOfWarSerializationAdapter.js"],"names":[],"mappings":"AAQA;;IAEI,uBAAiB;IAGjB,oDAA2D;IAE3D;;;;OAIG;IACH,uCAFW,QAAQ,QAyBlB;IAED;;;;OAIG;IACH,yCAFW,QAAQ,QAgClB;CACJ;gDAjF+C,yDAAyD;yBAChF,gBAAgB;8CAKlC,kFAAkF"}
1
+ {"version":3,"file":"FogOfWarSerializationAdapter.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/serialization/FogOfWarSerializationAdapter.js"],"names":[],"mappings":"AAQA;;IAEI,uBAAiB;IAGjB,oDAA2D;IAE3D;;;;OAIG;IACH,uCAFW,QAAQ,QAyBlB;IAED;;;;OAIG;IACH,yCAFW,QAAQ,QAmClB;CACJ;gDApF+C,yDAAyD;yBAChF,gBAAgB;8CAKlC,kFAAkF"}
@@ -75,6 +75,9 @@ export class FogOfWarSerializationAdapter extends BinaryClassSerializationAdapte
75
75
  //deserialize reveal state
76
76
  deserializeRowFirstTable(buffer, value.fadeMask);
77
77
 
78
+ //rebuild the cell -> fade row index to match the restored fade mask
79
+ value.rebuildFadeIndex();
80
+
78
81
  //
79
82
  value.textureNeedsUpdate = true;
80
83
  value.rebuildDistanceSampler();
@@ -47,6 +47,12 @@ export class FogOfWarRenderer {
47
47
  * @param {Vector4} color
48
48
  */
49
49
  setFogColor(color: Vector4): void;
50
+ /**
51
+ * What the fog does outside the fogged area. See {@link FogOfWarEdgeMode}.
52
+ *
53
+ * @param {FogOfWarEdgeMode} mode
54
+ */
55
+ setEdgeMode(mode: FogOfWarEdgeMode): void;
50
56
  /**
51
57
  *
52
58
  * @param {WebGLRenderer} renderer
@@ -59,4 +65,5 @@ export class FogOfWarRenderer {
59
65
  import { Mesh } from "three";
60
66
  import { OrthographicCamera } from "three";
61
67
  import { Scene } from "three";
68
+ import { FogOfWarEdgeMode } from "../FogOfWarEdgeMode.js";
62
69
  //# sourceMappingURL=FogOfWarRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FogOfWarRenderer.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/FogOfWarRenderer.js"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,+EAFoB,MAAM,QAAE,MAAM,QAAE,MAAM,QAAE,MAAM,gBAiBjD;AAED;IAEQ;;;OAGG;IACH,yBAAgD;IAEhD,2DAAkE;IAElE,2BAAwD;IACxD,aAAwB;IAI5B;;;OAGG;IACH,2CAKC;IAED;;;;;;OAMG;IACH,wBALW,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,QAqBhB;IAED;;;OAGG;IACH,uCAIC;IAED;;;OAGG;IACH,oCAGC;IAED;;;;OAIG;IACH,iBAHW,MAAM,KACN,MAAM,QAIhB;IAED;;;OAGG;IACH,kCAEC;IAED;;;;;;OAMG;IACH,wCAJW,oBAAkB,kBAAkB,SACpC,KAAK,mCAqBf;CACJ;qBAhJ+C,OAAO;mCAAP,OAAO;sBAAP,OAAO"}
1
+ {"version":3,"file":"FogOfWarRenderer.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/FogOfWarRenderer.js"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,+EAFoB,MAAM,QAAE,MAAM,QAAE,MAAM,QAAE,MAAM,gBAiBjD;AAED;IAEQ;;;OAGG;IACH,yBAAgD;IAEhD,2DAAkE;IAElE,2BAAwD;IACxD,aAAwB;IAI5B;;;OAGG;IACH,2CAKC;IAED;;;;;;OAMG;IACH,wBALW,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,QAqBhB;IAED;;;OAGG;IACH,uCAIC;IAED;;;OAGG;IACH,oCAGC;IAED;;;;OAIG;IACH,iBAHW,MAAM,KACN,MAAM,QAIhB;IAED;;;OAGG;IACH,kCAEC;IAED;;;;OAIG;IACH,kBAFW,gBAAgB,QAI1B;IAED;;;;;;OAMG;IACH,wCAJW,oBAAkB,kBAAkB,SACpC,KAAK,mCAqBf;CACJ;qBA1J+C,OAAO;mCAAP,OAAO;sBAAP,OAAO;iCAItB,wBAAwB"}