@woosh/meep-engine 3.64.0 → 3.65.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 (370) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/build/bundle-worker-image-decoder.js +1 -1
  3. package/build/bundle-worker-terrain.js +1 -1
  4. package/package.json +1 -1
  5. package/src/core/binary/BinaryBuffer.d.ts +5 -0
  6. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  7. package/src/core/binary/BinaryBuffer.js +39 -5
  8. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts +65 -6
  9. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts.map +1 -1
  10. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +451 -101
  11. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts +4 -2
  12. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -1
  13. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +4 -2
  14. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts +8 -1
  15. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts.map +1 -1
  16. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +45 -10
  17. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  18. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +237 -119
  19. package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts +143 -0
  20. package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts.map +1 -0
  21. package/src/engine/graphics/ecs/grass/GrassSpecies.js +189 -0
  22. package/src/engine/graphics/ecs/grass/GrassSurface.d.ts +25 -0
  23. package/src/engine/graphics/ecs/grass/GrassSurface.d.ts.map +1 -0
  24. package/src/engine/graphics/ecs/grass/GrassSurface.js +32 -0
  25. package/src/engine/graphics/ecs/grass/GroundCover.d.ts +68 -0
  26. package/src/engine/graphics/ecs/grass/GroundCover.d.ts.map +1 -0
  27. package/src/engine/graphics/ecs/grass/GroundCover.js +85 -0
  28. package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts +27 -0
  29. package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts.map +1 -0
  30. package/src/engine/graphics/texture/sampler/exr_to_sampler2d.js +91 -0
  31. package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts +23 -0
  32. package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts.map +1 -0
  33. package/src/engine/graphics/texture/sampler/sampler2d_to_exr.js +64 -0
  34. package/src/engine/graphics3/GrassSurfaceSystem.d.ts +153 -0
  35. package/src/engine/graphics3/GrassSurfaceSystem.d.ts.map +1 -0
  36. package/src/engine/graphics3/GrassSurfaceSystem.js +1076 -0
  37. package/src/engine/graphics3/GroundCoverSystem.d.ts +33 -0
  38. package/src/engine/graphics3/GroundCoverSystem.d.ts.map +1 -0
  39. package/src/engine/graphics3/GroundCoverSystem.js +58 -0
  40. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  41. package/src/engine/network/NetworkSession.js +372 -110
  42. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  43. package/src/engine/network/orchestrator/NetworkPeer.js +6 -1
  44. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  45. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +9 -2
  46. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  47. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +325 -140
  48. package/src/engine/network/sim/SimActionExecutor.d.ts +4 -1
  49. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  50. package/src/engine/network/sim/SimActionExecutor.js +65 -9
  51. package/src/engine/network/state/PredictionHistory.d.ts.map +1 -1
  52. package/src/engine/network/state/PredictionHistory.js +6 -1
  53. package/src/engine/network/time/AdaptiveRenderDelay.d.ts +25 -14
  54. package/src/engine/network/time/AdaptiveRenderDelay.d.ts.map +1 -1
  55. package/src/engine/network/time/AdaptiveRenderDelay.js +38 -20
  56. package/src/engine/network/time/RenderPlayout.d.ts +23 -3
  57. package/src/engine/network/time/RenderPlayout.d.ts.map +1 -1
  58. package/src/engine/network/time/RenderPlayout.js +40 -4
  59. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  60. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts +5 -1
  61. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts.map +1 -1
  62. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.js +27 -1
  63. package/src/format/image/exr/ExrCompression.d.ts +22 -0
  64. package/src/format/image/exr/ExrCompression.d.ts.map +1 -0
  65. package/src/format/image/exr/ExrCompression.js +27 -0
  66. package/src/format/image/exr/ExrImage.d.ts +32 -0
  67. package/src/format/image/exr/ExrImage.d.ts.map +1 -0
  68. package/src/format/image/exr/ExrImage.js +32 -0
  69. package/src/format/image/exr/ExrPixelType.d.ts +13 -0
  70. package/src/format/image/exr/ExrPixelType.d.ts.map +1 -0
  71. package/src/format/image/exr/ExrPixelType.js +18 -0
  72. package/src/format/image/exr/exr_compare_names.d.ts +16 -0
  73. package/src/format/image/exr/exr_compare_names.d.ts.map +1 -0
  74. package/src/format/image/exr/exr_compare_names.js +25 -0
  75. package/src/format/image/exr/exr_constants.d.ts +49 -0
  76. package/src/format/image/exr/exr_constants.d.ts.map +1 -0
  77. package/src/format/image/exr/exr_constants.js +56 -0
  78. package/src/format/image/exr/exr_read.d.ts +32 -0
  79. package/src/format/image/exr/exr_read.d.ts.map +1 -0
  80. package/src/format/image/exr/exr_read.js +531 -0
  81. package/src/format/image/exr/exr_sample_bits.d.ts +16 -0
  82. package/src/format/image/exr/exr_sample_bits.d.ts.map +1 -0
  83. package/src/format/image/exr/exr_sample_bits.js +21 -0
  84. package/src/format/image/exr/exr_scanlines_per_chunk.d.ts +17 -0
  85. package/src/format/image/exr/exr_scanlines_per_chunk.d.ts.map +1 -0
  86. package/src/format/image/exr/exr_scanlines_per_chunk.js +31 -0
  87. package/src/format/image/exr/exr_write.d.ts +27 -0
  88. package/src/format/image/exr/exr_write.d.ts.map +1 -0
  89. package/src/format/image/exr/exr_write.js +378 -0
  90. package/src/shade/device/shader/ShaderModuleCompiler.d.ts +2 -0
  91. package/src/shade/device/shader/ShaderModuleCompiler.d.ts.map +1 -1
  92. package/src/shade/device/shader/ShaderModuleCompiler.js +25 -2
  93. package/src/shade/playground/grass_surfaces/index.html +75 -0
  94. package/src/shade/playground/grass_surfaces/main.d.ts +2 -0
  95. package/src/shade/playground/grass_surfaces/main.d.ts.map +1 -0
  96. package/src/shade/playground/grass_surfaces/main.js +593 -0
  97. package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts +62 -0
  98. package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts.map +1 -0
  99. package/src/shade/playground/grass_surfaces/read_grass_surfaces.js +188 -0
  100. package/src/shade/playground/probe_clipmap/index.html +5 -0
  101. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  102. package/src/shade/playground/probe_clipmap/main.js +34 -1
  103. package/src/shade/playground/probe_clipmap_box/README.md +5 -1
  104. package/src/shade/playground/probe_clipmap_box/index.html +12 -0
  105. package/src/shade/playground/probe_clipmap_box/main.d.ts.map +1 -1
  106. package/src/shade/playground/probe_clipmap_box/main.js +15 -0
  107. package/src/shade/playground/probe_clipmap_box/scene.d.ts +8 -1
  108. package/src/shade/playground/probe_clipmap_box/scene.d.ts.map +1 -1
  109. package/src/shade/playground/probe_clipmap_box/scene.js +74 -6
  110. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  111. package/src/shade/renderer/Renderer.js +47 -43
  112. package/src/shade/renderer/extension/ViewTextures.d.ts +12 -5
  113. package/src/shade/renderer/extension/ViewTextures.d.ts.map +1 -1
  114. package/src/shade/renderer/extension/ViewTextures.js +19 -5
  115. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +3 -1
  116. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts.map +1 -1
  117. package/src/shade/renderer/fluid/GPUFluidCascadeStack.js +6 -1
  118. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +7 -0
  119. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts.map +1 -1
  120. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.js +10 -0
  121. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +7 -1
  122. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  123. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +46 -15
  124. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts +3 -2
  125. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts.map +1 -1
  126. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.js +3 -2
  127. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts +22 -0
  128. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts.map +1 -0
  129. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.js +247 -0
  130. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +50 -0
  131. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  132. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +101 -0
  133. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +2 -1
  134. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  135. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +2 -1
  136. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +8 -0
  137. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
  138. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +12 -0
  139. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts +12 -6
  140. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts.map +1 -1
  141. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.js +35 -29
  142. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.d.ts.map +1 -1
  143. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.js +4 -2
  144. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.d.ts.map +1 -1
  145. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.js +6 -4
  146. package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.d.ts.map +1 -1
  147. package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.js +2 -1
  148. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts +6 -5
  149. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts.map +1 -1
  150. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.js +15 -14
  151. package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.d.ts.map +1 -1
  152. package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.js +4 -2
  153. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +19 -2
  154. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  155. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +128 -10
  156. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +19 -13
  157. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  158. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +27 -15
  159. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts +19 -11
  160. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts.map +1 -1
  161. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.js +35 -11
  162. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +3 -3
  163. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  164. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +18 -14
  165. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts +40 -0
  166. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts.map +1 -0
  167. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.js +174 -0
  168. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.d.ts +4 -4
  169. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.js +10 -10
  170. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts +31 -0
  171. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts.map +1 -0
  172. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.js +54 -0
  173. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts +8 -4
  174. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  175. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +10 -21
  176. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts +7 -2
  177. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts.map +1 -1
  178. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.js +33 -29
  179. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts +28 -0
  180. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts.map +1 -0
  181. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.js +138 -0
  182. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +7 -3
  183. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  184. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +28 -17
  185. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +13 -9
  186. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts.map +1 -1
  187. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +18 -11
  188. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +1 -1
  189. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +18 -18
  190. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
  191. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +44 -48
  192. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.d.ts.map +1 -1
  193. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js +4 -2
  194. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts.map +1 -1
  195. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js +2 -3
  196. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts +5 -3
  197. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts.map +1 -1
  198. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js +15 -30
  199. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -1
  200. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
  201. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +4 -2
  202. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts +11 -0
  203. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts.map +1 -1
  204. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +29 -2
  205. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts +40 -0
  206. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts.map +1 -0
  207. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.js +126 -0
  208. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts +36 -0
  209. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts.map +1 -0
  210. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.js +221 -0
  211. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts +40 -0
  212. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts.map +1 -0
  213. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.js +86 -0
  214. package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts +135 -0
  215. package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts.map +1 -0
  216. package/src/shade/renderer/grass/blade/GPUGrassBlades.js +676 -0
  217. package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts +145 -0
  218. package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts.map +1 -0
  219. package/src/shade/renderer/grass/blade/GRASS_BLADE.js +179 -0
  220. package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts +63 -0
  221. package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts.map +1 -0
  222. package/src/shade/renderer/grass/blade/chunk_grass_blade.js +244 -0
  223. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts +58 -0
  224. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts.map +1 -0
  225. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.js +174 -0
  226. package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts +25 -0
  227. package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts.map +1 -0
  228. package/src/shade/renderer/grass/blade/grass_blade_id_draw.js +133 -0
  229. package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts +80 -0
  230. package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts.map +1 -0
  231. package/src/shade/renderer/grass/blade/grass_blade_mechanics.js +92 -0
  232. package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts +29 -0
  233. package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts.map +1 -0
  234. package/src/shade/renderer/grass/blade/grass_blade_resolve.js +175 -0
  235. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts +14 -0
  236. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts.map +1 -0
  237. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.js +45 -0
  238. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts +61 -0
  239. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts.map +1 -0
  240. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.js +340 -0
  241. package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts +46 -0
  242. package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts.map +1 -0
  243. package/src/shade/renderer/grass/blade/shader_grass_cell_count.js +280 -0
  244. package/src/shade/renderer/grass/population/chunk_grass_population.d.ts +60 -0
  245. package/src/shade/renderer/grass/population/chunk_grass_population.d.ts.map +1 -0
  246. package/src/shade/renderer/grass/population/chunk_grass_population.js +171 -0
  247. package/src/shade/renderer/grass/press/GPUGrassPress.d.ts +99 -0
  248. package/src/shade/renderer/grass/press/GPUGrassPress.d.ts.map +1 -0
  249. package/src/shade/renderer/grass/press/GPUGrassPress.js +436 -0
  250. package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts +179 -0
  251. package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts.map +1 -0
  252. package/src/shade/renderer/grass/press/GRASS_PRESS.js +217 -0
  253. package/src/shade/renderer/grass/press/chunk_grass_press.d.ts +65 -0
  254. package/src/shade/renderer/grass/press/chunk_grass_press.d.ts.map +1 -0
  255. package/src/shade/renderer/grass/press/chunk_grass_press.js +296 -0
  256. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts +31 -0
  257. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts.map +1 -0
  258. package/src/shade/renderer/grass/press/shader_grass_press_mark.js +169 -0
  259. package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts +27 -0
  260. package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts.map +1 -0
  261. package/src/shade/renderer/grass/press/shader_grass_press_stamp.js +183 -0
  262. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts +193 -0
  263. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts.map +1 -0
  264. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.js +1253 -0
  265. package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts +211 -0
  266. package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts.map +1 -0
  267. package/src/shade/renderer/grass/surface/GRASS_SURFACE.js +241 -0
  268. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts +82 -0
  269. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts.map +1 -0
  270. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.js +111 -0
  271. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts +39 -0
  272. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts.map +1 -0
  273. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.js +85 -0
  274. package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts +29 -0
  275. package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts.map +1 -0
  276. package/src/shade/renderer/grass/surface/chunk_grass_directory.js +112 -0
  277. package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts +27 -0
  278. package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts.map +1 -0
  279. package/src/shade/renderer/grass/surface/chunk_grass_slot.js +50 -0
  280. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts +21 -0
  281. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts.map +1 -0
  282. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.js +41 -0
  283. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts +32 -0
  284. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts.map +1 -0
  285. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.js +136 -0
  286. package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts +37 -0
  287. package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts.map +1 -0
  288. package/src/shade/renderer/grass/surface/chunk_grass_surfel.js +85 -0
  289. package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts +25 -0
  290. package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts.map +1 -0
  291. package/src/shade/renderer/grass/surface/chunk_grass_tile.js +52 -0
  292. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts +23 -0
  293. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts.map +1 -0
  294. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.js +87 -0
  295. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts +43 -0
  296. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts.map +1 -0
  297. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.js +265 -0
  298. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts +13 -0
  299. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts.map +1 -0
  300. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.js +49 -0
  301. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts +62 -0
  302. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts.map +1 -0
  303. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.js +162 -0
  304. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts +40 -0
  305. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts.map +1 -0
  306. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.js +155 -0
  307. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts +19 -0
  308. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts.map +1 -0
  309. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.js +58 -0
  310. package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts +30 -0
  311. package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts.map +1 -0
  312. package/src/shade/renderer/grass/surface/shader_grass_surface_want.js +251 -0
  313. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts +21 -0
  314. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts.map +1 -0
  315. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.js +75 -0
  316. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
  317. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +4 -23
  318. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts +12 -0
  319. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts.map +1 -0
  320. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.js +29 -0
  321. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  322. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +22 -0
  323. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  324. package/src/shade/renderer/scene/GPUSceneContext.js +3 -0
  325. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts +27 -0
  326. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts.map +1 -0
  327. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.js +38 -0
  328. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts +28 -0
  329. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts.map +1 -0
  330. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.js +58 -0
  331. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
  332. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +8 -0
  333. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts +8 -0
  334. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts.map +1 -1
  335. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.js +21 -3
  336. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts +37 -0
  337. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts.map +1 -0
  338. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.js +148 -0
  339. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts +20 -0
  340. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts.map +1 -0
  341. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.js +169 -0
  342. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts +18 -0
  343. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts.map +1 -1
  344. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.js +28 -0
  345. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts +13 -0
  346. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts.map +1 -0
  347. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.js +22 -0
  348. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  349. package/src/shade/renderer/water/GPUWaterRenderer.js +4 -0
  350. package/src/shade/renderer/wind/GPUSceneWind.d.ts +52 -5
  351. package/src/shade/renderer/wind/GPUSceneWind.d.ts.map +1 -1
  352. package/src/shade/renderer/wind/GPUSceneWind.js +164 -8
  353. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts +3 -1
  354. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts.map +1 -1
  355. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.js +3 -1
  356. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts +2 -1
  357. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts.map +1 -1
  358. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.js +18 -13
  359. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts +13 -0
  360. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts.map +1 -0
  361. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.js +23 -0
  362. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts +1 -0
  363. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts.map +1 -1
  364. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.js +6 -1
  365. package/src/shade/renderer/wind/wind_field_add_resources.d.ts +11 -0
  366. package/src/shade/renderer/wind/wind_field_add_resources.d.ts.map +1 -1
  367. package/src/shade/renderer/wind/wind_field_add_resources.js +15 -0
  368. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +1 -1
  369. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  370. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
