@woosh/meep-engine 3.39.0 → 3.40.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 (367) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/engine/EngineHarness.d.ts.map +1 -1
  4. package/src/engine/EngineHarness.js +17 -4
  5. package/src/engine/asset/loaders/image/avif/threaded_image_encoder.js +1 -1
  6. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  7. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
  8. package/src/engine/graphics/ecs/water/Water.d.ts +14 -17
  9. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  10. package/src/engine/graphics/ecs/water/Water.js +100 -110
  11. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
  12. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +36 -38
  13. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_3_4.d.ts +6 -0
  14. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_3_4.d.ts.map +1 -0
  15. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_3_4.js +10 -0
  16. package/src/engine/graphics/ecs/water/WaterWaves.d.ts +3 -3
  17. package/src/engine/graphics/ecs/water/WaterWaves.js +46 -46
  18. package/src/engine/graphics/texture/sampler/sampler2d_to_avif.d.ts.map +1 -1
  19. package/src/engine/graphics/texture/sampler/sampler2d_to_avif.js +8 -5
  20. package/src/engine/graphics3/WaterSystem.d.ts +21 -74
  21. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  22. package/src/engine/graphics3/WaterSystem.js +174 -367
  23. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  24. package/src/format/image/avif/api/convert_to_rgba.d.ts +2 -2
  25. package/src/format/image/avif/api/convert_to_rgba.d.ts.map +1 -1
  26. package/src/format/image/avif/api/convert_to_rgba.js +10 -9
  27. package/src/format/image/avif/api/decode_avif.d.ts +2 -2
  28. package/src/format/image/avif/api/decode_avif.d.ts.map +1 -1
  29. package/src/format/image/avif/api/decode_avif.js +1 -1
  30. package/src/shade/playground/ssr_reprojection/README.md +111 -103
  31. package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.js +68 -68
  32. package/src/shade/playground/ssr_reprojection/index.html +80 -79
  33. package/src/shade/playground/ssr_reprojection/main.js +256 -247
  34. package/src/shade/playground/ssr_variance/capture.js +96 -96
  35. package/src/shade/playground/ssr_variance/reference.js +78 -78
  36. package/src/shade/playground/vsm/README.md +96 -0
  37. package/src/shade/playground/vsm/VSMDebugOverlay.d.ts +49 -0
  38. package/src/shade/playground/vsm/VSMDebugOverlay.d.ts.map +1 -0
  39. package/src/shade/playground/vsm/VSMDebugOverlay.js +351 -0
  40. package/src/shade/playground/vsm/clipmap_panel.d.ts +8 -0
  41. package/src/shade/playground/vsm/clipmap_panel.d.ts.map +1 -0
  42. package/src/shade/playground/vsm/clipmap_panel.js +138 -0
  43. package/src/shade/playground/vsm/index.html +170 -0
  44. package/src/shade/playground/vsm/main.d.ts +2 -0
  45. package/src/shade/playground/vsm/main.d.ts.map +1 -0
  46. package/src/shade/playground/vsm/main.js +389 -0
  47. package/src/shade/playground/vsm/scenes.d.ts +52 -0
  48. package/src/shade/playground/vsm/scenes.d.ts.map +1 -0
  49. package/src/shade/playground/vsm/scenes.js +320 -0
  50. package/src/shade/playground/vsm/verify_vsm.d.ts +38 -0
  51. package/src/shade/playground/vsm/verify_vsm.d.ts.map +1 -0
  52. package/src/shade/playground/vsm/verify_vsm.js +305 -0
  53. package/src/shade/playground/water_refraction/README.md +57 -9
  54. package/src/shade/playground/water_refraction/main.js +12 -3
  55. package/src/shade/playground/water_refraction/world.d.ts +1 -0
  56. package/src/shade/playground/water_refraction/world.d.ts.map +1 -1
  57. package/src/shade/playground/water_refraction/world.js +15 -5
  58. package/src/shade/renderer/Renderer.d.ts +19 -0
  59. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  60. package/src/shade/renderer/Renderer.js +72 -1
  61. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +8 -0
  62. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  63. package/src/shade/renderer/animation/GPUAnimationManager.js +18 -0
  64. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts +7 -0
  65. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  66. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +11 -0
  67. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.d.ts +3 -1
  68. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.d.ts.map +1 -1
  69. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +10 -1
  70. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_read_code.d.ts.map +1 -1
  71. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_read_code.js +28 -0
  72. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_write_code.d.ts.map +1 -1
  73. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_write_code.js +31 -1
  74. package/src/shade/renderer/geometry/MeshletGeometry.d.ts +3 -0
  75. package/src/shade/renderer/geometry/MeshletGeometry.d.ts.map +1 -1
  76. package/src/shade/renderer/geometry/MeshletGeometry.js +1 -0
  77. package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.d.ts.map +1 -1
  78. package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.js +62 -4
  79. package/src/shade/renderer/light/environment/rgbe/parse_rgbe.d.ts +1 -1
  80. package/src/shade/renderer/light/environment/rgbe/parse_rgbe.js +2 -2
  81. package/src/shade/renderer/light/environment/rgbe/rgbe_byte_to_rgb_half.d.ts +2 -2
  82. package/src/shade/renderer/light/environment/rgbe/rgbe_byte_to_rgb_half.d.ts.map +1 -1
  83. package/src/shade/renderer/light/environment/rgbe/rgbe_byte_to_rgb_half.js +6 -14
  84. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts.map +1 -1
  85. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.js +5 -1
  86. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  87. package/src/shade/renderer/medium/ParticipatingMedium.d.ts +3 -0
  88. package/src/shade/renderer/medium/ParticipatingMedium.d.ts.map +1 -1
  89. package/src/shade/renderer/medium/ParticipatingMedium.js +7 -0
  90. package/src/shade/renderer/postprocess/eye/shader_autoexposure_build_histogram.d.ts.map +1 -1
  91. package/src/shade/renderer/postprocess/eye/shader_autoexposure_build_histogram.js +5 -35
  92. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  93. package/src/shade/renderer/postprocess/ssr/SSR.d.ts +12 -4
  94. package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
  95. package/src/shade/renderer/postprocess/ssr/SSR.js +36 -8
  96. package/src/shade/renderer/postprocess/ssr/SSR_DENOISE_TILE_SIZE.d.ts +3 -0
  97. package/src/shade/renderer/postprocess/ssr/SSR_DENOISE_TILE_SIZE.d.ts.map +1 -0
  98. package/src/shade/renderer/postprocess/ssr/SSR_DENOISE_TILE_SIZE.js +2 -0
  99. package/src/shade/renderer/postprocess/ssr/chunk_ssr_denoise_tile_active.d.ts +8 -0
  100. package/src/shade/renderer/postprocess/ssr/chunk_ssr_denoise_tile_active.d.ts.map +1 -0
  101. package/src/shade/renderer/postprocess/ssr/chunk_ssr_denoise_tile_active.js +40 -0
  102. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -1
  103. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +150 -137
  104. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_classify.d.ts +3 -0
  105. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_classify.d.ts.map +1 -0
  106. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_classify.js +44 -0
  107. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_dilate.d.ts +5 -0
  108. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_dilate.d.ts.map +1 -0
  109. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_dilate.js +35 -0
  110. package/src/shade/renderer/postprocess/ssr/ssr_graph_denoise_tiles.d.ts +26 -0
  111. package/src/shade/renderer/postprocess/ssr/ssr_graph_denoise_tiles.d.ts.map +1 -0
  112. package/src/shade/renderer/postprocess/ssr/ssr_graph_denoise_tiles.js +42 -0
  113. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +1 -0
  114. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
  115. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +113 -105
  116. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts +2 -0
  117. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
  118. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +177 -167
  119. package/src/shade/renderer/rasterize/DEPTH_STENCIL_STATE_SHADOW.d.ts.map +1 -1
  120. package/src/shade/renderer/rasterize/DEPTH_STENCIL_STATE_SHADOW.js +10 -0
  121. package/src/shade/renderer/rasterize/RasterizationJob.d.ts +1 -2
  122. package/src/shade/renderer/rasterize/RasterizationJob.d.ts.map +1 -1
  123. package/src/shade/renderer/rasterize/RasterizationJob.js +1 -2
  124. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +5 -6
  125. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  126. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +5 -6
  127. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +2 -2
  128. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  129. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +1 -1
  130. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
  131. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +11 -0
  132. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/build_lut_fog_parameters_jendersie_deon.d.ts.map +1 -1
  133. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/build_lut_fog_parameters_jendersie_deon.js +4 -5
  134. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.d.ts.map +1 -1
  135. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.js +1 -5
  136. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_importance.d.ts +30 -0
  137. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_importance.d.ts.map +1 -0
  138. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_importance.js +54 -0
  139. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.d.ts.map +1 -1
  140. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.js +3 -2
  141. package/src/shade/renderer/shadow/map/DIRECTIONAL_SHADOWMAP_STRUCT.d.ts +19 -0
  142. package/src/shade/renderer/shadow/map/DIRECTIONAL_SHADOWMAP_STRUCT.d.ts.map +1 -0
  143. package/src/shade/renderer/shadow/map/DIRECTIONAL_SHADOWMAP_STRUCT.js +27 -0
  144. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts +1 -0
  145. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
  146. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +4 -0
  147. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +54 -0
  148. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  149. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +502 -10
  150. package/src/shade/renderer/shadow/map/Shadowmap.d.ts +3 -2
  151. package/src/shade/renderer/shadow/map/Shadowmap.d.ts.map +1 -1
  152. package/src/shade/renderer/shadow/map/Shadowmap.js +3 -2
  153. package/src/shade/renderer/shadow/map/shader/chunk_sample_shadowmap_atlas_for_light.d.ts +8 -0
  154. package/src/shade/renderer/shadow/map/shader/chunk_sample_shadowmap_atlas_for_light.d.ts.map +1 -1
  155. package/src/shade/renderer/shadow/map/shader/chunk_sample_shadowmap_atlas_for_light.js +34 -3
  156. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts +3 -2
  157. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts.map +1 -1
  158. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.js +17 -2
  159. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
  160. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +137 -133
  161. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.d.ts +15 -0
  162. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.d.ts.map +1 -0
  163. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.js +14 -0
  164. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_PAGE_BUDGET.d.ts +16 -0
  165. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_PAGE_BUDGET.d.ts.map +1 -0
  166. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_PAGE_BUDGET.js +15 -0
  167. package/src/shade/renderer/shadow/vsm/PLAN.md +440 -0
  168. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +43 -0
  169. package/src/shade/renderer/shadow/vsm/VSMMoverTracker.d.ts +42 -0
  170. package/src/shade/renderer/shadow/vsm/VSMMoverTracker.d.ts.map +1 -0
  171. package/src/shade/renderer/shadow/vsm/VSMMoverTracker.js +153 -0
  172. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +327 -0
  173. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -0
  174. package/src/shade/renderer/shadow/vsm/VSMPageManager.js +1408 -0
  175. package/src/shade/renderer/shadow/vsm/VSM_COVER_LEVEL_STEP.d.ts +14 -0
  176. package/src/shade/renderer/shadow/vsm/VSM_COVER_LEVEL_STEP.d.ts.map +1 -0
  177. package/src/shade/renderer/shadow/vsm/VSM_COVER_LEVEL_STEP.js +13 -0
  178. package/src/shade/renderer/shadow/vsm/VSM_FEEDBACK_LAYOUT.d.ts +63 -0
  179. package/src/shade/renderer/shadow/vsm/VSM_FEEDBACK_LAYOUT.d.ts.map +1 -0
  180. package/src/shade/renderer/shadow/vsm/VSM_FEEDBACK_LAYOUT.js +70 -0
  181. package/src/shade/renderer/shadow/vsm/VSM_LEVEL_LIMIT.d.ts +11 -0
  182. package/src/shade/renderer/shadow/vsm/VSM_LEVEL_LIMIT.d.ts.map +1 -0
  183. package/src/shade/renderer/shadow/vsm/VSM_LEVEL_LIMIT.js +10 -0
  184. package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.d.ts +54 -0
  185. package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.d.ts.map +1 -0
  186. package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.js +75 -0
  187. package/src/shade/renderer/shadow/vsm/VSM_RECORD_STRUCT.d.ts +23 -0
  188. package/src/shade/renderer/shadow/vsm/VSM_RECORD_STRUCT.d.ts.map +1 -0
  189. package/src/shade/renderer/shadow/vsm/VSM_RECORD_STRUCT.js +85 -0
  190. package/src/shade/renderer/shadow/vsm/VSM_SLOT_STRUCT.d.ts +16 -0
  191. package/src/shade/renderer/shadow/vsm/VSM_SLOT_STRUCT.d.ts.map +1 -0
  192. package/src/shade/renderer/shadow/vsm/VSM_SLOT_STRUCT.js +29 -0
  193. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts +136 -25
  194. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts.map +1 -1
  195. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.js +282 -116
  196. package/src/shade/renderer/shadow/vsm/graph_vsm_feedback.d.ts +39 -0
  197. package/src/shade/renderer/shadow/vsm/graph_vsm_feedback.d.ts.map +1 -0
  198. package/src/shade/renderer/shadow/vsm/graph_vsm_feedback.js +289 -0
  199. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_virtual.d.ts +18 -0
  200. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_virtual.d.ts.map +1 -0
  201. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_virtual.js +65 -0
  202. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_addressing.d.ts +17 -0
  203. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_addressing.d.ts.map +1 -0
  204. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_addressing.js +75 -0
  205. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.d.ts +14 -0
  206. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.d.ts.map +1 -0
  207. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.js +45 -0
  208. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_find_page.d.ts +17 -0
  209. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_find_page.d.ts.map +1 -0
  210. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_find_page.js +100 -0
  211. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_page_view_projection.d.ts +15 -0
  212. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_page_view_projection.d.ts.map +1 -0
  213. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_page_view_projection.js +47 -0
  214. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_demand.d.ts +26 -0
  215. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_demand.d.ts.map +1 -0
  216. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_demand.js +156 -0
  217. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_gather_counts.d.ts +13 -0
  218. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_gather_counts.d.ts.map +1 -0
  219. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_gather_counts.js +65 -0
  220. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.d.ts +30 -0
  221. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.d.ts.map +1 -0
  222. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.js +184 -0
  223. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.d.ts +23 -0
  224. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.d.ts.map +1 -0
  225. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.js +109 -0
  226. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_stale.d.ts +56 -0
  227. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_stale.d.ts.map +1 -0
  228. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_stale.js +211 -0
  229. package/src/shade/renderer/texture/format/decode_typed_gpu_data.d.ts +1 -1
  230. package/src/shade/renderer/texture/format/decode_typed_gpu_data.d.ts.map +1 -1
  231. package/src/shade/renderer/texture/format/decode_typed_gpu_data.js +3 -13
  232. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts.map +1 -1
  233. package/src/shade/renderer/texture/format/infer_format_from_parameters.js +2 -3
  234. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts.map +1 -1
  235. package/src/shade/renderer/texture/texture_write_to_gpu.js +16 -16
  236. package/src/shade/renderer/texture/virtual/VTPhysicalCache.d.ts +4 -90
  237. package/src/shade/renderer/texture/virtual/VTPhysicalCache.d.ts.map +1 -1
  238. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +6 -210
  239. package/src/shade/renderer/texture/virtual/VTPhysicalSlots.d.ts +119 -0
  240. package/src/shade/renderer/texture/virtual/VTPhysicalSlots.d.ts.map +1 -0
  241. package/src/shade/renderer/texture/virtual/VTPhysicalSlots.js +245 -0
  242. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.d.ts +1 -1
  243. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.d.ts.map +1 -1
  244. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +3 -19
  245. package/src/shade/renderer/texture/virtual/chunk_vt_page_table_entry.d.ts +13 -0
  246. package/src/shade/renderer/texture/virtual/chunk_vt_page_table_entry.d.ts.map +1 -0
  247. package/src/shade/renderer/texture/virtual/chunk_vt_page_table_entry.js +34 -0
  248. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +7 -2
  249. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  250. package/src/shade/renderer/water/GPUWaterRenderer.js +49 -49
  251. package/src/shade/renderer/water/chunk_water_transport.js +3 -3
  252. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +11 -25
  253. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -1
  254. package/src/shade/renderer/water/medium/GPUWaterBodies.js +64 -116
  255. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts +1 -20
  256. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts.map +1 -1
  257. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.js +10 -42
  258. package/src/shade/renderer/water/medium/chunk_water_bodies.d.ts +3 -0
  259. package/src/shade/renderer/water/medium/chunk_water_bodies.d.ts.map +1 -0
  260. package/src/shade/renderer/water/medium/chunk_water_bodies.js +3 -0
  261. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +1 -5
  262. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +1 -1
  263. package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +32 -30
  264. package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts +4 -0
  265. package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts.map +1 -0
  266. package/src/shade/renderer/water/medium/shader_water_meshlets.js +69 -0
  267. package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts.map +1 -1
  268. package/src/shade/renderer/water/optics/chunk_water_inscatter.js +124 -123
  269. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts.map +1 -1
  270. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.js +75 -74
  271. package/src/shade/renderer/water/shader_water.d.ts +1 -1
  272. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  273. package/src/shade/renderer/water/shader_water.js +490 -406
  274. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +12 -85
  275. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +1 -1
  276. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +128 -408
  277. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts +6 -0
  278. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts.map +1 -0
  279. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.js +23 -0
  280. package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts +9 -0
  281. package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts.map +1 -0
  282. package/src/shade/renderer/water/surface/chunk_water_tessellation.js +144 -0
  283. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +3 -5
  284. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +1 -1
  285. package/src/shade/renderer/water/surface/chunk_water_wave.js +40 -48
  286. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +14 -0
  287. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -0
  288. package/src/shade/renderer/water/surface/graph_water_tessellation.js +34 -0
  289. package/src/shade/renderer/water/surface/shader_water_tiles.d.ts +4 -0
  290. package/src/shade/renderer/water/surface/shader_water_tiles.d.ts.map +1 -0
  291. package/src/shade/renderer/water/surface/shader_water_tiles.js +123 -0
  292. package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts +6 -0
  293. package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts.map +1 -0
  294. package/src/shade/renderer/water/surface/shader_water_wave_batch.js +85 -0
  295. package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts +6 -0
  296. package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts.map +1 -0
  297. package/src/shade/renderer/water/surface/shader_water_wave_domain.js +83 -0
  298. package/src/shade/renderer/water/water_height_texture_from_sampler.d.ts.map +1 -1
  299. package/src/shade/renderer/water/water_height_texture_from_sampler.js +4 -3
  300. package/src/engine/graphics/ecs/water/WATER_SIZE.d.ts +0 -2
  301. package/src/engine/graphics/ecs/water/WATER_SIZE.d.ts.map +0 -1
  302. package/src/engine/graphics/ecs/water/WATER_SIZE.js +0 -1
  303. package/src/shade/renderer/light/GPUViewLightContext.d.ts +0 -22
  304. package/src/shade/renderer/light/GPUViewLightContext.d.ts.map +0 -1
  305. package/src/shade/renderer/light/GPUViewLightContext.js +0 -62
  306. package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.d.ts +0 -14
  307. package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.d.ts.map +0 -1
  308. package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.js +0 -20
  309. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts +0 -27
  310. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts.map +0 -1
  311. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.js +0 -147
  312. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.d.ts +0 -7
  313. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.d.ts.map +0 -1
  314. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.js +0 -10
  315. package/src/shade/renderer/shadow/vsm/VSM_RESIDENCE_WORKGROUP_TILE_RESOLUTION.d.ts +0 -2
  316. package/src/shade/renderer/shadow/vsm/VSM_RESIDENCE_WORKGROUP_TILE_RESOLUTION.d.ts.map +0 -1
  317. package/src/shade/renderer/shadow/vsm/VSM_RESIDENCE_WORKGROUP_TILE_RESOLUTION.js +0 -1
  318. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.d.ts +0 -3
  319. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.d.ts.map +0 -1
  320. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.js +0 -8
  321. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts +0 -27
  322. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts.map +0 -1
  323. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.js +0 -102
  324. package/src/shade/renderer/shadow/vsm/debug_inspect_coarse_bounds_buffer.d.ts +0 -8
  325. package/src/shade/renderer/shadow/vsm/debug_inspect_coarse_bounds_buffer.d.ts.map +0 -1
  326. package/src/shade/renderer/shadow/vsm/debug_inspect_coarse_bounds_buffer.js +0 -34
  327. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.d.ts +0 -10
  328. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.d.ts.map +0 -1
  329. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.js +0 -64
  330. package/src/shade/renderer/shadow/vsm/decode_tile_address.d.ts +0 -11
  331. package/src/shade/renderer/shadow/vsm/decode_tile_address.d.ts.map +0 -1
  332. package/src/shade/renderer/shadow/vsm/decode_tile_address.js +0 -26
  333. package/src/shade/renderer/shadow/vsm/render/vms_render_tiles.d.ts +0 -2
  334. package/src/shade/renderer/shadow/vsm/render/vms_render_tiles.d.ts.map +0 -1
  335. package/src/shade/renderer/shadow/vsm/render/vms_render_tiles.js +0 -11
  336. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.d.ts +0 -20
  337. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.d.ts.map +0 -1
  338. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.js +0 -184
  339. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.d.ts +0 -25
  340. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.d.ts.map +0 -1
  341. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.js +0 -230
  342. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.d.ts +0 -19
  343. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.d.ts.map +0 -1
  344. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.js +0 -127
  345. package/src/shade/renderer/texture/encode/ENCODE_NOTES_2026_09_02.md +0 -462
  346. package/src/shade/renderer/water/optics/chunk_water_light_transmittance.d.ts +0 -23
  347. package/src/shade/renderer/water/optics/chunk_water_light_transmittance.d.ts.map +0 -1
  348. package/src/shade/renderer/water/optics/chunk_water_light_transmittance_dry.d.ts +0 -18
  349. package/src/shade/renderer/water/optics/chunk_water_light_transmittance_dry.d.ts.map +0 -1
  350. package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.d.ts +0 -12
  351. package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.d.ts.map +0 -1
  352. package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.js +0 -27
  353. package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.d.ts +0 -14
  354. package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.d.ts.map +0 -1
  355. package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.js +0 -49
  356. package/src/shade/renderer/water/surface/chunk_water_wave_grid.d.ts +0 -9
  357. package/src/shade/renderer/water/surface/chunk_water_wave_grid.d.ts.map +0 -1
  358. package/src/shade/renderer/water/surface/chunk_water_wave_grid.js +0 -47
  359. package/src/shade/renderer/water/surface/shader_water_surface_write.d.ts +0 -10
  360. package/src/shade/renderer/water/surface/shader_water_surface_write.d.ts.map +0 -1
  361. package/src/shade/renderer/water/surface/shader_water_surface_write.js +0 -51
  362. package/src/shade/renderer/water/surface/shader_water_wave_step.d.ts +0 -12
  363. package/src/shade/renderer/water/surface/shader_water_wave_step.d.ts.map +0 -1
  364. package/src/shade/renderer/water/surface/shader_water_wave_step.js +0 -54
  365. package/src/shade/renderer/water/surface/water_surface_grid_indices.d.ts +0 -13
  366. package/src/shade/renderer/water/surface/water_surface_grid_indices.d.ts.map +0 -1
  367. package/src/shade/renderer/water/surface/water_surface_grid_indices.js +0 -39
