@woosh/meep-engine 3.61.3 → 3.62.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 (266) hide show
  1. package/package.json +1 -1
  2. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
  3. package/src/engine/graphics3/PickingSystem.js +415 -413
  4. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  5. package/src/engine/graphics3/decal/GPUDecalRenderer.js +2 -1
  6. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  7. package/src/engine/graphics3/decal/shader_decal_composite.js +26 -16
  8. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -1
  9. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +6 -11
  10. package/src/engine/graphics3/highlight/GPUHighlightRenderer.d.ts.map +1 -1
  11. package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +6 -2
  12. package/src/engine/graphics3/highlight/shader_highlight_outline.d.ts.map +1 -1
  13. package/src/engine/graphics3/highlight/shader_highlight_outline.js +167 -150
  14. package/src/engine/graphics3/shader_picking_gather.d.ts.map +1 -1
  15. package/src/engine/graphics3/shader_picking_gather.js +133 -120
  16. package/src/shade/device/mock/SoftwareGPUTexture.d.ts.map +1 -1
  17. package/src/shade/device/mock/SoftwareGPUTexture.js +11 -0
  18. package/src/shade/device/pipeline/rp_validate_and_rewrite_for_color_attachments.js +1 -1
  19. package/src/shade/playground/contact_shadows/ContactShadowLab.js +6 -6
  20. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.d.ts.map +1 -1
  21. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.js +6 -4
  22. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.d.ts.map +1 -1
  23. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.js +6 -4
  24. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.d.ts.map +1 -1
  25. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.js +6 -4
  26. package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.js +1 -1
  27. package/src/shade/playground/ssr_variance/capture.js +1 -1
  28. package/src/shade/renderer/Renderer.d.ts +2 -2
  29. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  30. package/src/shade/renderer/Renderer.js +40 -53
  31. package/src/shade/renderer/deferred/chunk_read_gBuffer_material.d.ts.map +1 -1
  32. package/src/shade/renderer/deferred/chunk_read_gBuffer_material.js +3 -1
  33. package/src/shade/renderer/deferred/chunk_ss_trace_contact_shadow_soft.d.ts.map +1 -1
  34. package/src/shade/renderer/deferred/chunk_ss_trace_contact_shadow_soft.js +5 -4
  35. package/src/shade/renderer/deferred/shader_deferred_main.d.ts.map +1 -1
  36. package/src/shade/renderer/deferred/shader_deferred_main.js +6 -4
  37. package/src/shade/renderer/extension/GBufferTextures.d.ts +5 -1
  38. package/src/shade/renderer/extension/GBufferTextures.d.ts.map +1 -1
  39. package/src/shade/renderer/extension/GBufferTextures.js +5 -1
  40. package/src/shade/renderer/extension/ViewTextures.d.ts +11 -7
  41. package/src/shade/renderer/extension/ViewTextures.d.ts.map +1 -1
  42. package/src/shade/renderer/extension/ViewTextures.js +17 -13
  43. package/src/shade/renderer/gbuffer/G_BUFFER_ALBEDO_FORMAT.d.ts +2 -0
  44. package/src/shade/renderer/gbuffer/G_BUFFER_ALBEDO_FORMAT.d.ts.map +1 -1
  45. package/src/shade/renderer/gbuffer/G_BUFFER_ALBEDO_FORMAT.js +2 -0
  46. package/src/shade/renderer/gbuffer/G_BUFFER_COLOR_TARGETS.d.ts.map +1 -1
  47. package/src/shade/renderer/gbuffer/G_BUFFER_COLOR_TARGETS.js +8 -5
  48. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.d.ts +7 -5
  49. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.d.ts.map +1 -1
  50. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.js +29 -16
  51. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_instance.d.ts +9 -3
  52. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_instance.d.ts.map +1 -1
  53. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_instance.js +40 -32
  54. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
  55. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +14 -15
  56. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_debug_draw.d.ts.map +1 -1
  57. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_debug_draw.js +7 -3
  58. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_diffuse.js +1 -1
  59. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_indirect_lighting.d.ts.map +1 -1
  60. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_indirect_lighting.js +5 -3
  61. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.d.ts.map +1 -1
  62. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.js +1 -3
  63. package/src/shade/renderer/global_illumination/ibl/shader_diffuse_IBL.js +1 -1
  64. package/src/shade/renderer/global_illumination/ibl/shader_specular_IBL.js +2 -2
  65. package/src/shade/renderer/global_illumination/lpv/shader_diffuse_LPV.js +1 -1
  66. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
  67. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +14 -1
  68. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  69. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +10 -1
  70. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_diffuse.js +2 -2
  71. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.d.ts.map +1 -1
  72. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.js +5 -3
  73. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +1 -1
  74. package/src/shade/renderer/global_illumination/shader_resolve_indirect_lighting.d.ts.map +1 -1
  75. package/src/shade/renderer/global_illumination/shader_resolve_indirect_lighting.js +5 -3
  76. package/src/shade/renderer/global_illumination/sharc/shader/debug/shader_colored_hash.js +1 -1
  77. package/src/shade/renderer/global_illumination/sharc/shader/debug/shader_sharc_debug_radiance.js +1 -1
  78. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_update.js +1 -1
  79. package/src/shade/renderer/material/GPUMaterialManager.d.ts +22 -9
  80. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  81. package/src/shade/renderer/material/GPUMaterialManager.js +159 -65
  82. package/src/shade/renderer/material/MATERIAL_MASK.d.ts +19 -0
  83. package/src/shade/renderer/material/MATERIAL_MASK.d.ts.map +1 -0
  84. package/src/shade/renderer/material/MATERIAL_MASK.js +19 -0
  85. package/src/shade/renderer/material/dispatch/GPUMaterialComputeDispatch.d.ts +8 -8
  86. package/src/shade/renderer/material/dispatch/GPUMaterialComputeDispatch.d.ts.map +1 -1
  87. package/src/shade/renderer/material/dispatch/GPUMaterialComputeDispatch.js +11 -92
  88. package/src/shade/renderer/material/dispatch/chunk_material_quad_keys.d.ts.map +1 -1
  89. package/src/shade/renderer/material/dispatch/chunk_material_quad_keys.js +10 -2
  90. package/src/shade/renderer/material/dispatch/gbuffer_compute.js +2 -2
  91. package/src/shade/renderer/material/dispatch/graph_material_dispatch_compute.d.ts +7 -7
  92. package/src/shade/renderer/material/dispatch/graph_material_dispatch_compute.d.ts.map +1 -1
  93. package/src/shade/renderer/material/dispatch/graph_material_dispatch_compute.js +20 -16
  94. package/src/shade/renderer/material/dispatch/shader_material_pixel_count.d.ts.map +1 -1
  95. package/src/shade/renderer/material/dispatch/shader_material_pixel_count.js +3 -1
  96. package/src/shade/renderer/material/dispatch/shader_material_pixel_scatter.d.ts.map +1 -1
  97. package/src/shade/renderer/material/dispatch/shader_material_pixel_scatter.js +3 -1
  98. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  99. package/src/shade/renderer/material/standard/fragment_gbuffer.js +26 -25
  100. package/src/shade/renderer/material/standard/material_gbuffer_vertex.d.ts +10 -8
  101. package/src/shade/renderer/material/standard/material_gbuffer_vertex.d.ts.map +1 -1
  102. package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +16 -13
  103. package/src/shade/renderer/material/template/ShadeMaterialTemplate.d.ts +1 -1
  104. package/src/shade/renderer/material/template/ShadeMaterialTemplate.js +1 -1
  105. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.d.ts +5 -2
  106. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.d.ts.map +1 -1
  107. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.js +7 -4
  108. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts +3 -3
  109. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts.map +1 -1
  110. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.js +12 -10
  111. package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.js +2 -2
  112. package/src/shade/renderer/postprocess/denoise/shader_temporal_reprojection_luma.d.ts.map +1 -1
  113. package/src/shade/renderer/postprocess/denoise/shader_temporal_reprojection_luma.js +6 -4
  114. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +9 -7
  115. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  116. package/src/shade/renderer/postprocess/gtao/GTAO.js +11 -8
  117. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +3 -3
  118. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  119. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +17 -12
  120. package/src/shade/renderer/postprocess/gtao/chunk_integrate_lighting_sample.js +1 -1
  121. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +1 -1
  122. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +4 -4
  123. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +2 -2
  124. package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.js +41 -41
  125. package/src/shade/renderer/postprocess/ssr/prefilter/chunk_load_neighbourhood_sample.js +1 -1
  126. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +3 -3
  127. package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_add_hit_irradiance.d.ts.map +1 -1
  128. package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_add_hit_irradiance.js +5 -2
  129. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.js +4 -4
  130. package/src/shade/renderer/postprocess/ssr/ssr_combine_shader.d.ts.map +1 -1
  131. package/src/shade/renderer/postprocess/ssr/ssr_combine_shader.js +4 -2
  132. package/src/shade/renderer/postprocess/ssr/ssr_graph_prefilter_color.d.ts +2 -1
  133. package/src/shade/renderer/postprocess/ssr/ssr_graph_prefilter_color.d.ts.map +1 -1
  134. package/src/shade/renderer/postprocess/ssr/ssr_graph_prefilter_color.js +10 -2
  135. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +1 -1
  136. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +4 -4
  137. package/src/shade/renderer/postprocess/taa/shader_reprojection_map.d.ts.map +1 -1
  138. package/src/shade/renderer/postprocess/taa/shader_reprojection_map.js +6 -4
  139. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts +6 -8
  140. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
  141. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +29 -33
  142. package/src/shade/renderer/rasterize/TemplatePassDescriptor.d.ts.map +1 -1
  143. package/src/shade/renderer/rasterize/TemplatePassDescriptor.js +1 -0
  144. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.d.ts +6 -3
  145. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.d.ts.map +1 -1
  146. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.js +8 -3
  147. package/src/shade/renderer/rasterize/expand/GPUCollectionLimits.d.ts.map +1 -1
  148. package/src/shade/renderer/rasterize/expand/GPUCollectionLimits.js +3 -0
  149. package/src/shade/renderer/rasterize/expand/RasterizationCollectionType.d.ts +1 -0
  150. package/src/shade/renderer/rasterize/expand/RasterizationCollectionType.js +5 -0
  151. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts +8 -3
  152. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  153. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +54 -5
  154. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +1 -0
  155. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  156. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +1 -1
  157. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +1 -1
  158. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  159. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +2 -1
  160. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.d.ts +7 -2
  161. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.d.ts.map +1 -1
  162. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +73 -9
  163. package/src/shade/renderer/rasterize/native/viz/shader_vertex.js +4 -4
  164. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  165. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +23 -40
  166. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_opaque_pass_descriptor.d.ts.map +1 -1
  167. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_opaque_pass_descriptor.js +55 -76
  168. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +4 -2
  169. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  170. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +6 -2
  171. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts +4 -2
  172. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  173. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +6 -2
  174. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts +5 -2
  175. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  176. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +43 -2
  177. package/src/shade/renderer/rasterize/visibility/VISIBILITY_BUFFER.d.ts +39 -0
  178. package/src/shade/renderer/rasterize/visibility/VISIBILITY_BUFFER.d.ts.map +1 -0
  179. package/src/shade/renderer/rasterize/visibility/VISIBILITY_BUFFER.js +45 -0
  180. package/src/shade/renderer/rasterize/visibility/chunk_visibility_decode.d.ts +26 -0
  181. package/src/shade/renderer/rasterize/visibility/chunk_visibility_decode.d.ts.map +1 -0
  182. package/src/shade/renderer/rasterize/visibility/chunk_visibility_decode.js +48 -0
  183. package/src/shade/renderer/rasterize/visibility/shader_visible_meshlets_append.d.ts +40 -0
  184. package/src/shade/renderer/rasterize/visibility/shader_visible_meshlets_append.d.ts.map +1 -0
  185. package/src/shade/renderer/rasterize/visibility/shader_visible_meshlets_append.js +172 -0
  186. package/src/shade/renderer/restir/di/chunk_restir_reconstruct_surface.d.ts.map +1 -1
  187. package/src/shade/renderer/restir/di/chunk_restir_reconstruct_surface.js +6 -4
  188. package/src/shade/renderer/restir/di/chunk_restir_spatial_reuse.js +1 -1
  189. package/src/shade/renderer/restir/di/shader_restir_di_denoise.js +2 -2
  190. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferEOTF.d.ts +9 -0
  191. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferEOTF.d.ts.map +1 -0
  192. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferEOTF.js +18 -0
  193. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_determinant.d.ts +20 -0
  194. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_determinant.d.ts.map +1 -0
  195. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_determinant.js +26 -0
  196. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts +60 -18
  197. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts.map +1 -1
  198. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.js +163 -45
  199. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -1
  200. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +19 -18
  201. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_face_normal.d.ts +13 -0
  202. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_face_normal.d.ts.map +1 -0
  203. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_face_normal.js +22 -0
  204. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_normal.d.ts +8 -1
  205. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_normal.d.ts.map +1 -1
  206. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_normal.js +10 -8
  207. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_g_buffer_face_normal.d.ts +22 -0
  208. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_g_buffer_face_normal.d.ts.map +1 -0
  209. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_g_buffer_face_normal.js +32 -0
  210. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_g_buffer_normal.d.ts +11 -1
  211. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_g_buffer_normal.d.ts.map +1 -1
  212. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_g_buffer_normal.js +14 -6
  213. package/src/shade/renderer/shader/chunk/material/chunk_decode_g_buffer_albedo.d.ts +14 -0
  214. package/src/shade/renderer/shader/chunk/material/chunk_decode_g_buffer_albedo.d.ts.map +1 -0
  215. package/src/shade/renderer/shader/chunk/material/chunk_decode_g_buffer_albedo.js +24 -0
  216. package/src/shade/renderer/shader/chunk/material/chunk_encode_g_buffer_albedo.d.ts +17 -0
  217. package/src/shade/renderer/shader/chunk/material/chunk_encode_g_buffer_albedo.d.ts.map +1 -0
  218. package/src/shade/renderer/shader/chunk/material/chunk_encode_g_buffer_albedo.js +27 -0
  219. package/src/shade/renderer/shader/chunk/screen_space/SCREEN_SPACE_RESOURCES.d.ts +7 -1
  220. package/src/shade/renderer/shader/chunk/screen_space/SCREEN_SPACE_RESOURCES.d.ts.map +1 -1
  221. package/src/shade/renderer/shader/chunk/screen_space/SCREEN_SPACE_RESOURCES.js +8 -1
  222. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_load_world_space_normal.js +1 -1
  223. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.js +10 -10
  224. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts +8 -0
  225. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts.map +1 -1
  226. package/src/shade/renderer/shader/pass/RenderPassDescriptor.js +9 -0
  227. package/src/shade/renderer/shader/util/shader_decode_octahedral_normal.js +1 -1
  228. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.d.ts +3 -3
  229. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.d.ts.map +1 -1
  230. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.js +3 -3
  231. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.d.ts.map +1 -1
  232. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.js +6 -4
  233. package/src/shade/renderer/shadow/ray/RTXShadows.d.ts +6 -4
  234. package/src/shade/renderer/shadow/ray/RTXShadows.d.ts.map +1 -1
  235. package/src/shade/renderer/shadow/ray/RTXShadows.js +16 -5
  236. package/src/shade/renderer/shadow/ray/shader_ray_shadow_code.d.ts.map +1 -1
  237. package/src/shade/renderer/shadow/ray/shader_ray_shadow_code.js +7 -7
  238. package/src/shade/renderer/shadow/ray/shader_shadow_upscale.js +2 -2
  239. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.d.ts +4 -2
  240. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.d.ts.map +1 -1
  241. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.js +79 -74
  242. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.d.ts.map +1 -1
  243. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.js +7 -7
  244. package/src/shade/renderer/shadow/sdf/cascade/shader_scene_sdf_soft_shadows.d.ts.map +1 -1
  245. package/src/shade/renderer/shadow/sdf/cascade/shader_scene_sdf_soft_shadows.js +7 -3
  246. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.d.ts +7 -5
  247. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.d.ts.map +1 -1
  248. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.js +29 -16
  249. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts +5 -5
  250. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts.map +1 -1
  251. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +8 -8
  252. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.d.ts.map +1 -1
  253. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +13 -14
  254. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +2 -1
  255. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  256. package/src/shade/renderer/water/GPUWaterRenderer.js +6 -4
  257. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
  258. package/src/shade/renderer/water/chunk_water_transport.js +11 -9
  259. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  260. package/src/shade/renderer/water/shader_water.js +13 -11
  261. package/src/shade/renderer/gbuffer/G_BUFFER_MIP_LEVELS.d.ts +0 -7
  262. package/src/shade/renderer/gbuffer/G_BUFFER_MIP_LEVELS.d.ts.map +0 -1
  263. package/src/shade/renderer/gbuffer/G_BUFFER_MIP_LEVELS.js +0 -6
  264. package/src/shade/renderer/postprocess/ssr/shader_downsample_encoded_normal.d.ts +0 -3
  265. package/src/shade/renderer/postprocess/ssr/shader_downsample_encoded_normal.d.ts.map +0 -1
  266. package/src/shade/renderer/postprocess/ssr/shader_downsample_encoded_normal.js +0 -104