@@ -4,13 +4,17 @@
4
4
  export const PROBE_DUE_SETTINGS: WebGPUStruct;
5
5
  /**
6
6
  * Due (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.5): a thread an entry of the frame's active list,
7
- * dispatched twice through `PROBE_DISPATCH_ACTIVE` after schedule has set the period T. A probe is due
8
- * when a want waits on it:
9
- * - it is NEW, in the first pass, which lists them first: it has had no update;
10
- * - in the second pass, it is ALIVE or DEAD, a pixel or a hit wanted it since its last update, its
11
- * wanted time past its traced time, and `(frame + h(slot)) mod T` is 0. A DEAD probe is due every
12
- * period like an ALIVE one, its update casting `PROBE_DEAD_RAYS`, so that a probe inside a solid
13
- * notices when the scene changes (Enshrouded's dead probes trace 16 a frame).
7
+ * dispatched twice through `PROBE_DISPATCH_ACTIVE` after schedule has set the period T. A probe is due:
8
+ * - in the first pass, which lists them first, when it is NEW: it has had no update;
9
+ * - in the second pass, when it is ALIVE or DEAD and `(frame + h(slot)) mod P` is 0, where P is
10
+ * - T when a want waits on it: a pixel or a hit wanted it since its last update, its wanted time past
11
+ * its traced time;
12
+ * - T · `idle_periods` when it is idle, none waiting on it. Every live probe keeps being updated, so one
13
+ * that readers reach without wanting it, the bounce at a hit among them, and one a want comes back to,
14
+ * holds light no older than that. Its update is any other probe's.
15
+ *
16
+ * A DEAD probe is due like an ALIVE one, its update casting `PROBE_DEAD_RAYS`, so that a probe inside a
17
+ * solid notices when the scene changes (Enshrouded's dead probes trace 16 a frame).
14
18
  *
15
19
  * Wanted since its last update, not this frame: a hit wants a probe the frame after its own probe's
16
20
  * update, so a probe that only hits keep is wanted on few of its turns. Measured on Sponza with the
@@ -19,8 +23,8 @@ export const PROBE_DUE_SETTINGS: WebGPUStruct;
19
23
  * waits on nothing: the update comes after admit in its frame, so its traced time is the want's or later.
20
24
  *
21
25
  * The due go on the trace list, which holds the frame's updates, at most U: past it the count still
22
- * rises and nothing is written, and a probe not listed waits its next turn. With T = ⌈P_w / U⌉ over the
23
- * probes waiting, the expected count a frame is under U, so only NEW probes past U, a camera cut, wait.
26
+ * rises and nothing is written, and a probe not listed waits its next turn. Schedule's T keeps the
27
+ * expected count a frame under U, so only NEW probes past U, a camera cut, wait.
24
28
  *
25
29
  * @type {ComputeShader}
26
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_due.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js"],"names":[],"mappings":"AAkBA;;GAEG;AACH,iCAFU,YAAY,CAWC;AAavB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,uCAFU,aAAa,CA4CpB;6BApG0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_due.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js"],"names":[],"mappings":"AAkBA;;GAEG;AACH,iCAFU,YAAY,CAaC;AAavB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,uCAFU,aAAa,CA6CpB;6BA3G0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -28,6 +28,8 @@ export const PROBE_DUE_SETTINGS = WebGPUStruct.from({
28
28
  frame: u32,
29
29
  /** 1 for the pass that lists the NEW probes, 0 for the one that lists the rest */
