@woosh/meep-engine 3.53.0 → 3.55.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 (249) hide show
  1. package/package.json +1 -1
  2. package/src/core/math/noise/worley_noise_2d.d.ts +14 -0
  3. package/src/core/math/noise/worley_noise_2d.d.ts.map +1 -0
  4. package/src/core/math/noise/worley_noise_2d.js +74 -0
  5. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts +6 -1
  6. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts.map +1 -1
  7. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.js +363 -325
  8. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.d.ts +93 -14
  9. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.d.ts.map +1 -1
  10. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.js +322 -183
  11. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.d.ts +9 -0
  12. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.d.ts.map +1 -1
  13. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +110 -83
  14. package/src/engine/graphics/texture/atlas/SpriteAtlas.d.ts +42 -0
  15. package/src/engine/graphics/texture/atlas/SpriteAtlas.d.ts.map +1 -0
  16. package/src/engine/{graphics3/decal/DecalAtlas.js → graphics/texture/atlas/SpriteAtlas.js} +202 -194
  17. package/src/engine/{graphics3/decal/DecalAtlasFill.d.ts → graphics/texture/atlas/SpriteAtlasFill.d.ts} +4 -4
  18. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.d.ts.map +1 -0
  19. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.js +30 -0
  20. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts +7 -0
  21. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  22. package/src/engine/graphics/texture/atlas/TextureAtlas.js +11 -1
  23. package/src/engine/{graphics3/decal/decal_atlas_border.d.ts → graphics/texture/atlas/sprite_atlas_border.d.ts} +12 -12
  24. package/src/engine/graphics/texture/atlas/sprite_atlas_border.d.ts.map +1 -0
  25. package/src/engine/{graphics3/decal/decal_atlas_border.js → graphics/texture/atlas/sprite_atlas_border.js} +69 -69
  26. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  27. package/src/engine/graphics3/DecalSystem.js +447 -450
  28. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +9 -22
  29. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  30. package/src/engine/graphics3/GPUParticleEmitterSystem.js +892 -951
  31. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  32. package/src/generation/foliage/FOLIAGE_DESIGN.md +2 -1
  33. package/src/generation/foliage/config/BarkMaterialConfig.d.ts +4 -3
  34. package/src/generation/foliage/config/BarkMaterialConfig.d.ts.map +1 -1
  35. package/src/generation/foliage/config/BarkMaterialConfig.js +6 -6
  36. package/src/generation/foliage/config/FoliageBarkType.d.ts +11 -0
  37. package/src/generation/foliage/config/FoliageBarkType.d.ts.map +1 -0
  38. package/src/generation/foliage/config/FoliageBarkType.js +16 -0
  39. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts +58 -5
  40. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts.map +1 -1
  41. package/src/generation/foliage/config/FoliageMaterialConfig.js +279 -181
  42. package/src/generation/foliage/index.d.ts +2 -1
  43. package/src/generation/foliage/index.js +44 -43
  44. package/src/generation/foliage/material/make_bark_material.d.ts.map +1 -1
  45. package/src/generation/foliage/material/make_bark_material.js +23 -95
  46. package/src/generation/foliage/material/make_foliage_material.d.ts +29 -3
  47. package/src/generation/foliage/material/make_foliage_material.d.ts.map +1 -1
  48. package/src/generation/foliage/material/make_foliage_material.js +605 -359
  49. package/src/generation/foliage/mesh/branch_collar_project.d.ts.map +1 -1
  50. package/src/generation/foliage/mesh/branch_collar_project.js +205 -203
  51. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts +15 -0
  52. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts.map +1 -1
  53. package/src/generation/foliage/mesh/foliage_collect_attachments.js +569 -502
  54. package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
  55. package/src/generation/foliage/mesh/tree_mesh_build_bark.js +1049 -1047
  56. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts +2 -1
  57. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts.map +1 -1
  58. package/src/generation/foliage/mesh/tree_mesh_build_foliage.js +454 -415
  59. package/src/generation/foliage/presets/make_tree_species_preset.d.ts.map +1 -1
  60. package/src/generation/foliage/presets/make_tree_species_preset.js +870 -837
  61. package/src/generation/foliage/skeleton/crown_envelope_evaluate.js +121 -121
  62. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts +10 -7
  63. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts.map +1 -1
  64. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.js +206 -179
  65. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts.map +1 -1
  66. package/src/generation/foliage/skeleton/tree_skeleton_grow.js +1871 -1104
  67. package/src/shade/RENDERER_CONTRACT.md +5 -2
  68. package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -1
  69. package/src/shade/device/SHADE_DEVICE_FLOOR.js +2 -0
  70. package/src/shade/playground/foliage_wind/foliage_world.d.ts +6 -0
  71. package/src/shade/playground/foliage_wind/foliage_world.d.ts.map +1 -1
  72. package/src/shade/playground/foliage_wind/foliage_world.js +602 -593
  73. package/src/shade/playground/foliage_wind/index.html +260 -254
  74. package/src/shade/playground/foliage_wind/main.js +275 -263
  75. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  76. package/src/shade/renderer/Renderer.js +1 -1
  77. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  78. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  79. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  80. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  81. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  82. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +1 -1
  83. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +3 -3
  84. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  85. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +350 -294
  86. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts +0 -2
  87. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts.map +1 -1
  88. package/src/shade/renderer/hiz/generate_downsampler_code.js +99 -100
  89. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts +15 -0
  90. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts.map +1 -0
  91. package/src/shade/renderer/hiz/hzb_pick_alignment.js +148 -0
  92. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.d.ts +28 -0
  93. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.d.ts.map +1 -0
  94. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.js +58 -0
  95. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +5 -5
  96. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  97. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +57 -67
  98. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +3 -3
  99. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +1 -1
  100. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.js +111 -112
  101. package/src/shade/renderer/hiz/shader_hzb_spd.d.ts +5 -0
  102. package/src/shade/renderer/hiz/shader_hzb_spd.d.ts.map +1 -0
  103. package/src/shade/renderer/hiz/shader_hzb_spd.js +160 -0
  104. package/src/shade/renderer/light/GPULightCollection.d.ts +8 -0
  105. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  106. package/src/shade/renderer/light/GPULightCollection.js +67 -0
  107. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.d.ts +7 -0
  108. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.d.ts.map +1 -0
  109. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.js +13 -0
  110. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.d.ts +10 -0
  111. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.d.ts.map +1 -0
  112. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.js +72 -0
  113. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts +39 -0
  114. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts.map +1 -0
  115. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.js +90 -0
  116. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts +12 -0
  117. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts.map +1 -0
  118. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.js +56 -0
  119. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts +31 -0
  120. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts.map +1 -0
  121. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.js +196 -0
  122. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts +10 -0
  123. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts.map +1 -0
  124. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.js +163 -0
  125. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.d.ts +4 -0
  126. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.d.ts.map +1 -0
  127. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.js +137 -0
  128. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  129. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  130. package/src/shade/renderer/particles/GPUParticleSystem.js +4 -3
  131. package/src/shade/renderer/particles/graph_particles_avboit.d.ts +4 -3
  132. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  133. package/src/shade/renderer/particles/graph_particles_avboit.js +28 -11
  134. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +9 -13
  135. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts.map +1 -1
  136. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +25 -20
  137. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  138. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +21 -38
  139. package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.d.ts.map +1 -1
  140. package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.js +154 -147
  141. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +2 -1
  142. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  143. package/src/shade/renderer/postprocess/gtao/GTAO.js +5 -1
  144. package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.d.ts +8 -0
  145. package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.d.ts.map +1 -1
  146. package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.js +10 -1
  147. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  148. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +5 -0
  149. package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.d.ts +22 -0
  150. package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.d.ts.map +1 -0
  151. package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.js +33 -0
  152. package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.d.ts +22 -0
  153. package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.d.ts.map +1 -0
  154. package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.js +56 -0
  155. package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.d.ts +13 -0
  156. package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.d.ts.map +1 -0
  157. package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.js +42 -0
  158. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts +2 -1
  159. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  160. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +382 -357
  161. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  162. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +414 -410
  163. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  164. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -7
  165. package/src/shade/renderer/shader/chunk/depth/chunk_query_depth_from_screen_space_bb.d.ts.map +1 -1
  166. package/src/shade/renderer/shader/chunk/depth/chunk_query_depth_from_screen_space_bb.js +62 -62
  167. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.d.ts +6 -0
  168. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.d.ts.map +1 -0
  169. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.js +19 -0
  170. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_get_mip_resolution.d.ts.map +1 -1
  171. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_get_mip_resolution.js +16 -10
  172. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_hierarchical_raymarch.d.ts.map +1 -1
  173. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_hierarchical_raymarch.js +118 -117
  174. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts +41 -7
  175. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts.map +1 -1
  176. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.js +162 -58
  177. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +1 -1
  178. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +10 -12
  179. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  180. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +583 -606
  181. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +1 -1
  182. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +640 -640
  183. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts +113 -0
  184. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts.map +1 -0
  185. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.js +484 -0
  186. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts +50 -0
  187. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts.map +1 -0
  188. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.js +60 -0
  189. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts +25 -0
  190. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts.map +1 -0
  191. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.js +47 -0
  192. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.d.ts +74 -0
  193. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.d.ts.map +1 -0
  194. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.js +173 -0
  195. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts +26 -0
  196. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts.map +1 -0
  197. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.js +83 -0
  198. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts +36 -0
  199. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts.map +1 -0
  200. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.js +129 -0
  201. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts +121 -0
  202. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts.map +1 -0
  203. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.js +370 -0
  204. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts +16 -0
  205. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts.map +1 -0
  206. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.js +191 -0
  207. package/src/shade/renderer/wind/GPUSceneWind.d.ts +1 -1
  208. package/src/shade/renderer/wind/GPUSceneWind.js +1 -1
  209. package/src/engine/graphics3/decal/DecalAtlas.d.ts +0 -43
  210. package/src/engine/graphics3/decal/DecalAtlas.d.ts.map +0 -1
  211. package/src/engine/graphics3/decal/DecalAtlasFill.d.ts.map +0 -1
  212. package/src/engine/graphics3/decal/DecalAtlasFill.js +0 -32
  213. package/src/engine/graphics3/decal/decal_atlas_border.d.ts.map +0 -1
  214. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts +0 -26
  215. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts.map +0 -1
  216. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.js +0 -268
  217. package/src/shade/renderer/postprocess/ssr/copy_shader_f32.d.ts +0 -3
  218. package/src/shade/renderer/postprocess/ssr/copy_shader_f32.d.ts.map +0 -1
  219. package/src/shade/renderer/postprocess/ssr/copy_shader_f32.js +0 -32
  220. package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts +0 -17
  221. package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts.map +0 -1
  222. package/src/shade/renderer/postprocess/ssr/depth_max_shader.js +0 -98
  223. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts +0 -23
  224. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts.map +0 -1
  225. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.js +0 -118
  226. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.d.ts +0 -15
  227. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.d.ts.map +0 -1
  228. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.js +0 -49
  229. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts +0 -17
  230. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts.map +0 -1
  231. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.js +0 -50
  232. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts +0 -35
  233. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts.map +0 -1
  234. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.js +0 -105
  235. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts +0 -35
  236. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts.map +0 -1
  237. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.js +0 -80
  238. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.d.ts +0 -10
  239. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.d.ts.map +0 -1
  240. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.js +0 -51
  241. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.d.ts +0 -28
  242. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.d.ts.map +0 -1
  243. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.js +0 -39
  244. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +0 -48
  245. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +0 -1
  246. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +0 -121
  247. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts +0 -28
  248. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts.map +0 -1
  249. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.js +0 -237