@@ -1,413 +1,415 @@
1
- import { assert } from "../../core/assert.js";
2
- import { m4_invert } from "../../core/geom/3d/mat4/m4_invert.js";
3
- import { BufferResourceDescriptor } from "../../shade/device/graph/BufferResourceDescriptor.js";
4
- import { graph_read_buffer } from "../../shade/renderer/buffer/graph_read_buffer.js";
5
- import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
6
- import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
7
- import { ViewTextures } from "../../shade/renderer/extension/ViewTextures.js";
8
- import { VIZ_BUFFER_INVALID_MESH_VALUE } from "../../shade/renderer/rasterize/native/viz/VIZ_BUFFER_INVALID_MESH_VALUE.js";
9
- import { entity_of_row } from "../../shade/renderer/scene/rows/GPUSceneRows.js";
10
- import { ParentEntity } from "../ecs/parent/ParentEntity.js";
11
- import { System } from "../ecs/System.js";
12
- import { Pickable } from "./Pickable.js";
13
- import { PickingResult } from "./PickingResult.js";
14
- import {
15
- PICKING_GATHER_STRIDE_WORDS,
16
- PICKING_QUERY_OFF_VIEW,
17
- shader_picking_gather
18
- } from "./shader_picking_gather.js";
19
-
20
- /**
21
- * Bytes a query occupies in the gather buffer: the mesh row and the depth, one word each.
22
- *
23
- * @type {number}
24
- */
25
- const QUERY_STRIDE = PICKING_GATHER_STRIDE_WORDS * 4;
26
-
27
- /**
28
- * Answers "what is under this pixel" for entities that asked to be pickable.
29
- *
30
- * **Off the visibility buffer.** The rasterizer writes every pixel's `meshes` row — an entity's
31
- * row, one on — and its depth; a pick reads those two texels out of the frame that drew them and
32
- * resolves the row to the entity that drew it, then up its parents to the one that opted in. What
33
- * was drawn is what is picked: at triangle precision, under skinning, with nothing kept on the CPU
34
- * for it — no bounds, no tree, no per-frame refresh.
35
- *
36
- * **Asynchronous and batched, of necessity.** The texels are gathered by the frame after the query
37
- * was asked and read back when the device has finished it, so an answer is a frame or two late and
38
- * arrives for the whole batch at once. A result is a promise for that reason, and nothing this
39
- * tick can come to depend on it.
40
- *
41
- * Terrain picking, which is where the game's precision matters, does not come through here at all:
42
- * it is an exact raycast against the terrain's own representation and is untouched by any of this.
43
- */
44
- export class PickingSystem extends System {
45
- /**
46
- * @type {GraphicsEngine}
47
- */
48
- #graphics;
49
-
50
- /**
51
- * @type {PickingExtension|null}
52
- */
53
- #extension = null;
54
-
55
- /**
56
- * Entities that have opted in.
57
- *
58
- * @type {Set<number>}
59
- */
60
- #pickable = new Set();
61
-
62
- /**
63
- * Queries asked since the last frame took a batch, with the promise each batch answers.
64
- *
65
- * @type {{queries: PickingQuery[], resolve: function(PickingResult[]): void, reject: function(Error): void}[]}
66
- */
67
- #waiting = [];
68
-
69
- /**
70
- * @param {GraphicsEngine} graphics whose frames draw the view the picks read
71
- */
72
- constructor(graphics) {
73
- super();
74
-
75
- assert.defined(graphics, 'graphics');
76
-
77
- this.#graphics = graphics;
78
-
79
- this.dependencies = [Pickable];
80
- }
81
-
82
- async startup(entityManager) {
83
- this.entityManager = entityManager;
84
-
85
- this.#extension = this.#graphics.add_extension(new PickingExtension(this));
86
- }
87
-
88
- async shutdown(entityManager) {
89
- if (this.#extension !== null) {
90
- this.#graphics.remove_extension(this.#extension);
91
- this.#extension = null;
92
- }
93
-
94
- this.#pickable.clear();
95
-
96
- // nothing will draw a frame for them
97
- for (const batch of this.#waiting.splice(0)) {
98
- batch.reject(new Error('the picking system shut down before a frame answered'));
99
- }
100
- }
101
-
102
- /**
103
- * @param {Pickable} pickable
104
- * @param {number} entity
105
- */
106
- link(pickable, entity) {
107
- this.#pickable.add(entity);
108
- }
109
-
110
- /**
111
- * @param {Pickable} pickable
112
- * @param {number} entity
113
- */
114
- unlink(pickable, entity) {
115
- this.#pickable.delete(entity);
116
- }
117
-
118
- /**
119
- * How many entities have opted in.
120
- *
121
- * @returns {number}
122
- */
123
- get pickable_count() {
124
- return this.#pickable.size;
125
- }
126
-
127
- /**
128
- * Batches asked and not yet answered by a frame.
129
- *
130
- * @returns {number}
131
- */
132
- get waiting_count() {
133
- return this.#waiting.length;
134
- }
135
-
136
- /**
137
- * What is drawn under each of these pixels.
138
- *
139
- * One result per query, in the order the queries were given. A pixel over nothing pickable
140
- * still gets a result — `entity` is {@link PickingResult.NOTHING} — so the two arrays line up
141
- * by position and a caller never has to search for the answer to its own question.
142
- *
143
- * Answered by the next frame that draws the view, when the device has finished it.
144
- *
145
- * @param {PickingQuery[]} queries
146
- * @returns {Promise<PickingResult[]>}
147
- */
148
- pick(queries) {
149
- assert.isArray(queries, 'queries');
150
-
151
- if (queries.length === 0) {
152
- return Promise.resolve([]);
153
- }
154
-
155
- return new Promise((resolve, reject) => {
156
- this.#waiting.push({ queries: queries.slice(), resolve, reject });
157
- });
158
- }
159
-
160
- /**
161
- * Take everything asked since the last frame, to be answered by this one.
162
- *
163
- * @returns {{queries: PickingQuery[], resolve: function(PickingResult[]): void, reject: function(Error): void}[]}
164
- */
165
- take_waiting() {
166
- return this.#waiting.splice(0);
167
- }
168
-
169
- /**
170
- * The entity a drawn mesh row picks: the one that drew it, or the nearest of its ancestors,
171
- * that opted in.
172
- *
173
- * @param {number} row a `meshes` row, as the visibility buffer holds it
174
- * @returns {number} the entity, or {@link PickingResult.NOTHING}
175
- */
176
- resolve_row(row) {
177
- if (row >= VIZ_BUFFER_INVALID_MESH_VALUE) {
178
- return PickingResult.NOTHING;
179
- }
180
-
181
- const dataset = this.entityManager.dataset;
182
- const pickable = this.#pickable;
183
-
184
- let entity = entity_of_row(row);
185
-
186
- while (entity >= 0 && dataset.entityExists(entity)) {
187
- if (pickable.has(entity)) {
188
- return entity;
189
- }
190
-
191
- const parent = dataset.getComponent(entity, ParentEntity);
192
-
193
- entity = parent === undefined || parent === null ? -1 : parent.entity;
194
- }
195
-
196
- return PickingResult.NOTHING;
197
- }
198
- }
199
-
200
- /**
201
- * Gathers the queried texels out of the frame that drew them, after the G-buffer is written and
202
- * before anything overwrites the visibility buffer, and answers the batch when they land.
203
- */
204
- export class PickingExtension extends RenderExtension {
205
- name = "picking";
206
- phase = FramePhase.AfterGBuffer;
207
-
208
- /**
209
- * @type {PickingSystem}
210
- */
211
- #system;
212
-
213
- /**
214
- * @param {PickingSystem} system
215
- */
216
- constructor(system) {
217
- super();
218
-
219
- this.#system = system;
220
- }
221
-
222
- /**
223
- * @param {import("../../shade/renderer/extension/FrameContext.js").FrameContext} frame
224
- */
225
- record(frame) {
226
- const batches = this.#system.take_waiting();
227
-
228
- if (batches.length === 0) {
229
- return;
230
- }
231
-
232
- // the view's own resolution: what the visibility buffer is sized to, whatever the frame's
233
- // target at this phase is
234
- const resolution = frame.view.resolution;
235
- const width = resolution[0];
236
- const height = resolution[1];
237
-
238
- // what the frame projected with, kept for when the answer comes: the frame's camera has
239
- // moved on by then
240
- const view_projection = new Float32Array(frame.view.camera.view_projection_matrix);
241
- const camera = frame.view.camera.camera.transform;
242
- const eye = [camera.translation_x, camera.translation_y, camera.translation_z];
243
-
244
- /** @type {PickingQuery[]} every query of every batch, in order */
245
- const queries = [];
246
-
247
- for (const batch of batches) {
248
- for (const query of batch.queries) {
249
- queries.push(query);
250
- }
251
- }
252
-
253
- /*
254
- The pixel each query names, resolved here rather than in the shader: the fraction is the
255
- caller's unit and the resolution is the frame's, and neither is the gather's business. A
256
- query that is not on the view at all carries `PICKING_QUERY_OFF_VIEW` instead of a pixel.
257
- */
258
- const pixels = new Uint32Array(queries.length * PICKING_GATHER_STRIDE_WORDS);
259
-
260
- for (let i = 0; i < queries.length; i++) {
261
- const query = queries[i];
262
- const base = i * PICKING_GATHER_STRIDE_WORDS;
263
-
264
- if (on_view(query)) {
265
- pixels[base] = texel(query.u, width);
266
- pixels[base + 1] = texel(query.v, height);
267
- } else {
268
- pixels[base] = PICKING_QUERY_OFF_VIEW;
269
- }
270
- }
271
-
272
- const textures = frame.get(ViewTextures);
273
- const graph = frame.graph;
274
-
275
- const data = {};
276
- const pass = graph.add("Picking / gather texels", data, (data, resources, context) => {
277
- const encoder = context.encoder;
278
-
279
- shader_picking_gather.dispatchThreads({
280
- encoder,
281
- bindings: {
282
- query_count: queries.length,
283
- viz_mesh: resources.get(data.mesh),
284
- viz_depth: resources.get(data.depth),
285
- queries: encoder.allocateTransientBufferAndLoad(
286
- pixels.buffer, GPUBufferUsage.STORAGE
287
- ),
288
- results: resources.get(data.results),
289
- },
290
- thread_count_x: queries.length,
291
- });
292
- });
293
-
294
- data.mesh = pass.read(textures.visibility_mesh);
295
- data.depth = pass.read(textures.depth);
296
- data.results = pass.create("picking/results", BufferResourceDescriptor.from(
297
- queries.length * QUERY_STRIDE,
298
- GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
299
- ));
300
-
301
- const system = this.#system;
302
-
303
- graph_read_buffer({ graph, buffer: data.results })
304
- .then(ref => {
305
- try {
306
- const words = new Uint32Array(ref.getValue());
307
- const floats = new Float32Array(words.buffer);
308
-
309
- let index = 0;
310
-
311
- for (const batch of batches) {
312
- const results = [];
313
-
314
- for (let i = 0; i < batch.queries.length; i++, index++) {
315
- const query = batch.queries[i];
316
- const result = new PickingResult();
317
-
318
- results.push(result);
319
-
320
- if (!on_view(query)) {
321
- // the gather writes an invalid row here too; this is the same
322
- // answer without depending on a dispatch having run
323
- continue;
324
- }
325
-
326
- const base = index * PICKING_GATHER_STRIDE_WORDS;
327
- const entity = system.resolve_row(words[base]);
328
-
329
- if (entity === PickingResult.NOTHING) {
330
- continue;
331
- }
332
-
333
- result.entity = entity;
334
-
335
- unproject(
336
- result.position, view_projection,
337
- texel(query.u, width), texel(query.v, height), width, height,
338
- floats[base + 1]
339
- );
340
-
341
- result.distance = Math.hypot(
342
- result.position.x - eye[0],
343
- result.position.y - eye[1],
344
- result.position.z - eye[2]
345
- );
346
- }
347
-
348
- batch.resolve(results);
349
- }
350
- } finally {
351
- ref.release();
352
- }
353
- })
354
- .catch(error => {
355
- for (const batch of batches) {
356
- batch.reject(error instanceof Error ? error : new Error('the picking readback failed'));
357
- }
358
- });
359
- }
360
- }
361
-
362
- /**
363
- * @param {PickingQuery} query
364
- * @returns {boolean} whether the point is on the view at all
365
- */
366
- function on_view(query) {
367
- return query.u >= 0 && query.u < 1 && query.v >= 0 && query.v < 1;
368
- }
369
-
370
- /**
371
- * The texel a fraction of the view falls in.
372
- *
373
- * @param {number} fraction in [0, 1)
374
- * @param {number} extent texels
375
- * @returns {number}
376
- */
377
- function texel(fraction, extent) {
378
- return Math.min(extent - 1, Math.floor(fraction * extent));
379
- }
380
-
381
- const SCRATCH_INVERSE = new Float32Array(16);
382
-
383
- /**
384
- * The world point a pixel's depth stands for, through the inverse of what the frame projected
385
- * with. Depth is what the buffer holds — reverse-Z, `1` at the near plane — and WebGPU's clip z
386
- * is that same `[0, 1]`, so it goes straight in.
387
- *
388
- * @param {Vector3} target
389
- * @param {Float32Array} view_projection
390
- * @param {number} x pixel
391
- * @param {number} y pixel
392
- * @param {number} width
393
- * @param {number} height
394
- * @param {number} depth
395
- */
396
- function unproject(target, view_projection, x, y, width, height, depth) {
397
- const m = SCRATCH_INVERSE;
398
-
399
- m4_invert(m, view_projection);
400
-
401
- // the pixel's centre, in NDC: x to the right, y up
402
- const nx = ((x + 0.5) / width) * 2 - 1;
403
- const ny = 1 - ((y + 0.5) / height) * 2;
404
- const nz = depth;
405
-
406
- // column-major: column c is m[c * 4 .. c * 4 + 3]
407
- const wx = m[0] * nx + m[4] * ny + m[8] * nz + m[12];
408
- const wy = m[1] * nx + m[5] * ny + m[9] * nz + m[13];
409
- const wz = m[2] * nx + m[6] * ny + m[10] * nz + m[14];
410
- const ww = m[3] * nx + m[7] * ny + m[11] * nz + m[15];
411
-
412
- target.set(wx / ww, wy / ww, wz / ww);
413
- }
1
+ import { assert } from "../../core/assert.js";
2
+ import { m4_invert } from "../../core/geom/3d/mat4/m4_invert.js";
3
+ import { BufferResourceDescriptor } from "../../shade/device/graph/BufferResourceDescriptor.js";
4
+ import { graph_read_buffer } from "../../shade/renderer/buffer/graph_read_buffer.js";
5
+ import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
6
+ import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
7
+ import { ViewTextures } from "../../shade/renderer/extension/ViewTextures.js";
8
+ import { VIZ_BUFFER_INVALID_MESH_VALUE } from "../../shade/renderer/rasterize/native/viz/VIZ_BUFFER_INVALID_MESH_VALUE.js";
9
+ import { entity_of_row } from "../../shade/renderer/scene/rows/GPUSceneRows.js";
10
+ import { ParentEntity } from "../ecs/parent/ParentEntity.js";
11
+ import { System } from "../ecs/System.js";
12
+ import { Pickable } from "./Pickable.js";
13
+ import { PickingResult } from "./PickingResult.js";
14
+ import {
15
+ PICKING_GATHER_STRIDE_WORDS,
16
+ PICKING_QUERY_OFF_VIEW,
17
+ shader_picking_gather
18
+ } from "./shader_picking_gather.js";
19
+
20
+ /**
21
+ * Bytes a query occupies in the gather buffer: the mesh row and the depth, one word each.
22
+ *
23
+ * @type {number}
24
+ */
25
+ const QUERY_STRIDE = PICKING_GATHER_STRIDE_WORDS * 4;
26
+
27
+ /**
28
+ * Answers "what is under this pixel" for entities that asked to be pickable.
29
+ *
30
+ * **Off the visibility buffer.** The rasterizer writes every pixel's `meshes` row — an entity's
31
+ * row, one on — and its depth; a pick reads those two texels out of the frame that drew them and
32
+ * resolves the row to the entity that drew it, then up its parents to the one that opted in. What
33
+ * was drawn is what is picked: at triangle precision, under skinning, with nothing kept on the CPU
34
+ * for it — no bounds, no tree, no per-frame refresh.
35
+ *
36
+ * **Asynchronous and batched, of necessity.** The texels are gathered by the frame after the query
37
+ * was asked and read back when the device has finished it, so an answer is a frame or two late and
38
+ * arrives for the whole batch at once. A result is a promise for that reason, and nothing this
39
+ * tick can come to depend on it.
40
+ *
41
+ * Terrain picking, which is where the game's precision matters, does not come through here at all:
42
+ * it is an exact raycast against the terrain's own representation and is untouched by any of this.
43
+ */
44
+ export class PickingSystem extends System {
45
+ /**
46
+ * @type {GraphicsEngine}
47
+ */
48
+ #graphics;
49
+
50
+ /**
51
+ * @type {PickingExtension|null}
52
+ */
53
+ #extension = null;
54
+
55
+ /**
56
+ * Entities that have opted in.
57
+ *
58
+ * @type {Set<number>}
59
+ */
60
+ #pickable = new Set();
61
+
62
+ /**
63
+ * Queries asked since the last frame took a batch, with the promise each batch answers.
64
+ *
65
+ * @type {{queries: PickingQuery[], resolve: function(PickingResult[]): void, reject: function(Error): void}[]}
66
+ */
67
+ #waiting = [];
68
+
69
+ /**
70
+ * @param {GraphicsEngine} graphics whose frames draw the view the picks read
71
+ */
72
+ constructor(graphics) {
73
+ super();
74
+
75
+ assert.defined(graphics, 'graphics');
76
+
77
+ this.#graphics = graphics;
78
+
79
+ this.dependencies = [Pickable];
80
+ }
81
+
82
+ async startup(entityManager) {
83
+ this.entityManager = entityManager;
84
+
85
+ this.#extension = this.#graphics.add_extension(new PickingExtension(this));
86
+ }
87
+
88
+ async shutdown(entityManager) {
89
+ if (this.#extension !== null) {
90
+ this.#graphics.remove_extension(this.#extension);
91
+ this.#extension = null;
92
+ }
93
+
94
+ this.#pickable.clear();
95
+
96
+ // nothing will draw a frame for them
97
+ for (const batch of this.#waiting.splice(0)) {
98
+ batch.reject(new Error('the picking system shut down before a frame answered'));
99
+ }
100
+ }
101
+
102
+ /**
103
+ * @param {Pickable} pickable
104
+ * @param {number} entity
105
+ */
106
+ link(pickable, entity) {
107
+ this.#pickable.add(entity);
108
+ }
109
+
110
+ /**
111
+ * @param {Pickable} pickable
112
+ * @param {number} entity
113
+ */
114
+ unlink(pickable, entity) {
115
+ this.#pickable.delete(entity);
116
+ }
117
+
118
+ /**
119
+ * How many entities have opted in.
120
+ *
121
+ * @returns {number}
122
+ */
123
+ get pickable_count() {
124
+ return this.#pickable.size;
125
+ }
126
+
127
+ /**
128
+ * Batches asked and not yet answered by a frame.
129
+ *
130
+ * @returns {number}
131
+ */
132
+ get waiting_count() {
133
+ return this.#waiting.length;
134
+ }
135
+
136
+ /**
137
+ * What is drawn under each of these pixels.
138
+ *
139
+ * One result per query, in the order the queries were given. A pixel over nothing pickable
140
+ * still gets a result — `entity` is {@link PickingResult.NOTHING} — so the two arrays line up
141
+ * by position and a caller never has to search for the answer to its own question.
142
+ *
143
+ * Answered by the next frame that draws the view, when the device has finished it.
144
+ *
145
+ * @param {PickingQuery[]} queries
146
+ * @returns {Promise<PickingResult[]>}
147
+ */
148
+ pick(queries) {
149
+ assert.isArray(queries, 'queries');
150
+
151
+ if (queries.length === 0) {
152
+ return Promise.resolve([]);
153
+ }
154
+
155
+ return new Promise((resolve, reject) => {
156
+ this.#waiting.push({ queries: queries.slice(), resolve, reject });
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Take everything asked since the last frame, to be answered by this one.
162
+ *
163
+ * @returns {{queries: PickingQuery[], resolve: function(PickingResult[]): void, reject: function(Error): void}[]}
164
+ */
165
+ take_waiting() {
166
+ return this.#waiting.splice(0);
167
+ }
168
+
169
+ /**
170
+ * The entity a drawn mesh row picks: the one that drew it, or the nearest of its ancestors,
171
+ * that opted in.
172
+ *
173
+ * @param {number} row a `meshes` row, as the visibility buffer holds it
174
+ * @returns {number} the entity, or {@link PickingResult.NOTHING}
175
+ */
176
+ resolve_row(row) {
177
+ if (row >= VIZ_BUFFER_INVALID_MESH_VALUE) {
178
+ return PickingResult.NOTHING;
179
+ }
180
+
181
+ const dataset = this.entityManager.dataset;
182
+ const pickable = this.#pickable;
183
+
184
+ let entity = entity_of_row(row);
185
+
186
+ while (entity >= 0 && dataset.entityExists(entity)) {
187
+ if (pickable.has(entity)) {
188
+ return entity;
189
+ }
190
+
191
+ const parent = dataset.getComponent(entity, ParentEntity);
192
+
193
+ entity = parent === undefined || parent === null ? -1 : parent.entity;
194
+ }
195
+
196
+ return PickingResult.NOTHING;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Gathers the queried texels out of the frame that drew them, after the G-buffer is written and
202
+ * before anything overwrites the visibility buffer, and answers the batch when they land.
203
+ */
204
+ export class PickingExtension extends RenderExtension {
205
+ name = "picking";
206
+ phase = FramePhase.AfterGBuffer;
207
+
208
+ /**
209
+ * @type {PickingSystem}
210
+ */
211
+ #system;
212
+
213
+ /**
214
+ * @param {PickingSystem} system
215
+ */
216
+ constructor(system) {
217
+ super();
218
+
219
+ this.#system = system;
220
+ }
221
+
222
+ /**
223
+ * @param {import("../../shade/renderer/extension/FrameContext.js").FrameContext} frame
224
+ */
225
+ record(frame) {
226
+ const batches = this.#system.take_waiting();
227
+
228
+ if (batches.length === 0) {
229
+ return;
230
+ }
231
+
232
+ // the view's own resolution: what the visibility buffer is sized to, whatever the frame's
233
+ // target at this phase is
234
+ const resolution = frame.view.resolution;
235
+ const width = resolution[0];
236
+ const height = resolution[1];
237
+
238
+ // what the frame projected with, kept for when the answer comes: the frame's camera has
239
+ // moved on by then
240
+ const view_projection = new Float32Array(frame.view.camera.view_projection_matrix);
241
+ const camera = frame.view.camera.camera.transform;
242
+ const eye = [camera.translation_x, camera.translation_y, camera.translation_z];
243
+
244
+ /** @type {PickingQuery[]} every query of every batch, in order */
245
+ const queries = [];
246
+
247
+ for (const batch of batches) {
248
+ for (const query of batch.queries) {
249
+ queries.push(query);
250
+ }
251
+ }
252
+
253
+ /*
254
+ The pixel each query names, resolved here rather than in the shader: the fraction is the
255
+ caller's unit and the resolution is the frame's, and neither is the gather's business. A
256
+ query that is not on the view at all carries `PICKING_QUERY_OFF_VIEW` instead of a pixel.
257
+ */
258
+ const pixels = new Uint32Array(queries.length * PICKING_GATHER_STRIDE_WORDS);
259
+
260
+ for (let i = 0; i < queries.length; i++) {
261
+ const query = queries[i];
262
+ const base = i * PICKING_GATHER_STRIDE_WORDS;
263
+
264
+ if (on_view(query)) {
265
+ pixels[base] = texel(query.u, width);
266
+ pixels[base + 1] = texel(query.v, height);
267
+ } else {
268
+ pixels[base] = PICKING_QUERY_OFF_VIEW;
269
+ }
270
+ }
271
+
272
+ const textures = frame.get(ViewTextures);
273
+ const graph = frame.graph;
274
+
275
+ const data = {};
276
+ const pass = graph.add("Picking / gather texels", data, (data, resources, context) => {
277
+ const encoder = context.encoder;
278
+
279
+ shader_picking_gather.dispatchThreads({
280
+ encoder,
281
+ bindings: {
282
+ query_count: queries.length,
283
+ visibility: resources.get(data.visibility),
284
+ visible_meshlets: resources.get(data.visible_meshlets),
285
+ viz_depth: resources.get(data.depth),
286
+ queries: encoder.allocateTransientBufferAndLoad(
287
+ pixels.buffer, GPUBufferUsage.STORAGE
288
+ ),
289
+ results: resources.get(data.results),
290
+ },
291
+ thread_count_x: queries.length,
292
+ });
293
+ });
294
+
295
+ data.visibility = pass.read(textures.visibility);
296
+ data.visible_meshlets = pass.read(textures.visible_meshlets);
297
+ data.depth = pass.read(textures.depth);
298
+ data.results = pass.create("picking/results", BufferResourceDescriptor.from(
299
+ queries.length * QUERY_STRIDE,
300
+ GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
301
+ ));
302
+
303
+ const system = this.#system;
304
+
305
+ graph_read_buffer({ graph, buffer: data.results })
306
+ .then(ref => {
307
+ try {
308
+ const words = new Uint32Array(ref.getValue());
309
+ const floats = new Float32Array(words.buffer);
310
+
311
+ let index = 0;
312
+
313
+ for (const batch of batches) {
314
+ const results = [];
315
+
316
+ for (let i = 0; i < batch.queries.length; i++, index++) {
317
+ const query = batch.queries[i];
318
+ const result = new PickingResult();
319
+
320
+ results.push(result);
321
+
322
+ if (!on_view(query)) {
323
+ // the gather writes an invalid row here too; this is the same
324
+ // answer without depending on a dispatch having run
325
+ continue;
326
+ }
327
+
328
+ const base = index * PICKING_GATHER_STRIDE_WORDS;
329
+ const entity = system.resolve_row(words[base]);
330
+
331
+ if (entity === PickingResult.NOTHING) {
332
+ continue;
333
+ }
334
+
335
+ result.entity = entity;
336
+
337
+ unproject(
338
+ result.position, view_projection,
339
+ texel(query.u, width), texel(query.v, height), width, height,
340
+ floats[base + 1]
341
+ );
342
+
343
+ result.distance = Math.hypot(
344
+ result.position.x - eye[0],
345
+ result.position.y - eye[1],
346
+ result.position.z - eye[2]
347
+ );
348
+ }
349
+
350
+ batch.resolve(results);
351
+ }
352
+ } finally {
353
+ ref.release();
354
+ }
355
+ })
356
+ .catch(error => {
357
+ for (const batch of batches) {
358
+ batch.reject(error instanceof Error ? error : new Error('the picking readback failed'));
359
+ }
360
+ });
361
+ }
362
+ }
363
+
364
+ /**
365
+ * @param {PickingQuery} query
366
+ * @returns {boolean} whether the point is on the view at all
367
+ */
368
+ function on_view(query) {
369
+ return query.u >= 0 && query.u < 1 && query.v >= 0 && query.v < 1;
370
+ }
371
+
372
+ /**
373
+ * The texel a fraction of the view falls in.
374
+ *
375
+ * @param {number} fraction in [0, 1)
376
+ * @param {number} extent texels
377
+ * @returns {number}
378
+ */
379
+ function texel(fraction, extent) {
380
+ return Math.min(extent - 1, Math.floor(fraction * extent));
381
+ }
382
+
383
+ const SCRATCH_INVERSE = new Float32Array(16);
384
+
385
+ /**
386
+ * The world point a pixel's depth stands for, through the inverse of what the frame projected
387
+ * with. Depth is what the buffer holds — reverse-Z, `1` at the near plane — and WebGPU's clip z
388
+ * is that same `[0, 1]`, so it goes straight in.
389
+ *
390
+ * @param {Vector3} target
391
+ * @param {Float32Array} view_projection
392
+ * @param {number} x pixel
393
+ * @param {number} y pixel
394
+ * @param {number} width
395
+ * @param {number} height
396
+ * @param {number} depth
397
+ */
398
+ function unproject(target, view_projection, x, y, width, height, depth) {
399
+ const m = SCRATCH_INVERSE;
400
+
401
+ m4_invert(m, view_projection);
402
+
403
+ // the pixel's centre, in NDC: x to the right, y up
404
+ const nx = ((x + 0.5) / width) * 2 - 1;
405
+ const ny = 1 - ((y + 0.5) / height) * 2;
406
+ const nz = depth;
407
+
408
+ // column-major: column c is m[c * 4 .. c * 4 + 3]
409
+ const wx = m[0] * nx + m[4] * ny + m[8] * nz + m[12];
410
+ const wy = m[1] * nx + m[5] * ny + m[9] * nz + m[13];
411
+ const wz = m[2] * nx + m[6] * ny + m[10] * nz + m[14];
412
+ const ww = m[3] * nx + m[7] * ny + m[11] * nz + m[15];
413
+
414
+ target.set(wx / ww, wy / ww, wz / ww);
415
+ }