30
30
  new_only: u32,
31
+ /** periods between an idle probe's turns, `probe_clipmap_idle_periods`, at least 1 */
32
+ idle_periods: u32,
31
33
  }, "ProbeDueSettings");
32
34
 
33
35
  const resources = new ShaderResourceSetDescriptor();
@@ -43,13 +45,17 @@ resources.createGroup()
43
45
 
44
46
  /**
45
47
  * Due (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.5): a thread an entry of the frame's active list,
46
- * dispatched twice through `PROBE_DISPATCH_ACTIVE` after schedule has set the period T. A probe is due
47
- * when a want waits on it:
48
- * - it is NEW, in the first pass, which lists them first: it has had no update;
49
- * - in the second pass, it is ALIVE or DEAD, a pixel or a hit wanted it since its last update, its
50
- * wanted time past its traced time, and `(frame + h(slot)) mod T` is 0. A DEAD probe is due every
51
- * period like an ALIVE one, its update casting `PROBE_DEAD_RAYS`, so that a probe inside a solid
52
- * notices when the scene changes (Enshrouded's dead probes trace 16 a frame).
48
+ * dispatched twice through `PROBE_DISPATCH_ACTIVE` after schedule has set the period T. A probe is due:
49
+ * - in the first pass, which lists them first, when it is NEW: it has had no update;
50
+ * - in the second pass, when it is ALIVE or DEAD and `(frame + h(slot)) mod P` is 0, where P is
51
+ * - T when a want waits on it: a pixel or a hit wanted it since its last update, its wanted time past
52
+ * its traced time;
53
+ * - T · `idle_periods` when it is idle, none waiting on it. Every live probe keeps being updated, so one
54
+ * that readers reach without wanting it, the bounce at a hit among them, and one a want comes back to,
55
+ * holds light no older than that. Its update is any other probe's.
56
+ *
57
+ * A DEAD probe is due like an ALIVE one, its update casting `PROBE_DEAD_RAYS`, so that a probe inside a
58
+ * solid notices when the scene changes (Enshrouded's dead probes trace 16 a frame).
53
59
  *
54
60
  * Wanted since its last update, not this frame: a hit wants a probe the frame after its own probe's
55
61
  * update, so a probe that only hits keep is wanted on few of its turns. Measured on Sponza with the
@@ -58,8 +64,8 @@ resources.createGroup()
58
64
  * waits on nothing: the update comes after admit in its frame, so its traced time is the want's or later.
59
65
  *
60
66
  * The due go on the trace list, which holds the frame's updates, at most U: past it the count still
61
- * rises and nothing is written, and a probe not listed waits its next turn. With T = ⌈P_w / U⌉ over the
62
- * probes waiting, the expected count a frame is under U, so only NEW probes past U, a camera cut, wait.
67
+ * rises and nothing is written, and a probe not listed waits its next turn. Schedule's T keeps the
68
+ * expected count a frame under U, so only NEW probes past U, a camera cut, wait.
63
69
  *
64
70
  * @type {ComputeShader}
65
71
  */