@@ -1,450 +1,447 @@
1
- import { assert } from "../../core/assert.js";
2
- import { LoadingCache } from "../../core/cache/LoadingCache.js";
3
- import { strictEquals } from "../../core/function/strictEquals.js";
4
- import { computeStringHash } from "../../core/primitives/strings/computeStringHash.js";
5
- import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
6
- import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
7
- import { GameAssetType } from "../asset/GameAssetType.js";
8
- import { ImageRGBADataLoader } from "../asset/loaders/image/ImageRGBADataLoader.js";
9
- import { System } from "../ecs/System.js";
10
- import { Transform64 } from "../ecs/transform/Transform64.js";
11
- import { Decal } from "../graphics/ecs/decal/v2/Decal.js";
12
- import { ReferencedTextureAtlas } from "../graphics/texture/atlas/ReferencedTextureAtlas.js";
13
- import { sampler2d_ensure_uint8_RGBA } from "../graphics/texture/sampler/sampler2d_ensure_uint8_RGBA.js";
14
- import { DecalAtlas } from "./decal/DecalAtlas.js";
15
- import { DecalAtlasFill } from "./decal/DecalAtlasFill.js";
16
- import { decal_atlas_max_lod } from "./decal/decal_atlas_border.js";
17
- import {
18
- decal_identity_normal,
19
- decal_identity_orm,
20
- decal_pack_orm
21
- } from "./decal/decal_material_samplers.js";
22
- import { DECAL_RECORD_WORD_COUNT } from "./decal/DECAL_GPU_RECORD.js";
23
- import { sort_decals_by_priority, write_decal_record } from "./decal/decal_gpu_records.js";
24
- import { GPUDecalRenderer } from "./decal/GPUDecalRenderer.js";
25
-
26
- /**
27
- * Paints the game's decals into the G-buffer under Shade.
28
- *
29
- * **A decal is a surface override, not a tint** (D22): it carries albedo, a tangent-space normal,
30
- * roughness, metalness and emission, and it writes them into the G-buffer before anything is lit —
31
- * so ambient occlusion, screen-space reflection and shading all see a decal exactly as they see the
32
- * surface it is painted on. The renderer this replaces could only lerp diffuse colour inside a
33
- * forward shader.
34
- *
35
- * **Decals reach opaque surfaces only, by construction.** The G-buffer is all they touch and
36
- * transparency composites later, so a decal will never appear on water.
37
- *
38
- * The records are rebuilt every frame. That sounds wasteful and is the cheaper arrangement: the
39
- * froxel binning depends on the camera and so has to run every frame regardless, and rebuilding the
40
- * records alongside it means nothing has to watch a decal's colour for changes — which matters,
41
- * because the signal that would be watched does not fire for alpha-only edits.
42
- */
43
- export class DecalSystem extends System {
44
- dependencies = [Decal, Transform64];
45
-
46
- /**
47
- * @type {GraphicsEngine}
48
- */
49
- #graphics;
50
-
51
- /**
52
- * @type {AssetManager}
53
- */
54
- #assets;
55
-
56
- /**
57
- * @type {GPUDecalRenderer}
58
- */
59
- #renderer = new GPUDecalRenderer();
60
-
61
- /**
62
- * Three atlases, because their contents want different mip filters and different border fills —
63
- * and because albedo is sRGB while the other two are not.
64
- *
65
- * @type {DecalAtlas}
66
- */
67
- #atlas_albedo = new DecalAtlas(DecalAtlasFill.PremultipliedCoverage);
68
-
69
- /**
70
- * @type {DecalAtlas}
71
- */
72
- #atlas_normal = new DecalAtlas(DecalAtlasFill.EdgeExtend);
73
-
74
- /**
75
- * @type {DecalAtlas}
76
- */
77
- #atlas_orm = new DecalAtlas(DecalAtlasFill.EdgeExtend);
78
-
79
- /**
80
- * @type {ReferencedTextureAtlas}
81
- */
82
- #referenced_albedo = new ReferencedTextureAtlas(this.#atlas_albedo);
83
-
84
- /**
85
- * @type {ReferencedTextureAtlas}
86
- */
87
- #referenced_normal = new ReferencedTextureAtlas(this.#atlas_normal);
88
-
89
- /**
90
- * @type {ReferencedTextureAtlas}
91
- */
92
- #referenced_orm = new ReferencedTextureAtlas(this.#atlas_orm);
93
-
94
- /**
95
- * What every decal authoring no maps of its own shares. One patch each, packed once.
96
- *
97
- * @type {Sampler2D}
98
- */
99
- #identity_normal = decal_identity_normal();
100
-
101
- /**
102
- * @type {Sampler2D}
103
- */
104
- #identity_orm = decal_identity_orm();
105
-
106
- /**
107
- * @type {LoadingCache<string, Sampler2D>}
108
- */
109
- #textures;
110
-
111
- /**
112
- * The decals to draw, in link order until {@link update} sorts them into draw order.
113
- *
114
- * @type {Array<{entity: number, decal: Decal, transform: Transform, patches: object, listener: Function}>}
115
- */
116
- #entries = [];
117
-
118
- /**
119
- * @type {Map<number, object>}
120
- */
121
- #by_entity = new Map();
122
-
123
- /**
124
- * @type {Float32Array}
125
- */
126
- #records = new Float32Array(0);
127
-
128
- /**
129
- * @type {number}
130
- */
131
- #record_count = 0;
132
-
133
- /**
134
- * @type {RenderExtension|null}
135
- */
136
- #extension = null;
137
-
138
- /**
139
- * What the pass draws. Read-only — the decals follow the entities.
140
- *
141
- * @returns {Array}
142
- */
143
- get decals() {
144
- return this.#entries;
145
- }
146
-
147
- /**
148
- * @returns {number} decals packed for the GPU by the last update
149
- */
150
- get record_count() {
151
- return this.#record_count;
152
- }
153
-
154
- /**
155
- * What was packed, for a gate that wants to see it. Read-only.
156
- *
157
- * @returns {Float32Array}
158
- */
159
- get records() {
160
- return this.#records;
161
- }
162
-
163
- /**
164
- * @param {GraphicsEngine} graphics
165
- * @param {AssetManager} assets
166
- */
167
- constructor(graphics, assets) {
168
- super();
169
-
170
- assert.defined(graphics, 'graphics');
171
- assert.defined(assets, 'assets');
172
-
173
- this.#graphics = graphics;
174
- this.#assets = assets;
175
-
176
- this.#textures = new LoadingCache({
177
- load: key => this.#assets.promise(key, GameAssetType.Image)
178
- .then(asset => sampler2d_ensure_uint8_RGBA(asset.create())),
179
- keyHashFunction: computeStringHash,
180
- keyEqualityFunction: strictEquals
181
- });
182
- }
183
-
184
- async startup(entityManager) {
185
- this.entityManager = entityManager;
186
-
187
- const assets = this.#assets;
188
-
189
- if (!assets.hasLoaderForType(GameAssetType.Image)) {
190
- await assets.registerLoader(GameAssetType.Image, new ImageRGBADataLoader());
191
- }
192
-
193
- this.#extension = this.#graphics.add_extension(new DecalExtension(this));
194
- }
195
-
196
- async shutdown(entityManager) {
197
- if (this.#extension !== null) {
198
- this.#graphics.remove_extension(this.#extension);
199
-
200
- this.#extension = null;
201
- }
202
-
203
- this.#renderer.destroy();
204
-
205
- this.#entries.length = 0;
206
- this.#by_entity.clear();
207
- this.#record_count = 0;
208
- }
209
-
210
- /**
211
- * The `transform` IS the projection volume, and it projects along its local **+Z** — so the
212
- * surface being painted faces the other way. Get the sign backwards and every pixel of the
213
- * decal is rejected by the coverage fade, silently, with every CPU-side observable here
214
- * reporting success.
215
- *
216
- * @see Decal
217
- * @see chunk_decal_surface_frame
218
- *
219
- * @param {Decal} decal
220
- * @param {Transform64} transform
221
- * @param {number} entity
222
- */
223
- link(decal, transform, entity) {
224
- const entry = {
225
- entity,
226
- decal,
227
- transform,
228
- /**
229
- * Atlas patches, acquired as the textures arrive. A decal whose albedo has not loaded
230
- * yet is not packed at all — there is nothing to draw with, and drawing it with the
231
- * identity patch would put a white square on the ground until the image landed.
232
- */
233
- patches: { albedo: null, normal: null, orm: null }
234
- };
235
-
236
- this.#entries.push(entry);
237
- this.#by_entity.set(entity, entry);
238
-
239
- this.#acquire(entry);
240
- }
241
-
242
- /**
243
- * @param {Decal} decal
244
- * @param {Transform64} transform
245
- * @param {number} entity
246
- */
247
- unlink(decal, transform, entity) {
248
- const entry = this.#by_entity.get(entity);
249
-
250
- if (entry === undefined) {
251
- return;
252
- }
253
-
254
- this.#release(entry);
255
-
256
- const index = this.#entries.indexOf(entry);
257
-
258
- if (index !== -1) {
259
- this.#entries.splice(index, 1);
260
- }
261
-
262
- this.#by_entity.delete(entity);
263
- }
264
-
265
- /**
266
- * @param {object} entry
267
- */
268
- #release(entry) {
269
- const patches = entry.patches;
270
-
271
- if (patches.albedo !== null) {
272
- patches.albedo.release();
273
- patches.albedo = null;
274
- }
275
-
276
- if (patches.normal !== null) {
277
- patches.normal.release();
278
- patches.normal = null;
279
- }
280
-
281
- if (patches.orm !== null) {
282
- patches.orm.release();
283
- patches.orm = null;
284
- }
285
- }
286
-
287
- /**
288
- * Fetch this decal's textures and take a reference on the atlas patch each lands in.
289
- *
290
- * Reference counted, because combat spawns a footprint decal per step and every one of them
291
- * names the same file: packing a patch per footprint would repack three atlases and regenerate
292
- * their mip chains several times a second.
293
- *
294
- * @param {object} entry
295
- */
296
- #acquire(entry) {
297
- const decal = entry.decal;
298
-
299
- const albedo_uri = decal.albedo_uri;
300
-
301
- if (albedo_uri === "") {
302
- return;
303
- }
304
-
305
- this.#textures.get(albedo_uri).then(sampler => {
306
- if (this.#by_entity.get(entry.entity) !== entry) {
307
- // unlinked while the image was in flight
308
- return;
309
- }
310
-
311
- entry.patches.albedo = this.#referenced_albedo.acquire(sampler);
312
- });
313
-
314
- if (decal.uri_normal === "") {
315
- entry.patches.normal = this.#referenced_normal.acquire(this.#identity_normal);
316
- } else {
317
- this.#textures.get(decal.uri_normal).then(sampler => {
318
- if (this.#by_entity.get(entry.entity) !== entry) {
319
- return;
320
- }
321
-
322
- entry.patches.normal = this.#referenced_normal.acquire(sampler);
323
- });
324
- }
325
-
326
- if (decal.uri_orm === "" && decal.uri_emissive === "") {
327
- entry.patches.orm = this.#referenced_orm.acquire(this.#identity_orm);
328
-
329
- return;
330
- }
331
-
332
- Promise.all([
333
- decal.uri_orm === "" ? null : this.#textures.get(decal.uri_orm),
334
- decal.uri_emissive === "" ? null : this.#textures.get(decal.uri_emissive)
335
- ]).then(([orm, emissive]) => {
336
- if (this.#by_entity.get(entry.entity) !== entry) {
337
- return;
338
- }
339
-
340
- entry.patches.orm = this.#referenced_orm.acquire(decal_pack_orm(orm, emissive));
341
- });
342
- }
343
-
344
- update(timeDelta) {
345
- const entries = this.#entries;
346
-
347
- sort_decals_by_priority(entries);
348
-
349
- this.#atlas_albedo.update();
350
- this.#atlas_normal.update();
351
- this.#atlas_orm.update();
352
-
353
- const required = entries.length * DECAL_RECORD_WORD_COUNT;
354
-
355
- if (this.#records.length < required) {
356
- this.#records = new Float32Array(required);
357
- }
358
-
359
- const records = this.#records;
360
-
361
- let count = 0;
362
-
363
- for (let i = 0; i < entries.length; i++) {
364
- const entry = entries[i];
365
- const patches = entry.patches;
366
-
367
- if (patches.albedo === null || patches.normal === null || patches.orm === null) {
368
- // still waiting on an image; a decal is packed once it can be drawn properly
369
- continue;
370
- }
371
-
372
- const albedo = patches.albedo.getValue();
373
- const normal = patches.normal.getValue();
374
- const orm = patches.orm.getValue();
375
-
376
- write_decal_record(
377
- records,
378
- count * DECAL_RECORD_WORD_COUNT,
379
- entry.decal,
380
- entry.transform.matrix,
381
- { albedo: albedo.uv, normal: normal.uv, orm: orm.uv },
382
- {
383
- albedo: decal_atlas_max_lod(albedo.padding),
384
- normal: decal_atlas_max_lod(normal.padding),
385
- orm: decal_atlas_max_lod(orm.padding)
386
- }
387
- );
388
-
389
- count++;
390
- }
391
-
392
- this.#record_count = count;
393
- }
394
-
395
- /**
396
- * @param {FrameContext} frame
397
- */
398
- record(frame) {
399
- if (this.#record_count === 0) {
400
- /*
401
- Nothing to paint, so the frame keeps the G-buffer it already had. Skipping is what makes
402
- the replacement design free when no decal is on screen — the pass reads and rewrites
403
- every pixel when it runs.
404
- */
405
- return;
406
- }
407
-
408
- this.#renderer.graph_draw({
409
- frame,
410
- records: this.#records,
411
- record_count: this.#record_count,
412
- atlas_albedo: this.#atlas_albedo.sampler,
413
- atlas_normal: this.#atlas_normal.sampler,
414
- atlas_orm: this.#atlas_orm.sampler
415
- });
416
- }
417
- }
418
-
419
- /**
420
- * **Late on the phase**, because a decal composites over surfaces that are already written rather
421
- * than preceding them — anything another extension writes into the G-buffer at this phase. That
422
- * requirement used to be met by putting a registration call above an `await` and writing a comment
423
- * about it; here it is the declaration below. The textured ground no longer depends on it: terrain is
424
- * shaded in the G-buffer pass itself, before any extension runs.
425
- */
426
- export class DecalExtension extends RenderExtension {
427
- name = "decals";
428
-
429
- phase = FramePhase.AfterGBuffer;
430
-
431
- relative_order = 1;
432
-
433
- /**
434
- * @type {DecalSystem}
435
- */
436
- #system;
437
-
438
- /**
439
- * @param {DecalSystem} system
440
- */
441
- constructor(system) {
442
- super();
443
-
444
- this.#system = system;
445
- }
446
-
447
- record(frame) {
448
- this.#system.record(frame);
449
- }
450
- }
1
+ import { assert } from "../../core/assert.js";
2
+ import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
3
+ import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
4
+ import { GameAssetType } from "../asset/GameAssetType.js";
5
+ import { ImageRGBADataLoader } from "../asset/loaders/image/ImageRGBADataLoader.js";
6
+ import { System } from "../ecs/System.js";
7
+ import { Transform64 } from "../ecs/transform/Transform64.js";
8
+ import { Decal } from "../graphics/ecs/decal/v2/Decal.js";
9
+ import { ManagedAtlas } from "../graphics/texture/atlas/ManagedTextureAtlas.js";
10
+ import { SpriteAtlas } from "../graphics/texture/atlas/SpriteAtlas.js";
11
+ import { SpriteAtlasFill } from "../graphics/texture/atlas/SpriteAtlasFill.js";
12
+ import { sprite_atlas_max_lod } from "../graphics/texture/atlas/sprite_atlas_border.js";
13
+ import { sampler2d_ensure_uint8_RGBA } from "../graphics/texture/sampler/sampler2d_ensure_uint8_RGBA.js";
14
+ import {
15
+ decal_identity_normal,
16
+ decal_identity_orm,
17
+ decal_pack_orm
18
+ } from "./decal/decal_material_samplers.js";
19
+ import { DECAL_RECORD_WORD_COUNT } from "./decal/DECAL_GPU_RECORD.js";
20
+ import { sort_decals_by_priority, write_decal_record } from "./decal/decal_gpu_records.js";
21
+ import { GPUDecalRenderer } from "./decal/GPUDecalRenderer.js";
22
+
23
+ /**
24
+ * Paints the game's decals into the G-buffer under Shade.
25
+ *
26
+ * **A decal is a surface override, not a tint** (D22): it carries albedo, a tangent-space normal,
27
+ * roughness, metalness and emission, and it writes them into the G-buffer before anything is lit —
28
+ * so ambient occlusion, screen-space reflection and shading all see a decal exactly as they see the
29
+ * surface it is painted on. The renderer this replaces could only lerp diffuse colour inside a
30
+ * forward shader.
31
+ *
32
+ * **Decals reach opaque surfaces only, by construction.** The G-buffer is all they touch and
33
+ * transparency composites later, so a decal will never appear on water.
34
+ *
35
+ * The records are rebuilt every frame. That sounds wasteful and is the cheaper arrangement: the
36
+ * froxel binning depends on the camera and so has to run every frame regardless, and rebuilding the
37
+ * records alongside it means nothing has to watch a decal's colour for changes — which matters,
38
+ * because the signal that would be watched does not fire for alpha-only edits.
39
+ */
40
+ export class DecalSystem extends System {
41
+ dependencies = [Decal, Transform64];
42
+
43
+ /**
44
+ * @type {GraphicsEngine}
45
+ */
46
+ #graphics;
47
+
48
+ /**
49
+ * @type {AssetManager}
50
+ */
51
+ #assets;
52
+
53
+ /**
54
+ * @type {GPUDecalRenderer}
55
+ */
56
+ #renderer = new GPUDecalRenderer();
57
+
58
+ /**
59
+ * Three atlases, because their contents want different mip filters and different border fills —
60
+ * and because albedo is sRGB while the other two are not.
61
+ *
62
+ * @type {SpriteAtlas}
63
+ */
64
+ #atlas_albedo = new SpriteAtlas(SpriteAtlasFill.PremultipliedCoverage);
65
+
66
+ /**
67
+ * @type {SpriteAtlas}
68
+ */
69
+ #atlas_normal = new SpriteAtlas(SpriteAtlasFill.EdgeExtend);
70
+
71
+ /**
72
+ * @type {SpriteAtlas}
73
+ */
74
+ #atlas_orm = new SpriteAtlas(SpriteAtlasFill.EdgeExtend);
75
+
76
+ /**
77
+ * @type {ManagedAtlas}
78
+ */
79
+ #managed_albedo;
80
+
81
+ /**
82
+ * @type {ManagedAtlas}
83
+ */
84
+ #managed_normal;
85
+
86
+ /**
87
+ * @type {ManagedAtlas}
88
+ */
89
+ #managed_orm;
90
+
91
+ /**
92
+ * What every decal authoring no maps of its own shares. One patch each, packed once.
93
+ *
94
+ * @type {Sampler2D}
95
+ */
96
+ #identity_normal = decal_identity_normal();
97
+
98
+ /**
99
+ * @type {Sampler2D}
100
+ */
101
+ #identity_orm = decal_identity_orm();
102
+
103
+ /**
104
+ * The decals to draw, in link order until {@link update} sorts them into draw order.
105
+ *
106
+ * @type {Array<{entity: number, decal: Decal, transform: Transform, patches: object, listener: Function}>}
107
+ */
108
+ #entries = [];
109
+
110
+ /**
111
+ * @type {Map<number, object>}
112
+ */
113
+ #by_entity = new Map();
114
+
115
+ /**
116
+ * @type {Float32Array}
117
+ */
118
+ #records = new Float32Array(0);
119
+
120
+ /**
121
+ * @type {number}
122
+ */
123
+ #record_count = 0;
124
+
125
+ /**
126
+ * @type {RenderExtension|null}
127
+ */
128
+ #extension = null;
129
+
130
+ /**
131
+ * What the pass draws. Read-only — the decals follow the entities.
132
+ *
133
+ * @returns {Array}
134
+ */
135
+ get decals() {
136
+ return this.#entries;
137
+ }
138
+
139
+ /**
140
+ * @returns {number} decals packed for the GPU by the last update
141
+ */
142
+ get record_count() {
143
+ return this.#record_count;
144
+ }
145
+
146
+ /**
147
+ * What was packed, for a gate that wants to see it. Read-only.
148
+ *
149
+ * @returns {Float32Array}
150
+ */
151
+ get records() {
152
+ return this.#records;
153
+ }
154
+
155
+ /**
156
+ * @param {GraphicsEngine} graphics
157
+ * @param {AssetManager} assets
158
+ */
159
+ constructor(graphics, assets) {
160
+ super();
161
+
162
+ assert.defined(graphics, 'graphics');
163
+ assert.defined(assets, 'assets');
164
+
165
+ this.#graphics = graphics;
166
+ this.#assets = assets;
167
+
168
+ this.#managed_albedo = new ManagedAtlas({
169
+ assetManager: this.#assets,
170
+ atlas: this.#atlas_albedo
171
+ });
172
+
173
+ this.#managed_normal = new ManagedAtlas({
174
+ assetManager: this.#assets,
175
+ atlas: this.#atlas_normal
176
+ });
177
+
178
+ this.#managed_orm = new ManagedAtlas({
179
+ atlas: this.#atlas_orm,
180
+ load: async (key) => {
181
+ const separator = key.indexOf("\0");
182
+ const uri_orm = key.slice(0, separator);
183
+ const uri_emissive = key.slice(separator + 1);
184
+
185
+ const [orm, emissive] = await Promise.all([
186
+ uri_orm === "" ? null : this.#load_sampler(uri_orm),
187
+ uri_emissive === "" ? null : this.#load_sampler(uri_emissive)
188
+ ]);
189
+
190
+ return decal_pack_orm(orm, emissive);
191
+ }
192
+ });
193
+ }
194
+
195
+ /**
196
+ * @param {string} uri
197
+ * @returns {Promise<import("../graphics/texture/sampler/Sampler2D.js").Sampler2D>}
198
+ */
199
+ async #load_sampler(uri) {
200
+ const asset = await this.#assets.promise(uri, GameAssetType.Image);
201
+
202
+ return sampler2d_ensure_uint8_RGBA(asset.create());
203
+ }
204
+
205
+ async startup(entityManager) {
206
+ this.entityManager = entityManager;
207
+
208
+ const assets = this.#assets;
209
+
210
+ if (!assets.hasLoaderForType(GameAssetType.Image)) {
211
+ await assets.registerLoader(GameAssetType.Image, new ImageRGBADataLoader());
212
+ }
213
+
214
+ this.#extension = this.#graphics.add_extension(new DecalExtension(this));
215
+ }
216
+
217
+ async shutdown(entityManager) {
218
+ if (this.#extension !== null) {
219
+ this.#graphics.remove_extension(this.#extension);
220
+
221
+ this.#extension = null;
222
+ }
223
+
224
+ for (let i = 0; i < this.#entries.length; i++) {
225
+ this.#release(this.#entries[i]);
226
+ }
227
+
228
+ this.#renderer.destroy();
229
+
230
+ this.#entries.length = 0;
231
+ this.#by_entity.clear();
232
+ this.#record_count = 0;
233
+ }
234
+
235
+ /**
236
+ * The `transform` IS the projection volume, and it projects along its local **+Z** — so the
237
+ * surface being painted faces the other way. Get the sign backwards and every pixel of the
238
+ * decal is rejected by the coverage fade, silently, with every CPU-side observable here
239
+ * reporting success.
240
+ *
241
+ * @see Decal
242
+ * @see chunk_decal_surface_frame
243
+ *
244
+ * @param {Decal} decal
245
+ * @param {Transform64} transform
246
+ * @param {number} entity
247
+ */
248
+ link(decal, transform, entity) {
249
+ const entry = {
250
+ entity,
251
+ decal,
252
+ transform,
253
+ /**
254
+ * Atlas patches, acquired as the textures arrive. A decal whose albedo has not loaded
255
+ * yet is not packed at all — there is nothing to draw with, and drawing it with the
256
+ * identity patch would put a white square on the ground until the image landed.
257
+ */
258
+ patches: { albedo: null, normal: null, orm: null }
259
+ };
260
+
261
+ this.#entries.push(entry);
262
+ this.#by_entity.set(entity, entry);
263
+
264
+ this.#acquire(entry);
265
+ }
266
+
267
+ /**
268
+ * @param {Decal} decal
269
+ * @param {Transform64} transform
270
+ * @param {number} entity
271
+ */
272
+ unlink(decal, transform, entity) {
273
+ const entry = this.#by_entity.get(entity);
274
+
275
+ if (entry === undefined) {
276
+ return;
277
+ }
278
+
279
+ this.#release(entry);
280
+
281
+ const index = this.#entries.indexOf(entry);
282
+
283
+ if (index !== -1) {
284
+ this.#entries.splice(index, 1);
285
+ }
286
+
287
+ this.#by_entity.delete(entity);
288
+ }
289
+
290
+ /**
291
+ * @param {object} entry
292
+ */
293
+ #release(entry) {
294
+ const patches = entry.patches;
295
+
296
+ if (patches.albedo !== null) {
297
+ patches.albedo.release();
298
+ patches.albedo = null;
299
+ }
300
+
301
+ if (patches.normal !== null) {
302
+ patches.normal.release();
303
+ patches.normal = null;
304
+ }
305
+
306
+ if (patches.orm !== null) {
307
+ patches.orm.release();
308
+ patches.orm = null;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Fetch this decal's textures and take a reference on the atlas patch each lands in.
314
+ *
315
+ * Reference counted and graveyard cached through {@link ManagedAtlas}, because combat spawns a
316
+ * footprint decal per step and every one of them names the same file: packing or erasing a patch
317
+ * per footprint would repack three atlases and regenerate their mip chains several times a second.
318
+ *
319
+ * @param {object} entry
320
+ */
321
+ #acquire(entry) {
322
+ const decal = entry.decal;
323
+
324
+ const albedo_uri = decal.albedo_uri;
325
+
326
+ if (albedo_uri === "") {
327
+ return;
328
+ }
329
+
330
+ entry.patches.albedo = this.#managed_albedo.acquire(albedo_uri);
331
+
332
+ entry.patches.normal = decal.uri_normal === ""
333
+ ? this.#managed_normal.acquire_sampler(this.#identity_normal)
334
+ : this.#managed_normal.acquire(decal.uri_normal);
335
+
336
+ entry.patches.orm = (decal.uri_orm === "" && decal.uri_emissive === "")
337
+ ? this.#managed_orm.acquire_sampler(this.#identity_orm)
338
+ : this.#managed_orm.acquire(`${decal.uri_orm}\0${decal.uri_emissive}`);
339
+ }
340
+
341
+ update(timeDelta) {
342
+ const entries = this.#entries;
343
+
344
+ sort_decals_by_priority(entries);
345
+
346
+ this.#managed_albedo.update();
347
+ this.#managed_normal.update();
348
+ this.#managed_orm.update();
349
+
350
+ const required = entries.length * DECAL_RECORD_WORD_COUNT;
351
+
352
+ if (this.#records.length < required) {
353
+ this.#records = new Float32Array(required);
354
+ }
355
+
356
+ const records = this.#records;
357
+
358
+ let count = 0;
359
+
360
+ for (let i = 0; i < entries.length; i++) {
361
+ const entry = entries[i];
362
+ const patches = entry.patches;
363
+
364
+ const albedo = patches.albedo?.getValue() ?? null;
365
+ const normal = patches.normal?.getValue() ?? null;
366
+ const orm = patches.orm?.getValue() ?? null;
367
+
368
+ if (albedo === null || normal === null || orm === null) {
369
+ // still waiting on an image; a decal is packed once it can be drawn properly
370
+ continue;
371
+ }
372
+
373
+ write_decal_record(
374
+ records,
375
+ count * DECAL_RECORD_WORD_COUNT,
376
+ entry.decal,
377
+ entry.transform.matrix,
378
+ { albedo: albedo.uv, normal: normal.uv, orm: orm.uv },
379
+ {
380
+ albedo: sprite_atlas_max_lod(albedo.padding),
381
+ normal: sprite_atlas_max_lod(normal.padding),
382
+ orm: sprite_atlas_max_lod(orm.padding)
383
+ }
384
+ );
385
+
386
+ count++;
387
+ }
388
+
389
+ this.#record_count = count;
390
+ }
391
+
392
+ /**
393
+ * @param {FrameContext} frame
394
+ */
395
+ record(frame) {
396
+ if (this.#record_count === 0) {
397
+ /*
398
+ Nothing to paint, so the frame keeps the G-buffer it already had. Skipping is what makes
399
+ the replacement design free when no decal is on screen — the pass reads and rewrites
400
+ every pixel when it runs.
401
+ */
402
+ return;
403
+ }
404
+
405
+ this.#renderer.graph_draw({
406
+ frame,
407
+ records: this.#records,
408
+ record_count: this.#record_count,
409
+ atlas_albedo: this.#atlas_albedo.sampler,
410
+ atlas_normal: this.#atlas_normal.sampler,
411
+ atlas_orm: this.#atlas_orm.sampler
412
+ });
413
+ }
414
+ }
415
+
416
+ /**
417
+ * **Late on the phase**, because a decal composites over surfaces that are already written rather
418
+ * than preceding them — anything another extension writes into the G-buffer at this phase. That
419
+ * requirement used to be met by putting a registration call above an `await` and writing a comment
420
+ * about it; here it is the declaration below. The textured ground no longer depends on it: terrain is
421
+ * shaded in the G-buffer pass itself, before any extension runs.
422
+ */
423
+ export class DecalExtension extends RenderExtension {
424
+ name = "decals";
425
+
426
+ phase = FramePhase.AfterGBuffer;
427
+
428
+ relative_order = 1;
429
+
430
+ /**
431
+ * @type {DecalSystem}
432
+ */
433
+ #system;
434
+
435
+ /**
436
+ * @param {DecalSystem} system
437
+ */
438
+ constructor(system) {
439
+ super();
440
+
441
+ this.#system = system;
442
+ }
443
+
444
+ record(frame) {
445
+ this.#system.record(frame);
446
+ }
447
+ }