@@ -63,6 +63,13 @@ import { ShadowmapDrawSelector } from "./ShadowmapDrawSelector.js";
63
63
  import { ShadowmapRegistry } from "./ShadowmapRegistry.js";
64
64
  import { ShadowmapResolutionPolicy } from "./ShadowmapResolutionPolicy.js";
65
65
  import { SpotLightShadowmap } from "./SpotLightShadowmap.js";
66
+ import { DEFAULT_VSM_FIRST_TEXEL_SIZE } from "../vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.js";
67
+ import { DEFAULT_VSM_PAGE_BUDGET } from "../vsm/DEFAULT_VSM_PAGE_BUDGET.js";
68
+ import { graph_vsm_feedback } from "../vsm/graph_vsm_feedback.js";
69
+ import { VirtualShadowMap } from "../vsm/VirtualShadowMap.js";
70
+ import { VSMMoverTracker } from "../vsm/VSMMoverTracker.js";
71
+ import { VSM_DEFAULT_REQUEST_CAPACITY } from "../vsm/VSM_FEEDBACK_LAYOUT.js";
72
+ import { VSM_SLOT_EMPTY } from "../vsm/VSM_SLOT_STRUCT.js";
66
73
 
67
74
  /**
68
75
  * @typedef {import("../../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext} ShadeGPUCommandContext
@@ -94,12 +101,49 @@ const DEFAULT_SHADOWMAP_DIRECTIONAL_RESOLUTION = [
94
101
  */