@@ -87,13 +93,14 @@ fn main(
87
93
  // wanted since its last update
88
94
  let waiting = probe_records[slot].wanted_frame > probe_records[slot].traced_frame;
89
95
 
90
- let period = max(probe_counters[PROBE_COUNTER_PERIOD], 1u);
96
+ // an idle probe's turn comes once in as many periods as the settings give
97
+ let period = max(probe_counters[PROBE_COUNTER_PERIOD], 1u) * select(max(settings.idle_periods, 1u), 1u, waiting);
91
98
  let turn = settings.frame + hash_lowbias32(slot);
92
99
 
93
100
  if (settings.new_only != 0u) {
94
101
  due = kind == PROBE_STATE_NEW;
95
102
  } else {
96
- due = waiting && (kind == PROBE_STATE_ALIVE || kind == PROBE_STATE_DEAD) && turn % period == 0u;
103
+ due = (kind == PROBE_STATE_ALIVE || kind == PROBE_STATE_DEAD) && turn % period == 0u;
97
104
  }
98
105
  }
99
106
 
@@ -204,7 +204,7 @@ fn probe_clipmap_local_irradiance(cascade: u32, position: vec3<f32>, normal: vec
204
204
  return estimate;
205
205
  }
206
206
 
207
- let shadow = brick_clipmap_trace(lifted, to_light / span, 0.0, reach, settings.brick_cascade_count);
207
+ let shadow = brick_clipmap_trace(lifted, to_light / span, 0.0, reach, settings.brick_cascade_count, settings.brick_atlas_shift);
208
208
 
209
209
  return select(vec3<f32>(0.0), estimate, shadow.kind == BRICK_TRACE_MISS);
210
210
  }
@@ -6,9 +6,10 @@ export const PROBE_PROJECT_SETTINGS: WebGPUStruct;
6
6
  * Projection, rows (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.8): a thread a row of each listed probe's
7
7
  * radiance map, dispatched through `PROBE_DISPATCH_ROWS`. It sums the row's texels, new and kept, as
8
8
  * RGB radiance SH3, `L·Y(ω)·dω` over each texel's solid angle, so that `sh3_color_get_irradiance_at` gives
9
- * E; the row's rays this update, resolved and back faces; and how far the update's
10
- * samples fell from their means, with that sum's variance. One row of the probe's projection each, which
11
- * the probe's pass sums.
9
+ * E; the row's rays this update, resolved and back faces; and the row's part of L0 as the texels'
10
+ * estimators hold it, each texel by its solid angle: their short means less their means, their deviations
11
+ * squared (by the solid angle squared) and their short means, luminance. One row of the probe's projection
12
+ * each, which the probe's pass sums.
12
13
  *
13
14
  * @type {ComputeShader}
14
15
  */
@@ -25,21 +26,20 @@ export const shader_probe_clipmap_project_rows: ComputeShader;
25
26
  * radiance is nothing, so their share would read a probe inside a solid as alive. Such an update keeps
26
27
  * the probe's kind and its back-face share.
27
28
  *
28
- * **The probe's ray request** (§8.6): the rays its next guided update asks for, in `probe_requests`.
29
- * - After a probe's first update, the most: Enshrouded's new probe that asks for all it can have.
30
- * - When its light has moved, the most again. The update's change score sums its blended texels' falls
31
- * from their means, luminance, each in its own deviation and held to `PROBE_CHANGE_SIGNIFICANCE` by
32
- * accumulation, over the root of their count. A test texel by texel sees no change under a one-ray
33
- * texel's own noise (measured: the estimator's inconsistency barely moves on a doubling); a sum of the
34
- * falls unbounded is one bright sample's.
35
- * - The score is held to the probe's own: moved means past `PROBE_CHANGE_SIGNIFICANCE` of its running
36
- * deviation, at least one, from its running mean, both over the estimator's variance window,
37
- * `PROBE_VARIANCE_BLEND` an update however long since the last. A probe whose means sit off its samples
38
- * (the estimator's own bias on texels lit by few rays) scores steadily off zero.
39
- * - Measured, Sponza on the 4090, the camera and the light still, 3,600 frames settled: of the probes
40
- * past 64 updates, 4.2% asked for the most against a sum of the raw falls held to zero, 3.2% with each
41
- * texel's fall bounded, and 1.1–1.3% held to their own statistics.
42
- * - Otherwise half what it asked for last, down to the fewest: a converged probe sips.
29
+ * **The probe's L0 estimator** (§8.6, §8.8): a multi-scale mean estimator (`chunk_multiscale_mean`) over the
30
+ * probe's whole light, L0, the solid-angle-weighted sum of its radiance texels. Its short and long means are
31
+ * L0 of the texels' short and long means, and its deviation the root of their deviations' sum, each by its
32
+ * solid angle squared, as the rows sum them; its inconsistency and blend reduction are the probe's own, in
33
+ * `probe_requests`. Its catch-up, the published estimator's, decides what the probe's next update takes:
34
+ * - **Blend**: every texel's long mean blends by at least the catch-up (`shader_probe_clipmap_accumulate`).
35
+ * A one-ray texel's own test sees no change under its own noise; L0 pools every texel's evidence, so a
36
+ * change in the probe's whole light, brighter or darker, is found where no texel can find it.
37
+ * - **Rays**: the next guided update asks for half what the last asked for, raised toward the most by the
38
+ * catch-up, and never under the fewest: a probe whose light moved asks for more, a steady one sips.
39
+ * - After a probe's first update the most rays, a blend of nothing and the estimator seeded as a texel's is,
40
+ * an inconsistency of one and no blend reduction: its slot's last owner's state is not its own.
41
+ * - An update whose texels hold no spread, L0's deviation nothing, leaves the estimator as it was and halves
42
+ * the rays.
43
43
  *
44
44
  * @type {ComputeShader}
45
45
  */
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_project.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js"],"names":[],"mappings":"AAmCA;;GAEG;AACH,qCAFU,YAAY,CAmBK;AAW3B;;;;;;;;;GASG;AACH,gDAFU,aAAa,CAsDpB;AAcH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,2CAFU,aAAa,CA8FpB;6BA3P0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_project.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js"],"names":[],"mappings":"AAgCA;;GAEG;AACH,qCAFU,YAAY,CAmBK;AAW3B;;;;;;;;;;GAUG;AACH,gDAFU,aAAa,CAyDpB;AAcH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,2CAFU,aAAa,CA0FpB;6BArP0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -1,3 +1,5 @@
1
+ import { chunk_rgb_to_luminance } from "../../../shader/chunk/color/chunk_rgb_to_luminance.js";
2
+ import { chunk_multiscale_mean } from "../../../shader/chunk/filter/chunk_multiscale_mean.js";
1
3
  import {
2
4
  chunk_octahedral_solid_angle
3
5
  } from "../../../shader/chunk/geometry/octahedron/chunk_octahedral_solid_angle.js";
@@ -26,12 +28,7 @@ import {
26
28
  PROBE_STATES_STORE_RESOURCE,
27
29
  PROBE_TRACE_READ_RESOURCE
28
30
  } from "./chunk_probe_clipmap.js";
29
- import {
30
- PROBE_CHANGE_SIGNIFICANCE,
31
- PROBE_RADIANCE_SAMPLES_LIMIT,
32
- PROBE_THREAD_WORKGROUP_SIZE,
33
- PROBE_VARIANCE_BLEND
34
- } from "./PROBE_CLIPMAP.js";
31
+ import { PROBE_SHORT_BLEND, PROBE_THREAD_WORKGROUP_SIZE } from "./PROBE_CLIPMAP.js";
35
32
 
36
33
  /**
37
34
  * @type {WebGPUStruct}
@@ -68,9 +65,10 @@ row_resources.createGroup()
68
65
  * Projection, rows (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.8): a thread a row of each listed probe's
69
66
  * radiance map, dispatched through `PROBE_DISPATCH_ROWS`. It sums the row's texels, new and kept, as
70
67
  * RGB radiance SH3, `L·Y(ω)·dω` over each texel's solid angle, so that `sh3_color_get_irradiance_at` gives
71
- * E; the row's rays this update, resolved and back faces; and how far the update's
72
- * samples fell from their means, with that sum's variance. One row of the probe's projection each, which
73
- * the probe's pass sums.
68
+ * E; the row's rays this update, resolved and back faces; and the row's part of L0 as the texels'
69
+ * estimators hold it, each texel by its solid angle: their short means less their means, their deviations
70
+ * squared (by the solid angle squared) and their short means, luminance. One row of the probe's projection
71
+ * each, which the probe's pass sums.
74
72
  *
75
73
  * @type {ComputeShader}
76
74
  */
@@ -106,13 +104,15 @@ fn main(
106
104
  let uv = (vec2<f32>(f32(x), f32(row)) + 0.5) * inverse;
107
105
  let solid_angle = octahedral_solid_angle(uv, inverse);
108
106
  let rays = probe_maps[maps + 2u * texels + i];
109
- let change = unpack2x16float(probe_maps[maps + 3u * texels + settings.depth_resolution * settings.depth_resolution + i]);
107
+ // the texel's estimator: its short mean less its mean, and its deviation, luminance
108
+ let state = unpack2x16float(probe_maps[maps + 3u * texels + settings.depth_resolution * settings.depth_resolution + i]);
110
109
 
111
110
  sh3_color_accumulate(&row_sum.sh, light * solid_angle, sh3_basis_at(uv_octahedral_unit_decode(uv)));
112
111
  row_sum.resolved += rays & 0xFFFFu;
113
112
  row_sum.back_faces += rays >> 16u;
114
- row_sum.change += change.x;
115
- row_sum.blended += change.y;
113
+ row_sum.gap += state.x * solid_angle;
114
+ row_sum.variance += state.y * state.y * solid_angle * solid_angle;
115
+ row_sum.level += (rgb_to_luminance(light) + state.x) * solid_angle;
116
116
  }
117
117
 
118
118
  probe_projection[thread] = row_sum;
@@ -124,6 +124,7 @@ fn main(
124
124
  chunk_uv_octahedral_unit_decode,
125
125
  chunk_sh3_basis_at,
126
126
  chunk_sh3_color_accumulate,
127
+ chunk_rgb_to_luminance,
127
128
  PROBE_PROJECTION_ROW_STRUCT.declaration_chunk,
128
129
  ]),
129
130
  });
@@ -152,21 +153,20 @@ probe_resources.createGroup()
152
153
  * radiance is nothing, so their share would read a probe inside a solid as alive. Such an update keeps
153
154
  * the probe's kind and its back-face share.
154
155
  *