95
102
  const DEFAULT_SHADOWMAP_LOCAL_RESOLUTION = 128;
96
103
 
104
+ /**
105
+ * How every shadow caster is culled: as the main view culls it, so a caster's front faces are what
106
+ * reach the shadow map and acne is the shared rasterizer slope bias's to prevent
107
+ * ({@link DEPTH_STENCIL_STATE_SHADOW}). Casting back faces on top of that bias pushed a caster's
108
+ * steep far face past the ground at its base, a gap of 1–1.5 shadow texels at every contact.
109
+ * @type {Readonly<Record<number, GPUCullMode>>}
110
+ */
111
+ const SHADOW_CULL_POLICY = CullPolicy.Standard;
112
+
97
113
  /**
98
114
  * Writes the CPU's half of a `shadow_cascade` row and leaves `caster_near`, the GPU's, alone.
99
115
  * @type {GPURecordWriter}
100
116
  */
101
117
  const CASCADE_VIEW_WRITER = generate_record_writer(CSM_CASCADE_STRUCT, ['atlas', 'projection']);
102
118
 
119
+ /**
120
+ * A virtual shadow map's clipmap: levels, and pages on a side of each level's window. Twelve levels
121
+ * of 64² pages at the default 1 cm first texel reach from 41 m windows at level 0 to 168 km at the
122
+ * coarsest; see {@link VirtualShadowMap}.
123
+ */
124
+ const VSM_LEVEL_COUNT = 12;
125
+ const VSM_PAGES_PER_LEVEL = 64;
126
+
127
+ /**
128
+ * Texels on a side of a virtual shadow map page — the reference's and UE's 128 — and of the pool
129
+ * the map claims in the atlas for them, 30 × 30 slots of 128 plus {@link SHADOWMAP_ATLAS_BORDER}
130
+ * each side. A device whose atlas is smaller gets a pool of what fits.
131
+ */
132
+ const VSM_PAGE_SIZE = 128;
133
+ const VSM_POOL_SIZE = 30 * (VSM_PAGE_SIZE + 2 * SHADOWMAP_ATLAS_BORDER);
134
+
135
+ /**
136
+ * How far either side of the camera's snapped depth along the light a virtual shadow map's depth
137
+ * range reaches, in metres. Casters further toward the light than this are clipped.
138
+ */
139
+ const VSM_DEPTH_EXTENT = 1000;
140
+
141
+ /**
142
+ * Frames a page something looked at is safe from eviction for: longer than a reading takes to come
143
+ * back with three readbacks in flight.
144
+ */
145
+ const VSM_PROTECT_FRAMES = 6;
146
+
103
147
  /**
104
148
  * Shadows for a single scene
105
149
  */
@@ -189,6 +233,74 @@ export class GPUSceneShadowmapContext {
189
233
  */
190
234
  use_tetrahedron_point_shadows = false;
191
235
 
236
+ /**
237
+ * Gives directional lights virtual shadow maps instead of cascades. The renderer pushes this
238
+ * from its `feature_virtual_shadow_maps` flag before each light update, alongside
239
+ * {@link enabled}; a flip is picked up in {@link process_lights}, which rebuilds every
240
+ * directional map of the other kind on the same pass, as it does for the point-light generator.
241
+ * @type {boolean}
242
+ */
243
+ use_virtual_shadow_maps = false;
244
+
245
+ /**
246
+ * Pages the scene's virtual shadow maps may render per frame. See {@link virtual_page_budget}.
247
+ * @type {number}
248
+ */
249
+ #virtual_page_budget = DEFAULT_VSM_PAGE_BUDGET;
250
+
251
+ /**
252
+ * @returns {number} pages per frame, across the scene's virtual shadow maps
253
+ */
254
+ get virtual_page_budget() {
255
+ return this.#virtual_page_budget;
256
+ }
257
+
258
+ /**
259
+ * The renderer pushes this from {@link Renderer.shadow_virtual_page_budget} every frame. Pages a
260
+ * frame wants beyond it wait, best first, for the next.
261
+ *
262
+ * @param {number} v pages per frame; zero draws none and leaves every page as it is
263
+ */
264
+ set virtual_page_budget(v) {
265
+ assert.isNonNegativeInteger(v, 'virtual_page_budget');
266
+
267
+ this.#virtual_page_budget = v;
268
+ }
269
+
270
+ /**
271
+ * Added to the level every point of a virtual shadow map selects; positive is coarser. Pushed by
272
+ * the renderer every frame and written into each map's record, so a change takes effect on the
273
+ * next frame's demand and sampling alike.
274
+ * @type {number}
275
+ */
276
+ virtual_lod_bias = 0;
277
+
278
+ /**
279
+ * World size of a virtual shadow map's level-0 texel. See {@link virtual_first_texel_size}.
280
+ * @type {number}
281
+ */
282
+ #virtual_first_texel_size = DEFAULT_VSM_FIRST_TEXEL_SIZE;
283
+
284
+ /**
285
+ * @returns {number}
286
+ */
287
+ get virtual_first_texel_size() {
288
+ return this.#virtual_first_texel_size;
289
+ }
290
+
291
+ /**
292
+ * Pushed from {@link Renderer.shadow_virtual_first_texel_size}. A change rebuilds every virtual
293
+ * shadow map in {@link process_lights}: every page of the clipmap changes size.
294
+ *
295
+ * @param {number} v metres, positive
296
+ */
297
+ set virtual_first_texel_size(v) {
298
+ assert.isNumber(v, 'virtual_first_texel_size');
299
+ assert.greaterThan(v, 0, 'virtual_first_texel_size');
300
+
301
+ this.#virtual_first_texel_size = v;
302
+ }
303
+
192
304
  /**
193
305
  * How many cascades a directional shadow map is built with. See {@link cascade_count}.
194
306
  * @type {number}
@@ -312,6 +424,32 @@ export class GPUSceneShadowmapContext {
312
424
  */
313
425
  #cascade_record = { atlas: [0, 0, 0, 0], projection: null };
314
426
 
427
+ /**
428
+ * Scratch for {@link #write_virtual_rows}: one `shadow_virtual_slot` row, and one window corner.
429
+ */
430
+ #virtual_slot_record = { level: 0, x: 0, y: 0 };
431
+ #virtual_level_record = [0, 0];
432
+
433
+ /**
434
+ * Where the scene's casters were last frame, for the virtual shadow maps' mover pass. Made on the
435
+ * first frame a virtual map asks for feedback.
436
+ * @type {VSMMoverTracker|null}
437
+ */
438
+ #vsm_movers = null;
439
+
440
+ /**
441
+ * Which virtual map's turn it is to take the first of the page budget's remainder; advanced by
442
+ * every {@link select_for_draw}.
443
+ * @type {number}
444
+ */
445
+ #virtual_budget_turn = 0;
446
+
447
+ /**
448
+ * Scratch for {@link graph_virtual_feedback}: the scene's virtual maps this frame.
449
+ * @type {VirtualShadowMap[]}
450
+ */
451
+ #vsm_feedback_maps = [];
452
+
315
453
  /**
316
454
  * @returns {number} number of shadowmaps rasterized in the most recent draw pass
317
455
  */
@@ -528,6 +666,256 @@ export class GPUSceneShadowmapContext {
528
666
  map.caster_range_valid = false;
529
667
  }
530
668
 
669
+ /**
670
+ * Give a virtual shadow map its rows of the light database: its `shadow_virtual` record, and its
671
+ * runs of window corners, page-table entries — every cell empty — and slots — every slot empty.
672
+ * The record is written for real by the map's first draw, before anything samples it.
673
+ *
674
+ * @param {VirtualShadowMap} map
675
+ */
676
+ #claim_virtual(map) {
677
+ const database = this.#owner.database;
678
+ const manager = map.manager;
679
+
680
+ const levels = new Array(manager.level_count);
681
+
682
+ for (let i = 0; i < levels.length; i++) {
683
+ levels[i] = [0, 0];
684
+ }
685
+
686
+ const pages = new Array(manager.cell_count).fill(0);
687
+ const slots = new Array(manager.slots.capacity);
688
+
689
+ for (let i = 0; i < slots.length; i++) {
690
+ slots[i] = { level: VSM_SLOT_EMPTY, x: 0, y: 0 };
691
+ }
692
+
693
+ map.first_level = database.get('shadow_virtual_level').add_run(levels);
694
+ map.first_page = database.get('shadow_virtual_page').add_run(pages);
695
+ map.first_slot = database.get('shadow_virtual_slot').add_run(slots);
696
+ map.record_row = database.get('shadow_virtual').add(map.make_virtual_record());
697
+
698
+ // zeroed at creation: no slot has a mover over it yet
699
+ map.stale_flags = this.#graphics.device.createBuffer({
700
+ label: 'VSM / stale flags',
701
+ size: manager.slots.capacity * Uint32Array.BYTES_PER_ELEMENT,
702
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
703
+ });
704
+ }
705
+
706
+ /**
707
+ * Take a virtual shadow map's rows back. The page and slot rows the GPU reads go with the
708
+ * record that names them.
709
+ *
710
+ * @param {VirtualShadowMap} map
711
+ */
712
+ #release_virtual(map) {
713
+ if (map.record_row === -1) {
714
+ return;
715
+ }
716
+
717
+ const database = this.#owner.database;
718
+ const manager = map.manager;
719
+
720
+ const t_levels = database.get('shadow_virtual_level');
721
+ const t_pages = database.get('shadow_virtual_page');
722
+ const t_slots = database.get('shadow_virtual_slot');
723
+
724
+ for (let i = 0; i < manager.level_count; i++) {
725
+ t_levels.remove(map.first_level + i);
726
+ }
727
+
728
+ for (let i = 0; i < manager.cell_count; i++) {
729
+ t_pages.remove(map.first_page + i);
730
+ }
731
+
732
+ for (let i = 0; i < manager.slots.capacity; i++) {
733
+ t_slots.remove(map.first_slot + i);
734
+ }
735
+
736
+ database.get('shadow_virtual').remove(map.record_row);
737
+
738
+ map.record_row = -1;
739
+ map.first_level = -1;
740
+ map.first_page = -1;
741
+ map.first_slot = -1;
742
+ }
743
+
744
+ /**
745
+ * Write the rows of a virtual shadow map that changed since the last frame — page-table entries
746
+ * of cells installed, evicted or scrolled away, slots that changed hands, and window corners —
747
+ * and then its record.
748
+ *
749
+ * @param {VirtualShadowMap} map
750
+ * @param {GPUDatabase} database
751
+ */
752
+ #write_virtual_rows(map, database) {
753
+ const manager = map.manager;
754
+
755
+ const t_pages = database.get('shadow_virtual_page');
756
+ const t_slots = database.get('shadow_virtual_slot');
757
+
758
+ for (let i = 0; i < manager.dirty_cell_count; i++) {
759
+ const cell = manager.dirty_cell(i);
760
+
761
+ t_pages.set(map.first_page + cell, manager.cell_entry(cell));
762
+ }
763
+
764
+ const slot_record = this.#virtual_slot_record;
765
+
766
+ for (let i = 0; i < manager.dirty_slot_count; i++) {
767
+ const slot = manager.dirty_slot(i);
768
+ const level = manager.slot_level(slot);
769
+
770
+ slot_record.level = level === -1 ? VSM_SLOT_EMPTY : level;
771
+ slot_record.x = manager.slot_x(slot);
772
+ slot_record.y = manager.slot_y(slot);
773
+
774
+ t_slots.set(map.first_slot + slot, slot_record);
775
+ }
776
+
777
+ if (manager.levels_dirty) {
778
+ const t_levels = database.get('shadow_virtual_level');
779
+ const corner = this.#virtual_level_record;
780
+
781
+ for (let level = 0; level < manager.level_count; level++) {
782
+ corner[0] = manager.origin_x(level);
783
+ corner[1] = manager.origin_y(level);
784
+
785
+ t_levels.set(map.first_level + level, corner);
786
+ }
787
+ }
788
+
789
+ manager.clear_dirty();
790
+
791
+ database.get('shadow_virtual').set(map.record_row, map.make_virtual_record());
792
+ }
793
+
794
+ /**
795
+ * Record this frame's virtual shadow map feedback — the casters that moved, the pages they made
796
+ * stale, the pages the view's pixels ask for — into the frame's `Shading` graph once the main
797
+ * view's depth exists, and read it back for the maps' next frames. Does nothing in a scene
798
+ * without virtual shadow maps. See {@link graph_vsm_feedback}.
799
+ *
800
+ * @param {Object} options
801
+ * @param {FrameGraph} options.graph
802
+ * @param {import("../../view/GPUViewContext.js").GPUViewContext} options.view the main view
803
+ * @param {number} options.depth graph handle of the main view's depth
804
+ */
805
+ graph_virtual_feedback({ graph, view, depth }) {
806
+ const maps = this.#vsm_feedback_maps;
807
+ const all = this.#registry.maps;
808
+
809
+ maps.length = 0;
810
+
811
+ for (let i = 0; i < all.length; i++) {
812
+ if (all[i].isVirtualShadowMap === true) {
813
+ maps.push(all[i]);
814
+ }
815
+ }
816
+
817
+ if (maps.length === 0) {
818
+ // no virtual map to feed: the casters' memory goes, and starts afresh with the next map
819
+ if (this.#vsm_movers !== null) {
820
+ this.#vsm_movers.destroy();
821
+ this.#vsm_movers = null;
822
+ }
823
+
824
+ return;
825
+ }
826
+
827
+ if (this.#vsm_movers === null) {
828
+ this.#vsm_movers = new VSMMoverTracker(this.#graphics.device);
829
+ }
830
+
831
+ graph_vsm_feedback({
832
+ graph,
833
+ view,
834
+ depth,
835
+ maps,
836
+ tracker: this.#vsm_movers,
837
+ light_database: this.#owner.database,
838
+ });
839
+
840
+ // the graph holds what it needs; don't keep the maps reachable from here until next frame
841
+ maps.length = 0;
842
+ }
843
+
844
+ /**
845
+ * Rasterize the pages a virtual shadow map chose this frame, each into its slot of the pool
846
+ * through a pooled view aimed at the page — the {@link #draw_direct} path, with the Fast
847
+ * pipeline: a pooled view's last depth belongs to some other page, and occlusion buys nothing on
848
+ * a slot.
849
+ *
850
+ * @param {VirtualShadowMap} map
851
+ * @param {ShadeGPUCommandContext} command_ctx
852
+ */
853
+ #draw_virtual(map, command_ctx) {
854
+ const manager = map.manager;
855
+ const count = manager.job_count;
856
+
857
+ if (count === 0) {
858
+ return;
859
+ }
860
+
861
+ const views = map.views;
862
+
863
+ while (views.length < count) {
864
+ const camera = new OrthographicCamera();
865
+ camera.update();
866
+
867
+ const view = new View(camera, this.#scene);
868
+ view.label = `Virtual Shadow Page ${views.length}`;
869
+
870
+ views.push(view);
871
+ }
872
+
873
+ const graph = new FrameGraph("virtual shadow pages");
874
+ const jobs = new Array(count);
875
+
876
+ const pool = map.layout[0];
877
+ const slot_size = map.slot_size;
878
+ const per_axis = manager.slots.slots_per_axis;
879
+
880
+ for (let i = 0; i < count; i++) {
881
+ const view = views[i];
882
+
883
+ map.aim_page_camera(view.camera, manager.job_level(i), manager.job_x(i), manager.job_y(i));
884
+
885
+ const view_ctx = this.shadow_view_manager.obtain(view);
886
+
887
+ view_ctx.update(this.#graphics);
888
+ view_ctx.setViewportSize(slot_size, slot_size);
889
+
890
+ const slot = manager.job_slot(i);
891
+
892
+ const job = new RasterizationJob();
893
+ job.pipeline_type = RasterizationPipelineType.Fast;
894
+ job.opaque_pass = shadow_rasterization_pass_descriptor;
895
+ job.alpha_tested_pass = shadow_rasterization_alpha_tested_pass_descriptor;
896
+ job.cull_policy = SHADOW_CULL_POLICY;
897
+ job.render_target = this.#render_target;
898
+ job.view_ctx = view_ctx;
899
+ job.viewport = [
900
+ pool.x0 + (slot % per_axis) * slot_size,
901
+ pool.y0 + ((slot / per_axis) | 0) * slot_size,
902
+ slot_size,
903
+ slot_size,
904
+ ];
905
+
906
+ jobs[i] = job;
907
+ }
908
+
909
+ execute_rasterization_jobs({
910
+ jobs,
911
+ limits: this.#graphics.collection_limits,
912
+ command_ctx,
913
+ graph,
914
+ });
915
+
916
+ command_ctx.encodeGraph(graph);
917
+ }
918
+
531
919
  /**
532
920
  * Measure this frame's casters against the map's cascades and publish the result to their
533
921
  * rows, for the readback to carry to the next frame's fit.
@@ -712,9 +1100,44 @@ export class GPUSceneShadowmapContext {
712
1100
 
713
1101
  // Step 2: recompute projected areas. Writing onto the map so the policy + scorer can
714
1102
  // just read `map.projected_area_px` without threading a parallel array through.
1103
+ let virtual_maps = 0;
1104
+
715
1105
  for (let i = 0; i < n; i++) {
716
1106
  const m = shadowmaps[i];
717
1107
  m.projected_area_px = compute_light_projected_area_px(m.light, main_camera, view_resolution);
1108
+
1109
+ if (m.isVirtualShadowMap === true) {
1110
+ virtual_maps++;
1111
+ }
1112
+ }
1113
+
1114
+ if (virtual_maps > 0) {
1115
+ // the scene's page budget, shared out between its virtual maps: all of it to the one
1116
+ // map of a scene with one sun; with several, the pages that do not divide evenly go
1117
+ // round them a frame at a time, so a budget below the suns' count starves none
1118
+ const budget = this.#virtual_page_budget;
1119
+ const share = Math.floor(budget / virtual_maps);
1120
+ const remainder = budget - share * virtual_maps;
1121
+ const first_extra = this.#virtual_budget_turn++ % virtual_maps;
1122
+
1123
+ let k = 0;
1124
+
1125
+ for (let i = 0; i < n; i++) {
1126
+ const m = shadowmaps[i];
1127
+
1128
+ if (m.isVirtualShadowMap !== true) {
1129
+ continue;
1130
+ }
1131
+
1132
+ const turn = (k - first_extra + virtual_maps) % virtual_maps;
1133
+
1134
+ // what its update works with this frame
1135
+ m.page_budget = share + (turn < remainder ? 1 : 0);
1136
+ m.lod_bias = this.virtual_lod_bias;
1137
+ m.view_height = view_resolution[1];
1138
+
1139
+ k++;
1140
+ }
718
1141
  }
719
1142
 
720
1143
  // Step 2b: a cached map whose last cut of virtual geometry was clamped, and under which pages
@@ -810,10 +1233,15 @@ export class GPUSceneShadowmapContext {
810
1233
  continue;
811
1234
  }
812
1235
 
1236
+ if (map.isVirtualShadowMap === true) {
1237
+ // its rows change with its pages, whether or not anything else about it did
1238
+ this.#write_virtual_rows(map, database);
1239
+ }
1240
+
813
1241
  if (map.metadata_changed) {
814
1242
  table.set(slot, map.make_record());
815
1243
 
816
- if (light.isDirectionalLight) {
1244
+ if (light.isDirectionalLight && map.isVirtualShadowMap !== true) {
817
1245
  // the record names the cascades; their rows hold what to sample — written a
818
1246
  // field at a time, since the GPU owns the rest of the row
819
1247
  for (let j = 0; j < map.cascade_count; j++) {
@@ -921,7 +1349,9 @@ export class GPUSceneShadowmapContext {
921
1349
 
922
1350
  const light = map.light;
923
1351
 
924
- if (light.isDirectionalLight || light.isSpotLight) {
1352
+ if (map.isVirtualShadowMap === true) {
1353
+ this.#draw_virtual(map, command_ctx);
1354
+ } else if (light.isDirectionalLight || light.isSpotLight) {
925
1355
  this.#draw_direct(map, command_ctx);
926
1356
  } else if (light.isPointLight) {
927
1357
  if (map.isTetrahedron) {
@@ -1011,7 +1441,7 @@ export class GPUSceneShadowmapContext {
1011
1441
  const job = new RasterizationJob();
1012
1442
  job.opaque_pass = shadow_rasterization_pass_descriptor;
1013
1443
  job.alpha_tested_pass = shadow_rasterization_alpha_tested_pass_descriptor;
1014
- job.cull_policy = CullPolicy.Reversed;
1444
+ job.cull_policy = SHADOW_CULL_POLICY;
1015
1445
  job.render_target = this.#render_target;
1016
1446
  job.view_ctx = view_ctx;
1017
1447
  job.viewport = [atlas_aabb.x0, atlas_aabb.y0, atlas_aabb.width, atlas_aabb.height];
@@ -1129,7 +1559,7 @@ export class GPUSceneShadowmapContext {
1129
1559
  job.pipeline_type = RasterizationPipelineType.Fast;
1130
1560
  job.opaque_pass = shadow_rasterization_pass_descriptor;
1131
1561
  job.alpha_tested_pass = shadow_rasterization_alpha_tested_pass_descriptor;
1132
- job.cull_policy = CullPolicy.Reversed;
1562
+ job.cull_policy = SHADOW_CULL_POLICY;
1133
1563
  job.render_target = null;
1134
1564
  job.view_ctx = view_ctx;
1135
1565
  job.viewport = [
@@ -1275,7 +1705,7 @@ export class GPUSceneShadowmapContext {
1275
1705
  job.pipeline_type = RasterizationPipelineType.Fast;
1276
1706
  job.opaque_pass = shadow_rasterization_pass_descriptor;
1277
1707
  job.alpha_tested_pass = shadow_rasterization_alpha_tested_pass_descriptor;
1278
- job.cull_policy = CullPolicy.Reversed;
1708
+ job.cull_policy = SHADOW_CULL_POLICY;
1279
1709
  job.render_target = null;
1280
1710
  job.view_ctx = view_ctx;
1281
1711
  job.viewport = [
@@ -1407,7 +1837,12 @@ export class GPUSceneShadowmapContext {
1407
1837
  // free the per-view/-camera GPU contexts — without this an evicted map's buffers leak
1408
1838
  this.#release_map_gpu_contexts(map);
1409
1839
 
1410
- if (map.light.isDirectionalLight === true) {
1840
+ if (map.isVirtualShadowMap === true) {
1841
+ this.#release_virtual(map);
1842
+
1843
+ // a reading of its feedback still on its way back finds the map gone and drops itself
1844
+ map.destroy();
1845
+ } else if (map.light.isDirectionalLight === true) {
1411
1846
  this.#release_cascades(map);
1412
1847
  }
1413
1848
 
@@ -1463,13 +1898,16 @@ export class GPUSceneShadowmapContext {
1463
1898
  let changed = false;
1464
1899
 
1465
1900
  const use_tetra = this.use_tetrahedron_point_shadows;
1901
+ const use_virtual = this.use_virtual_shadow_maps;
1466
1902
  const cascade_count = this.#cascade_count;
1903
+ const first_texel_size = this.#virtual_first_texel_size;
1467
1904
  const newcomers = this.#reconcile_newcomers;
1468
1905
 
1469
1906
  /**
1470
1907
  * Whether a map was built under settings that no longer hold — a point map by the other
1471
- * generator (cube vs tetra), whose face count and draw path no longer match, or a
1472
- * directional map with another number of cascades — and so must be rebuilt.
1908
+ * generator (cube vs tetra), whose face count and draw path no longer match, a directional
1909
+ * map of the other kind (cascades vs virtual), one with another number of cascades, or a
1910
+ * virtual one with another first texel — and so must be rebuilt.
1473
1911
  *
1474
1912
  * @param {Shadowmap} map
1475
1913
  * @returns {boolean}
@@ -1482,7 +1920,15 @@ export class GPUSceneShadowmapContext {
1482
1920
  }
1483
1921
 
1484
1922
  if (light.isDirectionalLight) {
1485
- return map.cascade_count !== cascade_count;
1923
+ const is_virtual = map.isVirtualShadowMap === true;
1924
+
1925
+ if (is_virtual !== use_virtual) {
1926
+ return true;
1927
+ }
1928
+
1929
+ return is_virtual
1930
+ ? map.manager.first_texel_size !== first_texel_size
1931
+ : map.cascade_count !== cascade_count;
1486
1932
  }
1487
1933
 
1488
1934
  return false;
@@ -1568,6 +2014,43 @@ export class GPUSceneShadowmapContext {
1568
2014
  registry.add(map);
1569
2015
  };
1570
2016
 
2017
+ const add_virtual = (light) => {
2018
+ // the pool: as many whole slots as fit the default pool, or the atlas when it is smaller
2019
+ const slot_size = VSM_PAGE_SIZE + 2 * SHADOWMAP_ATLAS_BORDER;
2020
+ const pool_side = Math.floor(Math.min(VSM_POOL_SIZE, this.atlas.size.x, this.atlas.size.y) / slot_size) * slot_size;
2021
+
2022
+ // the pool first: an atlas without room for it throws here, every frame the sun is
2023
+ // retried, and the map's page bookkeeping — megabytes — is not built for nothing
2024
+ const pool = this.bind(pool_side);
2025
+
2026
+ let map;
2027
+
2028
+ try {
2029
+ map = new VirtualShadowMap({
2030
+ level_count: VSM_LEVEL_COUNT,
2031
+ pages_per_level: VSM_PAGES_PER_LEVEL,
2032
+ first_texel_size,
2033
+ page_size: VSM_PAGE_SIZE,
2034
+ border: SHADOWMAP_ATLAS_BORDER,
2035
+ pool_size: pool_side,
2036
+ request_capacity: VSM_DEFAULT_REQUEST_CAPACITY,
2037
+ protect_frames: VSM_PROTECT_FRAMES,
2038
+ depth_extent: VSM_DEPTH_EXTENT,
2039
+ });
2040
+
2041
+ map.light = light;
2042
+ map.layout[0] = pool;
2043
+
2044
+ this.#claim_virtual(map);
2045
+ } catch (e) {
2046
+ // nothing registered the map: give its pool back rather than leave an orphan rect
2047
+ this.atlas.remove(pool);
2048
+ throw e;
2049
+ }
2050
+
2051
+ registry.add(map);
2052
+ };
2053
+
1571
2054
  const add_spot = (light) => {
1572
2055
  const map = new SpotLightShadowmap();
1573
2056
  map.light = light;
@@ -1627,7 +2110,11 @@ export class GPUSceneShadowmapContext {
1627
2110
  }
1628
2111
 
1629
2112
  if (light.isDirectionalLight) {
1630
- add_directional(light);
2113
+ if (use_virtual) {
2114
+ add_virtual(light);
2115
+ } else {
2116
+ add_directional(light);
2117
+ }
1631
2118
  changed = true;
1632
2119
  } else if (light.isSpotLight) {
1633
2120
  add_spot(light);
@@ -1711,6 +2198,11 @@ export class GPUSceneShadowmapContext {
1711
2198
  this.#caster_scratch = null;
1712
2199
  }
1713
2200
 
2201
+ if (this.#vsm_movers !== null) {
2202
+ this.#vsm_movers.destroy();
2203
+ this.#vsm_movers = null;
2204
+ }
2205
+
1714
2206
  this.texture.destroy();
1715
2207
 
1716
2208
  const maps = this.#registry.maps;
@@ -159,8 +159,9 @@ export class Shadowmap {
159
159
  * scene's GPU database. Shape matches the table's element type:
160
160
  * - point → vec4f (atlas rect; sampling is direction-based, no projection needed)
161
161
  * - spot → SHADOWMAP_VIEW_METADATA_STRUCT (atlas + projection)
162
- * - directional → CSM_SHADOWMAP_STRUCT (where its cascades are: a range over the
163
- * `shadow_cascade` table, whose rows the shadow context writes separately)
162
+ * - directional → DIRECTIONAL_SHADOWMAP_STRUCT (where its cascades are: a range over the
163
+ * `shadow_cascade` table, whose rows the shadow context writes separately; or which
164
+ * `shadow_virtual` row is its virtual shadow map)
164
165
  * @returns {*}
165
166
  */
166
167
  make_record(): any;
@@ -1 +1 @@
1
- {"version":3,"file":"Shadowmap.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shadow/map/Shadowmap.js"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;IACI,WAAO;IAEP;;OAEG;IACH,OAFU,KAAK,CAEV;IAEL;;;;OAIG;IACH,QAFU,KAAK,EAAE,CAEL;IAEZ;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,gBAFU,KAAK,EAAE,GAAG,IAAI,CAEF;IAEtB;;;OAGG;IACH,OAFU,IAAI,EAAE,CAEL;IAEX;;;;;;;;;;;OAWG;IACH,mBAFU,MAAM,CAEM;IAEtB;;;;;;OAMG;IACH,0BAFU,MAAM,CAEc;IAE9B;;;;;;;OAOG;IACH,yBAFU,MAAM,CAEa;IAE7B;;;;;;;;;;;;;;;;;OAiBG;IACH,YAFU,OAAO,CAEC;IAElB;;;;;;;OAOG;IACH,uBAFU,OAAO,CAEa;IAE9B;;;;OAIG;IACH,kBAFU,MAAM,CAEK;IAErB;;;;;;;;OAQG;IACH,aAFU,OAAO,CAEG;IAEpB;;;;;;;;;;;;;;OAcG;IACH,kBAFU,OAAO,CAEO;IAExB;;;;;;;;;;;OAWG;IACH,iBAFU,MAAM,CAEI;IAEpB;;;;OAIG;IACH,oBAHW,MAAM,aACN,eAAe,QAIzB;IAED;;;;;;;;OAQG;IACH,mBAEC;IAED,gBAEC;CACJ;oBA7LY,OAAO,wCAAwC,EAAE,OAAO;qBACxD,OAAO,wBAAwB,EAAE,MAAM;oBACvC,OAAO,4BAA4B,EAAE,KAAK;8BAC1C,OAAO,gCAAgC,EAAE,eAAe;mBACxD,OAAO,oBAAoB,EAAE,IAAI"}
1
+ {"version":3,"file":"Shadowmap.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shadow/map/Shadowmap.js"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;IACI,WAAO;IAEP;;OAEG;IACH,OAFU,KAAK,CAEV;IAEL;;;;OAIG;IACH,QAFU,KAAK,EAAE,CAEL;IAEZ;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,gBAFU,KAAK,EAAE,GAAG,IAAI,CAEF;IAEtB;;;OAGG;IACH,OAFU,IAAI,EAAE,CAEL;IAEX;;;;;;;;;;;OAWG;IACH,mBAFU,MAAM,CAEM;IAEtB;;;;;;OAMG;IACH,0BAFU,MAAM,CAEc;IAE9B;;;;;;;OAOG;IACH,yBAFU,MAAM,CAEa;IAE7B;;;;;;;;;;;;;;;;;OAiBG;IACH,YAFU,OAAO,CAEC;IAElB;;;;;;;OAOG;IACH,uBAFU,OAAO,CAEa;IAE9B;;;;OAIG;IACH,kBAFU,MAAM,CAEK;IAErB;;;;;;;;OAQG;IACH,aAFU,OAAO,CAEG;IAEpB;;;;;;;;;;;;;;OAcG;IACH,kBAFU,OAAO,CAEO;IAExB;;;;;;;;;;;OAWG;IACH,iBAFU,MAAM,CAEI;IAEpB;;;;OAIG;IACH,oBAHW,MAAM,aACN,eAAe,QAIzB;IAED;;;;;;;;;OASG;IACH,mBAEC;IAED,gBAEC;CACJ;oBA9LY,OAAO,wCAAwC,EAAE,OAAO;qBACxD,OAAO,wBAAwB,EAAE,MAAM;oBACvC,OAAO,4BAA4B,EAAE,KAAK;8BAC1C,OAAO,gCAAgC,EAAE,eAAe;mBACxD,OAAO,oBAAoB,EAAE,IAAI"}
@@ -177,8 +177,9 @@ export class Shadowmap {
177
177
  * scene's GPU database. Shape matches the table's element type:
178
178
  * - point → vec4f (atlas rect; sampling is direction-based, no projection needed)
179
179
  * - spot → SHADOWMAP_VIEW_METADATA_STRUCT (atlas + projection)
180
- * - directional → CSM_SHADOWMAP_STRUCT (where its cascades are: a range over the
181
- * `shadow_cascade` table, whose rows the shadow context writes separately)
180
+ * - directional → DIRECTIONAL_SHADOWMAP_STRUCT (where its cascades are: a range over the
181
+ * `shadow_cascade` table, whose rows the shadow context writes separately; or which
182
+ * `shadow_virtual` row is its virtual shadow map)
182
183
  * @returns {*}
183
184
  */
184
185
  make_record() {
@@ -1,6 +1,14 @@
1
1
  /**
2
2
  * Samples the shadow atlas for a given light, using the supplied shadow projection matrix and
3
3
  * atlas rect. Applies receiver normal-offset bias to reduce self-shadowing.
4
+ *
5
+ * Two entry points: `sample_shadowmap_atlas_for_light` sizes the bias by a texel's world size as
6
+ * read off the matrix, and `sample_shadowmap_atlas_with_texel` takes that size from a caller that
7
+ * knows it exactly — a virtual shadow map's page, whose texel is fixed by its level, where the
8
+ * matrix's reading grows with the depth range as the light turns toward a world axis.
9
+ *
10
+ * A receiver past the far plane is shadowed only by what was drawn in front of it: its reference
11
+ * depth is held just above the cleared value, which would otherwise compare as shadowed.
4
12
  * @type {CodeChunk}
5
13
  */
6
14
  export const chunk_sample_shadowmap_atlas_for_light: CodeChunk;