155
- * **The probe's ray request** (§8.6): the rays its next guided update asks for, in `probe_requests`.
156
- * - After a probe's first update, the most: Enshrouded's new probe that asks for all it can have.
157
- * - When its light has moved, the most again. The update's change score sums its blended texels' falls
158
- * from their means, luminance, each in its own deviation and held to `PROBE_CHANGE_SIGNIFICANCE` by
159
- * accumulation, over the root of their count. A test texel by texel sees no change under a one-ray
160
- * texel's own noise (measured: the estimator's inconsistency barely moves on a doubling); a sum of the
161
- * falls unbounded is one bright sample's.
162
- * - The score is held to the probe's own: moved means past `PROBE_CHANGE_SIGNIFICANCE` of its running
163
- * deviation, at least one, from its running mean, both over the estimator's variance window,
164
- * `PROBE_VARIANCE_BLEND` an update however long since the last. A probe whose means sit off its samples
165
- * (the estimator's own bias on texels lit by few rays) scores steadily off zero.
166
- * - Measured, Sponza on the 4090, the camera and the light still, 3,600 frames settled: of the probes
167
- * past 64 updates, 4.2% asked for the most against a sum of the raw falls held to zero, 3.2% with each
168
- * texel's fall bounded, and 1.1–1.3% held to their own statistics.
169
- * - Otherwise half what it asked for last, down to the fewest: a converged probe sips.
156
+ * **The probe's L0 estimator** (§8.6, §8.8): a multi-scale mean estimator (`chunk_multiscale_mean`) over the
157
+ * probe's whole light, L0, the solid-angle-weighted sum of its radiance texels. Its short and long means are
158
+ * L0 of the texels' short and long means, and its deviation the root of their deviations' sum, each by its
159
+ * solid angle squared, as the rows sum them; its inconsistency and blend reduction are the probe's own, in
160
+ * `probe_requests`. Its catch-up, the published estimator's, decides what the probe's next update takes:
161
+ * - **Blend**: every texel's long mean blends by at least the catch-up (`shader_probe_clipmap_accumulate`).
162
+ * A one-ray texel's own test sees no change under its own noise; L0 pools every texel's evidence, so a
163
+ * change in the probe's whole light, brighter or darker, is found where no texel can find it.
164
+ * - **Rays**: the next guided update asks for half what the last asked for, raised toward the most by the
165
+ * catch-up, and never under the fewest: a probe whose light moved asks for more, a steady one sips.
166
+ * - After a probe's first update the most rays, a blend of nothing and the estimator seeded as a texel's is,
167
+ * an inconsistency of one and no blend reduction: its slot's last owner's state is not its own.
168
+ * - An update whose texels hold no spread, L0's deviation nothing, leaves the estimator as it was and halves
169
+ * the rays.
170
170
  *
171
171
  * @type {ComputeShader}
172
172
  */
@@ -201,8 +201,9 @@ fn main(
201
201
 
202
202
  sum.resolved += part.resolved;
203
203
  sum.back_faces += part.back_faces;
204
- sum.change += part.change;
205
- sum.blended += part.blended;
204
+ sum.gap += part.gap;
205
+ sum.variance += part.variance;
206
+ sum.level += part.level;
206
207
  }
207
208
 
208
209
  let state = probe_states[slot].x;
@@ -227,28 +228,22 @@ fn main(
227
228
  probe_records[slot].sh = probe_record_sh_encode(sum.sh);
228
229
  probe_records[slot].traced_frame = settings.frame;
229
230
 
230
- // the next guided update's rays: the most after the first update and whenever the light moved, else half
231
+ // the L0 estimator: the next update's blend and rays
231
232
  var request = probe_requests[slot];
232
233
 
233
234
  if (updates == 0u) {
234
- // its slot's last owner's statistics are not its own
235
- request = ${PROBE_REQUEST_STRUCT.make({ rays: "settings.rays_max", change_mean: "0.0", change_variance: "0.0", changes: "0u" })};
236
- } else if (sum.blended > 0.0) {
237
- let score = sum.change / sqrt(sum.blended);
238
- let reach = ${PROBE_CHANGE_SIGNIFICANCE} * max(sqrt(request.change_variance), 1.0);
239
- let moved = abs(score - request.change_mean) > reach;
240
-
241
- // the score's running statistics, a running mean while young, then over the estimator's variance
242
- // window; a score past their reach counts at it, so a change in the light does not become the probe's
243
- // own spread, while a lasting shift of its own still does within a few windows
244
- let held = clamp(score, request.change_mean - reach, request.change_mean + reach);
245
- let blend = max(1.0 / f32(request.changes + 1u), ${PROBE_VARIANCE_BLEND});
246
- let mean = mix(request.change_mean, held, blend);
247
-
248
- request.change_variance = mix(request.change_variance, (held - request.change_mean) * (held - mean), blend);
249
- request.change_mean = mean;
250
- request.changes = min(request.changes + 1u, ${PROBE_RADIANCE_SAMPLES_LIMIT}u);
251
- request.rays = select(max(settings.rays_min, request.rays >> 1u), settings.rays_max, moved);
235
+ // seeded: its slot's last owner's state is not its own
236
+ request = ${PROBE_REQUEST_STRUCT.make({ rays: "settings.rays_max", blend: "0.0", inconsistency: "1.0", reduction: "0.0" })};
237
+ } else if (sum.variance > 0.0) {
238
+ let deviation = sqrt(sum.variance);
239
+ let relative = abs(sum.gap) / deviation;
240
+ let inconsistency = mix(request.inconsistency, relative, ${PROBE_SHORT_BLEND});
241
+ let blend = saturate(multiscale_mean_catch_up(relative, inconsistency, request.reduction));
242
+
243
+ request.reduction = mix(request.reduction, multiscale_mean_blend_reduction(sum.level / deviation), MULTISCALE_MEAN_REDUCTION_BLEND);
244
+ request.inconsistency = inconsistency;
245
+ request.blend = blend;
246
+ request.rays = max(settings.rays_min, u32(round(mix(f32(request.rays >> 1u), f32(settings.rays_max), blend))));
252
247
  } else {
253
248
  request.rays = max(settings.rays_min, request.rays >> 1u);
254
249
  }
@@ -259,6 +254,7 @@ fn main(
259
254
  chunk_probe_clipmap,
260
255
  chunk_probe_clipmap_guide,
261
256
  chunk_folded_workgroup_index,
257
+ chunk_multiscale_mean,
262
258
  PROBE_PROJECTION_ROW_STRUCT.declaration_chunk,
263
259
  PROBE_REQUEST_STRUCT.declaration_chunk,
264
260
  ]),
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_relocate.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js"],"names":[],"mappings":"AAyBA;;GAEG;AACH,sCAFU,YAAY,CAOM;AAkB5B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,4CAFU,aAAa,CAyDpB;6BAzH0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_relocate.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js"],"names":[],"mappings":"AAyBA;;GAEG;AACH,sCAFU,YAAY,CASM;AAkB5B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,4CAFU,aAAa,CAyDpB;6BA3H0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -31,6 +31,8 @@ export const PROBE_RELOCATE_SETTINGS = WebGPUStruct.from({
31
31
  trace_capacity: u32,
32
32
  /** the brick clipmap's cascades, what its field is read through */
33
33
  brick_cascade_count: u32,
34
+ /** the brick atlas's footprint as shifts, `GPUBrickClipmap#atlas_shift` */
35
+ brick_atlas_shift: u32,
34
36
  }, "ProbeRelocateSettings");
35
37
 
36
38
  const resources = new ShaderResourceSetDescriptor();
@@ -97,13 +99,13 @@ fn main(
97
99
  var position = lattice;
98
100
 
99
101
  for (var step = 0u; step < ${PROBE_RELOCATION_STEPS}u; step++) {
100
- let distance = brick_clipmap_distance(position, settings.brick_cascade_count);
102
+ let distance = brick_clipmap_distance(position, settings.brick_cascade_count, settings.brick_atlas_shift);
101
103
 
102
104
  if (distance >= clearance) {
103
105
  break;
104
106
  }
105
107
 
106
- let gradient = brick_clipmap_gradient(position, settings.brick_cascade_count);
108
+ let gradient = brick_clipmap_gradient(position, settings.brick_cascade_count, settings.brick_atlas_shift);
107
109
 
108
110
  if (all(gradient == vec3<f32>(0.0))) {
109
111
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_retire.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js"],"names":[],"mappings":"AAyBA;;GAEG;AACH,oCAFU,YAAY,CASI;AAiB1B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,0CAFU,aAAa,CAsFpB;6BAxJ0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_retire.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js"],"names":[],"mappings":"AAyBA;;GAEG;AACH,oCAFU,YAAY,CASI;AAiB1B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,0CAFU,aAAa,CAqFpB;6BAvJ0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -120,10 +120,9 @@ fn main(
120
120
 
121
121
  // the oldest buckets whole, and as many of the one below them as are asked for; one that left its
122
122
  // window goes anyway and takes none of those
123
- let whole = PROBE_AGE_BUCKETS - min(probe_eviction.buckets, PROBE_AGE_BUCKETS);
124
- var evicted = evictable && counted >= whole;
123
+ var evicted = evictable && age_eviction_whole(counted, probe_eviction.buckets);
125
124
 
126
- if (held && evictable && counted + 1u == whole && probe_eviction.remaining > 0u) {
125
+ if (held && evictable && age_eviction_partial(counted, probe_eviction.buckets) && probe_eviction.remaining > 0u) {
127
126
  evicted = atomicAdd(&probe_counters[PROBE_COUNTER_EVICTED], 1u) < probe_eviction.remaining;
128
127
  }
129
128
 
@@ -6,9 +6,11 @@ export const PROBE_SCHEDULE_SETTINGS: WebGPUStruct;
6
6
  * Schedule (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.3, §8.5): one thread, after admit, when the frame's
7
7
  * active list is whole.
8
8
  * - The period: with P_w the probes waiting for an update, the NEW and those wanted since their last
9
- * update, `T = max(1, ⌈P_w / U⌉)` frames, so that every probe a want waits on is updated within a period
10
- * and the period follows the live count. Retire and admit counted them in
11
- * `probe_counters[PROBE_COUNTER_WAITING]`, which it empties for the next frame's; the period goes to
9
+ * update, P_i the idle, every other live probe, and m `idle_periods`,
10
+ * `T = max(1, ⌈(P_w + P_i / m) / U⌉)` frames. Every probe a want waits on is updated within a period and
11
+ * every idle one within m of them, a frame's updates U at most on average, and the period follows the
12
+ * live count. Retire and admit counted the waiting in `probe_counters[PROBE_COUNTER_WAITING]`, which it
13
+ * empties for the next frame's; the idle are the rest of the frame's active list. The period goes to
12
14
  * `probe_counters[PROBE_COUNTER_PERIOD]` for the due passes.
13
15
  * - The next frame's eviction, `probe_eviction`: as many slots as the free ones fall short of the frame's
14
16
  * wanted points and the slack, the probes wanted longest ago first. Walking retire's age buckets from the
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_schedule.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js"],"names":[],"mappings":"AAgBA;;GAEG;AACH,sCAFU,YAAY,CAWM;AAa5B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,4CAFU,aAAa,CA8DpB;6BAzH0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_schedule.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js"],"names":[],"mappings":"AAgBA;;GAEG;AACH,sCAFU,YAAY,CAaM;AAa5B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,4CAFU,aAAa,CA2CpB;6BA1G0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -26,6 +26,8 @@ export const PROBE_SCHEDULE_SETTINGS = WebGPUStruct.from({
26
26
  trace_capacity: u32,
27
27
  /** slots kept free beyond the frame's wanted points, ⌈capacity · PROBE_FREE_SLACK⌉ */
28
28
  slack: u32,
29
+ /** idle probes the period counts as one waiting, `probe_clipmap_idle_periods`, at least 1 */
30
+ idle_periods: u32,
29
31
  }, "ProbeScheduleSettings");
30
32
 
31
33
  const resources = new ShaderResourceSetDescriptor();
@@ -43,9 +45,11 @@ resources.createGroup()
43
45
  * Schedule (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.3, §8.5): one thread, after admit, when the frame's
44
46
  * active list is whole.
45
47
  * - The period: with P_w the probes waiting for an update, the NEW and those wanted since their last
46
- * update, `T = max(1, ⌈P_w / U⌉)` frames, so that every probe a want waits on is updated within a period
47
- * and the period follows the live count. Retire and admit counted them in
48
- * `probe_counters[PROBE_COUNTER_WAITING]`, which it empties for the next frame's; the period goes to
48
+ * update, P_i the idle, every other live probe, and m `idle_periods`,
49
+ * `T = max(1, ⌈(P_w + P_i / m) / U⌉)` frames. Every probe a want waits on is updated within a period and
50
+ * every idle one within m of them, a frame's updates U at most on average, and the period follows the
51
+ * live count. Retire and admit counted the waiting in `probe_counters[PROBE_COUNTER_WAITING]`, which it
52
+ * empties for the next frame's; the idle are the rest of the frame's active list. The period goes to
49
53
  * `probe_counters[PROBE_COUNTER_PERIOD]` for the due passes.
50
54
  * - The next frame's eviction, `probe_eviction`: as many slots as the free ones fall short of the frame's
51
55
  * wanted points and the slack, the probes wanted longest ago first. Walking retire's age buckets from the
@@ -73,47 +77,28 @@ export const shader_probe_clipmap_schedule = ComputeShader.from({
73
77
  `
74
78
  @compute @workgroup_size(1)
75
79
  fn main() {
80
+ let live = min(probe_active_write.count, settings.capacity);
76
81
  let waiting = atomicLoad(&probe_counters[PROBE_COUNTER_WAITING]);
82
+ let idle = live - min(waiting, live);
83
+ let idle_periods = max(settings.idle_periods, 1u);
77
84
  let traced = max(settings.trace_capacity, 1u);
78
85
 
79
86
  atomicStore(&probe_counters[PROBE_COUNTER_WAITING], 0u);
80
- atomicStore(&probe_counters[PROBE_COUNTER_PERIOD], max(divide_round_up_u32(waiting, traced), 1u));
87
+ // ⌈(P_w + P_i / m) / U⌉ in whole numbers
88
+ atomicStore(&probe_counters[PROBE_COUNTER_PERIOD], max(divide_round_up_u32(waiting * idle_periods + idle, traced * idle_periods), 1u));
81
89
 
82
90
  // the slots short of the frame's wanted points and the slack
83
91
  let wanted = min(probe_wanted.count, settings.wanted_capacity);
84
92
  let room = wanted + settings.slack;
85
93
  let free = atomicLoad(&probe_counters[PROBE_COUNTER_FREE]);
86
94
 
87
- var lacking = select(0u, room - free, room > free);
88
- var buckets = 0u;
89
- var remaining = 0u;
90
-
91
- for (var k = 0u; k < PROBE_AGE_BUCKETS; k++) {
92
- let at = PROBE_COUNTER_AGES + PROBE_AGE_BUCKETS - 1u - k;
93
- let aged = atomicLoad(&probe_counters[at]);
94
-
95
- atomicStore(&probe_counters[at], 0u);
95
+ let plan = age_eviction_plan(&probe_counters, PROBE_COUNTER_AGES, select(0u, room - free, room > free));
96
96
 
97
- // a bucket nothing was counted in is taken only for an older one: what ages into a bucket below every
98
- // probe counted is not asked for
99
- if (lacking > 0u && aged > 0u) {
100
- if (aged <= lacking) {
101
- lacking -= aged;
102
- buckets = k + 1u;
103
- } else {
104
- buckets = k;
105
- remaining = lacking;
106
- lacking = 0u;
107
- }
108
- }
109
- }
110
-
111
- probe_eviction.buckets = buckets;
112
- probe_eviction.remaining = remaining;
97
+ probe_eviction.buckets = plan.x;
98
+ probe_eviction.remaining = plan.y;
113
99
 
114
100
  atomicStore(&probe_counters[PROBE_COUNTER_EVICTED], 0u);
115
101
 
116
- let live = min(probe_active_write.count, settings.capacity);
117
102
  let folded = folded_dispatch(divide_round_up_u32(live, PROBE_THREAD_WORKGROUP_SIZE));
118
103
 
119
104
  probe_dispatch[PROBE_DISPATCH_ACTIVE] = folded.x;
@@ -110,7 +110,7 @@ fn main(
110
110
  let cascade = (record.flags >> PROBE_RAY_CASCADE_SHIFT) & PROBE_RAY_CASCADE_MASK;
111
111
 
112
112
  let draw = hash_to_float01(pcg4d(vec4<u32>(probe_trace.elements[k], record.direction, 0x510E527Fu, 0u)).x);
113
- let surface = probe_clipmap_hit_material_pick(position, normal, cascade, draw);
113
+ let surface = probe_clipmap_hit_material_pick(position, normal, cascade, draw, settings.brick_atlas_shift);
114
114
  let direct = rgbe9995_decode(record.light);
115
115
 
116
116
  radiance = surface.emissive + surface.albedo * direct * RECIPROCAL_PI;
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_trace.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js"],"names":[],"mappings":"AAkDA;;GAEG;AACH,mCAFU,YAAY,CAeG;AAsBzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,yCAFU,aAAa,CAuJpB;6BAvP0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_trace.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js"],"names":[],"mappings":"AAkDA;;GAEG;AACH,mCAFU,YAAY,CAiBG;AAsBzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,yCAFU,aAAa,CAuJpB;6BAzP0B,sCAAsC;8BAHrC,kCAAkC"}
@@ -64,6 +64,8 @@ export const PROBE_TRACE_SETTINGS = WebGPUStruct.from({
64
64
  t_max: f32,
65
65
  /** updates between a probe's uniform ones, `PROBE_UNIFORM_PERIOD` */
66
66
  uniform_period: u32,
67
+ /** the brick atlas's footprint as shifts, `GPUBrickClipmap#atlas_shift` */
68
+ brick_atlas_shift: u32,
67
69
  }, "ProbeTraceSettings");
68
70
 
69
71
  const resources = new ShaderResourceSetDescriptor();
@@ -198,7 +200,7 @@ fn main(
198
200
  let start = brick_clipmap_start(origin, cascade_count);
199
201
  let t_min = brick_levels[min(start, cascade_count - 1u)].cell_size / ${MESH_SDF_BRICK_VOXELS}.0;
200
202
 
201
- let hit = brick_clipmap_trace(origin, direction, t_min, settings.t_max, cascade_count);
203
+ let hit = brick_clipmap_trace(origin, direction, t_min, settings.t_max, cascade_count, settings.brick_atlas_shift);
202
204
 
203
205
  let is_hit = hit.kind == BRICK_TRACE_HIT;
204
206
  let back_face = is_hit && dot(hit.normal, direction) > 0.0;
@@ -229,7 +231,7 @@ fn main(
229
231
  // a point of the disk of its own for each light, apart from the origin's draw
230
232
  let hash = pcg3d(vec3<u32>(slot, packed, 0x3C6EF372u + shadowed));
231
233
  let toward = cone_sample_direction(-light.direction, light.disk_radius, vec2<f32>(hash_to_float01(hash.x), hash_to_float01(hash.y)));
232
- let shadow = brick_clipmap_trace(lifted, toward, 0.0, settings.t_max, cascade_count);
234
+ let shadow = brick_clipmap_trace(lifted, toward, 0.0, settings.t_max, cascade_count, settings.brick_atlas_shift);
233
235
 
234
236
  visible = select(0.0, 1.0, shadow.kind == BRICK_TRACE_MISS);
235
237
  }
@@ -2,6 +2,11 @@
2
2
  * @type {WebGPUStruct}
3
3
  */
4
4
  export const PROBE_WANT_SETTINGS: WebGPUStruct;
5
+ /**
6
+ * {@link PROBE_WANT_SETTINGS} and how the surface's normals are read.
7
+ * @type {WebGPUStruct}
8
+ */
9
+ export const PROBE_WANT_SURFACE_SETTINGS: WebGPUStruct;
5
10
  /**
6
11
  * Want (PROBE_CLIPMAP_PROPOSAL_2026_10_27.md §8.3): a thread a tile of {@link PROBE_WANT_TILE}² pixels,
7
12
  * the launch the screen's tiles.
@@ -10,6 +15,12 @@ export const PROBE_WANT_SETTINGS: WebGPUStruct;
10
15
  * that pixel's pick reads (§9.1), biased along `mix(n, v, 0.2)`, through `probe_clipmap_want`. A sky
11
16
  * pixel wants nothing.
12
17
  *
18
+ * The surface is any the frame holds a depth and a normal of, a pixel each: the G-buffer's, or one drawn
19
+ * apart from it whose reader picks its probes per pixel, the water's interface. `gBufferDepth` is its
20
+ * reverse-Z depth, zero where it has no surface; `gBufferNormals` its normals, octahedral in the channel
21
+ * `settings.normal_channel`, as the G-buffer packs its own in its first. Where `settings.two_sided` is set,
22
+ * a normal that faces away from the eye is turned to face it, as a reader of such a surface turns it.
23
+ *
13
24
  * At a cascade's outer edge a lattice cell is 58 pixels on a 1080p screen at a 60° field of view, and
14
25
  * 19 at 120°, so a tile of 8 samples every cell several times a frame, and a workgroup's 64 tiles want
15
26
  * a few cages between them, which its claims keep to one want each.
@@ -1 +1 @@
1
- {"version":3,"file":"shader_probe_clipmap_want.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js"],"names":[],"mappings":"AAqBA;;GAEG;AACH,kCAFU,YAAY,CAcE;AAexB;;;;;;;;;;;;;GAaG;AACH,wCAFU,aAAa,CAqCpB;6BA1F0B,sCAAsC;8BAHrC,kCAAkC"}
1
+ {"version":3,"file":"shader_probe_clipmap_want.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js"],"names":[],"mappings":"AAqBA;;GAEG;AACH,kCAFU,YAAY,CAcE;AAExB;;;GAGG;AACH,0CAFU,YAAY,CAaS;AAe/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wCAFU,aAAa,CAyCpB;6BArH0B,sCAAsC;8BAHrC,kCAAkC"}