@woosh/meep-engine 3.64.1 → 3.67.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 (372) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/build/bundle-worker-image-decoder.js +1 -1
  3. package/build/bundle-worker-terrain.js +1 -1
  4. package/package.json +1 -1
  5. package/src/core/binary/BinaryBuffer.d.ts +5 -0
  6. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  7. package/src/core/binary/BinaryBuffer.js +39 -5
  8. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts +65 -6
  9. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts.map +1 -1
  10. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +451 -101
  11. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts +4 -2
  12. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -1
  13. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +4 -2
  14. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts +8 -1
  15. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts.map +1 -1
  16. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +45 -10
  17. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  18. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +237 -119
  19. package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts +143 -0
  20. package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts.map +1 -0
  21. package/src/engine/graphics/ecs/grass/GrassSpecies.js +189 -0
  22. package/src/engine/graphics/ecs/grass/GrassSurface.d.ts +25 -0
  23. package/src/engine/graphics/ecs/grass/GrassSurface.d.ts.map +1 -0
  24. package/src/engine/graphics/ecs/grass/GrassSurface.js +32 -0
  25. package/src/engine/graphics/ecs/grass/GroundCover.d.ts +68 -0
  26. package/src/engine/graphics/ecs/grass/GroundCover.d.ts.map +1 -0
  27. package/src/engine/graphics/ecs/grass/GroundCover.js +85 -0
  28. package/src/engine/graphics3/GrassSurfaceSystem.d.ts +153 -0
  29. package/src/engine/graphics3/GrassSurfaceSystem.d.ts.map +1 -0
  30. package/src/engine/graphics3/GrassSurfaceSystem.js +1076 -0
  31. package/src/engine/graphics3/GroundCoverSystem.d.ts +33 -0
  32. package/src/engine/graphics3/GroundCoverSystem.d.ts.map +1 -0
  33. package/src/engine/graphics3/GroundCoverSystem.js +58 -0
  34. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  35. package/src/engine/network/NetworkSession.js +372 -110
  36. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  37. package/src/engine/network/orchestrator/NetworkPeer.js +6 -1
  38. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  39. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +9 -2
  40. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  41. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +325 -140
  42. package/src/engine/network/sim/SimActionExecutor.d.ts +4 -1
  43. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  44. package/src/engine/network/sim/SimActionExecutor.js +65 -9
  45. package/src/engine/network/state/PredictionHistory.d.ts.map +1 -1
  46. package/src/engine/network/state/PredictionHistory.js +6 -1
  47. package/src/engine/network/time/AdaptiveRenderDelay.d.ts +25 -14
  48. package/src/engine/network/time/AdaptiveRenderDelay.d.ts.map +1 -1
  49. package/src/engine/network/time/AdaptiveRenderDelay.js +38 -20
  50. package/src/engine/network/time/RenderPlayout.d.ts +23 -3
  51. package/src/engine/network/time/RenderPlayout.d.ts.map +1 -1
  52. package/src/engine/network/time/RenderPlayout.js +40 -4
  53. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  54. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts +5 -1
  55. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts.map +1 -1
  56. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.js +27 -1
  57. package/src/format/lut/cube/CubeLut.d.ts +38 -0
  58. package/src/format/lut/cube/CubeLut.d.ts.map +1 -0
  59. package/src/format/lut/cube/CubeLut.js +23 -0
  60. package/src/format/lut/cube/cube_read.d.ts +23 -0
  61. package/src/format/lut/cube/cube_read.d.ts.map +1 -0
  62. package/src/format/lut/cube/cube_read.js +199 -0
  63. package/src/format/lut/cube/cube_write.d.ts +15 -0
  64. package/src/format/lut/cube/cube_write.d.ts.map +1 -0
  65. package/src/format/lut/cube/cube_write.js +90 -0
  66. package/src/shade/device/shader/ShaderModuleCompiler.d.ts +2 -0
  67. package/src/shade/device/shader/ShaderModuleCompiler.d.ts.map +1 -1
  68. package/src/shade/device/shader/ShaderModuleCompiler.js +25 -2
  69. package/src/shade/playground/grass_surfaces/index.html +75 -0
  70. package/src/shade/playground/grass_surfaces/main.d.ts +2 -0
  71. package/src/shade/playground/grass_surfaces/main.d.ts.map +1 -0
  72. package/src/shade/playground/grass_surfaces/main.js +593 -0
  73. package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts +62 -0
  74. package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts.map +1 -0
  75. package/src/shade/playground/grass_surfaces/read_grass_surfaces.js +188 -0
  76. package/src/shade/playground/probe_clipmap/README.md +1 -0
  77. package/src/shade/playground/probe_clipmap/index.html +5 -0
  78. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  79. package/src/shade/playground/probe_clipmap/main.js +7 -0
  80. package/src/shade/playground/probe_clipmap_box/README.md +5 -1
  81. package/src/shade/playground/probe_clipmap_box/index.html +12 -0
  82. package/src/shade/playground/probe_clipmap_box/main.d.ts.map +1 -1
  83. package/src/shade/playground/probe_clipmap_box/main.js +15 -0
  84. package/src/shade/playground/probe_clipmap_box/scene.d.ts +8 -1
  85. package/src/shade/playground/probe_clipmap_box/scene.d.ts.map +1 -1
  86. package/src/shade/playground/probe_clipmap_box/scene.js +74 -6
  87. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  88. package/src/shade/renderer/Renderer.js +59 -55
  89. package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts +73 -4
  90. package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts.map +1 -1
  91. package/src/shade/renderer/deferred/tonemap/ColorGrade.js +113 -7
  92. package/src/shade/renderer/deferred/tonemap/TONEMAP_PURKINJE_SHIFT.d.ts +12 -0
  93. package/src/shade/renderer/deferred/tonemap/TONEMAP_PURKINJE_SHIFT.d.ts.map +1 -0
  94. package/src/shade/renderer/deferred/tonemap/TONEMAP_PURKINJE_SHIFT.js +12 -0
  95. package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts +15 -1
  96. package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts.map +1 -1
  97. package/src/shade/renderer/deferred/tonemap/ToneMapper.js +31 -3
  98. package/src/shade/renderer/deferred/tonemap/chunk_tonemap_purkinje.d.ts +25 -0
  99. package/src/shade/renderer/deferred/tonemap/chunk_tonemap_purkinje.d.ts.map +1 -0
  100. package/src/shade/renderer/deferred/tonemap/chunk_tonemap_purkinje.js +51 -0
  101. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts +2 -2
  102. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts.map +1 -1
  103. package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.js +6 -3
  104. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts +3 -2
  105. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts.map +1 -1
  106. package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.js +7 -3
  107. package/src/shade/renderer/extension/ViewTextures.d.ts +12 -5
  108. package/src/shade/renderer/extension/ViewTextures.d.ts.map +1 -1
  109. package/src/shade/renderer/extension/ViewTextures.js +19 -5
  110. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +3 -1
  111. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts.map +1 -1
  112. package/src/shade/renderer/fluid/GPUFluidCascadeStack.js +6 -1
  113. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +7 -0
  114. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts.map +1 -1
  115. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.js +10 -0
  116. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +7 -1
  117. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  118. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +46 -15
  119. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts +3 -2
  120. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts.map +1 -1
  121. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.js +3 -2
  122. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts +22 -0
  123. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts.map +1 -0
  124. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.js +247 -0
  125. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +50 -0
  126. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  127. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +101 -0
  128. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +2 -1
  129. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  130. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +2 -1
  131. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +8 -0
  132. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
  133. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +12 -0
  134. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts +12 -6
  135. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts.map +1 -1
  136. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.js +35 -29
  137. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.d.ts.map +1 -1
  138. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.js +4 -2
  139. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.d.ts.map +1 -1
  140. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.js +6 -4
  141. package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.d.ts.map +1 -1
  142. package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.js +2 -1
  143. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts +6 -5
  144. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts.map +1 -1
  145. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.js +15 -14
  146. package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.d.ts.map +1 -1
  147. package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.js +4 -2
  148. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +19 -2
  149. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  150. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +128 -10
  151. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +19 -1
  152. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  153. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +27 -2
  154. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts +19 -11
  155. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts.map +1 -1
  156. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.js +35 -11
  157. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  158. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +3 -1
  159. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts +40 -0
  160. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts.map +1 -0
  161. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.js +174 -0
  162. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.d.ts +4 -4
  163. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.js +10 -10
  164. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts +31 -0
  165. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts.map +1 -0
  166. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.js +54 -0
  167. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts +8 -4
  168. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  169. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +10 -21
  170. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts +7 -2
  171. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts.map +1 -1
  172. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.js +33 -29
  173. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts +28 -0
  174. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts.map +1 -0
  175. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.js +138 -0
  176. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +13 -9
  177. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts.map +1 -1
  178. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +18 -11
  179. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +1 -1
  180. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.d.ts.map +1 -1
  181. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js +4 -2
  182. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts.map +1 -1
  183. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js +2 -3
  184. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts +5 -3
  185. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts.map +1 -1
  186. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js +15 -30
  187. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -1
  188. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
  189. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +4 -2
  190. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts +11 -0
  191. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts.map +1 -1
  192. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +29 -2
  193. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts +40 -0
  194. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts.map +1 -0
  195. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.js +126 -0
  196. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts +36 -0
  197. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts.map +1 -0
  198. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.js +221 -0
  199. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts +40 -0
  200. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts.map +1 -0
  201. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.js +86 -0
  202. package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts +135 -0
  203. package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts.map +1 -0
  204. package/src/shade/renderer/grass/blade/GPUGrassBlades.js +676 -0
  205. package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts +145 -0
  206. package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts.map +1 -0
  207. package/src/shade/renderer/grass/blade/GRASS_BLADE.js +179 -0
  208. package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts +63 -0
  209. package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts.map +1 -0
  210. package/src/shade/renderer/grass/blade/chunk_grass_blade.js +244 -0
  211. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts +58 -0
  212. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts.map +1 -0
  213. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.js +174 -0
  214. package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts +25 -0
  215. package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts.map +1 -0
  216. package/src/shade/renderer/grass/blade/grass_blade_id_draw.js +133 -0
  217. package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts +80 -0
  218. package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts.map +1 -0
  219. package/src/shade/renderer/grass/blade/grass_blade_mechanics.js +92 -0
  220. package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts +29 -0
  221. package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts.map +1 -0
  222. package/src/shade/renderer/grass/blade/grass_blade_resolve.js +175 -0
  223. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts +14 -0
  224. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts.map +1 -0
  225. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.js +45 -0
  226. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts +61 -0
  227. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts.map +1 -0
  228. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.js +340 -0
  229. package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts +46 -0
  230. package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts.map +1 -0
  231. package/src/shade/renderer/grass/blade/shader_grass_cell_count.js +280 -0
  232. package/src/shade/renderer/grass/population/chunk_grass_population.d.ts +60 -0
  233. package/src/shade/renderer/grass/population/chunk_grass_population.d.ts.map +1 -0
  234. package/src/shade/renderer/grass/population/chunk_grass_population.js +171 -0
  235. package/src/shade/renderer/grass/press/GPUGrassPress.d.ts +99 -0
  236. package/src/shade/renderer/grass/press/GPUGrassPress.d.ts.map +1 -0
  237. package/src/shade/renderer/grass/press/GPUGrassPress.js +436 -0
  238. package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts +179 -0
  239. package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts.map +1 -0
  240. package/src/shade/renderer/grass/press/GRASS_PRESS.js +217 -0
  241. package/src/shade/renderer/grass/press/chunk_grass_press.d.ts +65 -0
  242. package/src/shade/renderer/grass/press/chunk_grass_press.d.ts.map +1 -0
  243. package/src/shade/renderer/grass/press/chunk_grass_press.js +296 -0
  244. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts +31 -0
  245. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts.map +1 -0
  246. package/src/shade/renderer/grass/press/shader_grass_press_mark.js +169 -0
  247. package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts +27 -0
  248. package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts.map +1 -0
  249. package/src/shade/renderer/grass/press/shader_grass_press_stamp.js +183 -0
  250. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts +193 -0
  251. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts.map +1 -0
  252. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.js +1253 -0
  253. package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts +211 -0
  254. package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts.map +1 -0
  255. package/src/shade/renderer/grass/surface/GRASS_SURFACE.js +241 -0
  256. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts +82 -0
  257. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts.map +1 -0
  258. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.js +111 -0
  259. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts +39 -0
  260. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts.map +1 -0
  261. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.js +85 -0
  262. package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts +29 -0
  263. package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts.map +1 -0
  264. package/src/shade/renderer/grass/surface/chunk_grass_directory.js +112 -0
  265. package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts +27 -0
  266. package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts.map +1 -0
  267. package/src/shade/renderer/grass/surface/chunk_grass_slot.js +50 -0
  268. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts +21 -0
  269. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts.map +1 -0
  270. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.js +41 -0
  271. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts +32 -0
  272. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts.map +1 -0
  273. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.js +136 -0
  274. package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts +37 -0
  275. package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts.map +1 -0
  276. package/src/shade/renderer/grass/surface/chunk_grass_surfel.js +85 -0
  277. package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts +25 -0
  278. package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts.map +1 -0
  279. package/src/shade/renderer/grass/surface/chunk_grass_tile.js +52 -0
  280. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts +23 -0
  281. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts.map +1 -0
  282. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.js +87 -0
  283. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts +43 -0
  284. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts.map +1 -0
  285. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.js +265 -0
  286. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts +13 -0
  287. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts.map +1 -0
  288. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.js +49 -0
  289. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts +62 -0
  290. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts.map +1 -0
  291. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.js +162 -0
  292. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts +40 -0
  293. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts.map +1 -0
  294. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.js +155 -0
  295. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts +19 -0
  296. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts.map +1 -0
  297. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.js +58 -0
  298. package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts +30 -0
  299. package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts.map +1 -0
  300. package/src/shade/renderer/grass/surface/shader_grass_surface_want.js +251 -0
  301. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts +21 -0
  302. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts.map +1 -0
  303. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.js +75 -0
  304. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts +35 -9
  305. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts.map +1 -1
  306. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.js +81 -11
  307. package/src/shade/renderer/postprocess/eye/shader_autoexposure_adapt.d.ts.map +1 -1
  308. package/src/shade/renderer/postprocess/eye/shader_autoexposure_adapt.js +4 -1
  309. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
  310. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +4 -23
  311. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts +12 -0
  312. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts.map +1 -0
  313. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.js +29 -0
  314. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  315. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +22 -0
  316. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  317. package/src/shade/renderer/scene/GPUSceneContext.js +3 -0
  318. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts +27 -0
  319. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts.map +1 -0
  320. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.js +38 -0
  321. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts +28 -0
  322. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts.map +1 -0
  323. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.js +58 -0
  324. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
  325. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +8 -0
  326. package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts +6 -0
  327. package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts.map +1 -1
  328. package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.js +7 -0
  329. package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts +4 -4
  330. package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts.map +1 -1
  331. package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.js +86 -5
  332. package/src/shade/renderer/shader/chunk/color/tonemap/purkinje/chunk_apply_purkinje_shift.d.ts +65 -0
  333. package/src/shade/renderer/shader/chunk/color/tonemap/purkinje/chunk_apply_purkinje_shift.d.ts.map +1 -0
  334. package/src/shade/renderer/shader/chunk/color/tonemap/purkinje/chunk_apply_purkinje_shift.js +136 -0
  335. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts +37 -0
  336. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts.map +1 -0
  337. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.js +148 -0
  338. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts +20 -0
  339. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts.map +1 -0
  340. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.js +169 -0
  341. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts +18 -0
  342. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts.map +1 -1
  343. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.js +28 -0
  344. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts +13 -0
  345. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts.map +1 -0
  346. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.js +22 -0
  347. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  348. package/src/shade/renderer/water/GPUWaterRenderer.js +4 -0
  349. package/src/shade/renderer/wind/GPUSceneWind.d.ts +52 -5
  350. package/src/shade/renderer/wind/GPUSceneWind.d.ts.map +1 -1
  351. package/src/shade/renderer/wind/GPUSceneWind.js +164 -8
  352. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts +3 -1
  353. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts.map +1 -1
  354. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.js +3 -1
  355. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts +2 -1
  356. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts.map +1 -1
  357. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.js +18 -13
  358. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts +13 -0
  359. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts.map +1 -0
  360. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.js +23 -0
  361. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts +1 -0
  362. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts.map +1 -1
  363. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.js +6 -1
  364. package/src/shade/renderer/wind/wind_field_add_resources.d.ts +11 -0
  365. package/src/shade/renderer/wind/wind_field_add_resources.d.ts.map +1 -1
  366. package/src/shade/renderer/wind/wind_field_add_resources.js +15 -0
  367. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +1 -1
  368. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  369. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
  370. package/src/shade/renderer/shader/chunk/color/tonemap/purkinjie/chunk_apply_purkinjie_shift.d.ts +0 -11
  371. package/src/shade/renderer/shader/chunk/color/tonemap/purkinjie/chunk_apply_purkinjie_shift.d.ts.map +0 -1
  372. package/src/shade/renderer/shader/chunk/color/tonemap/purkinjie/chunk_apply_purkinjie_shift.js +0 -56
@@ -0,0 +1,1076 @@
1
+ import { assert } from "../../core/assert.js";
2
+ import { m4_from_rotation_translation_scale_array } from "../../core/geom/3d/mat4/m4_from_rotation_translation_scale_array.js";
3
+ import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
4
+ import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
5
+ import { GPUGrassBlades } from "../../shade/renderer/grass/blade/GPUGrassBlades.js";
6
+ import { GPUGrassSurfaces } from "../../shade/renderer/grass/surface/GPUGrassSurfaces.js";
7
+ import { grass_surface_lattice_bits } from "../../shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.js";
8
+ import {
9
+ MESH_GROUND_COVER_FLAG_GROWS,
10
+ MESH_GROUND_COVER_FLAG_MEMBER
11
+ } from "../../shade/renderer/scene/MESH_GROUND_COVER_STRUCT.js";
12
+ import { row_of_entity } from "../../shade/renderer/scene/rows/GPUSceneRows.js";
13
+ import { EventType } from "../ecs/EventType.js";
14
+ import { System } from "../ecs/System.js";
15
+ import { TRANSFORM_ATTACHMENT_EVENT_CHANGE } from "../ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.js";
16
+ import { TransformAttachment } from "../ecs/transform-attachment/TransformAttachment.js";
17
+ import { TRANSFORM64_EVENT_CHANGE } from "../ecs/transform/TRANSFORM64_EVENT_CHANGE.js";
18
+ import {
19
+ Transform64,
20
+ TRANSFORM64_ROTATION_OFFSET,
21
+ TRANSFORM64_SCALE_OFFSET,
22
+ TRANSFORM64_TRANSLATION_OFFSET
23
+ } from "../ecs/transform/Transform64.js";
24
+ import { GrassSurface } from "../graphics/ecs/grass/GrassSurface.js";
25
+ import { GPUFluidSystem } from "./GPUFluidSystem.js";
26
+ import { SHADED_GEOMETRY_EVENT_CHANGE } from "../graphics/ecs/mesh-v2/SHADED_GEOMETRY_EVENT_CHANGE.js";
27
+
28
+ /**
29
+ * @typedef {import("../../shade/renderer/scene/GPUSceneContext.js").GPUSceneContext} GPUSceneContext
30
+ * @typedef {import("../../shade/renderer/scene/Scene.js").Scene} Scene
31
+ * @typedef {import("../graphics/ecs/grass/GrassSpecies.js").GrassSpecies} GrassSpecies
32
+ * @typedef {import("../graphics/ecs/grass/GroundCover.js").GroundCover} GroundCover
33
+ * @typedef {import("../graphics/ecs/mesh-v2/ShadedGeometry.js").ShadedGeometry} ShadedGeometry
34
+ * @typedef {import("./GraphicsEngine.js").GraphicsEngine} GraphicsEngine
35
+ */
36
+
37
+ /**
38
+ * A grass surface's entity, and what is held on its behalf.
39
+ */
40
+ class SurfaceEntry {
41
+ /** @type {number} */
42
+ entity = -1;
43
+
44
+ /** @type {Transform64} */
45
+ transform = null;
46
+
47
+ /**
48
+ * Whether the entity's `transforms` row is held in the current context.
49
+ * @type {boolean}
50
+ */
51
+ held = false;
52
+
53
+ /** @type {function(): void} */
54
+ on_world = null;
55
+
56
+ /** @type {function(): void} */
57
+ on_local = null;
58
+
59
+ /** @type {function({klass: Function}): void} */
60
+ on_components = null;
61
+ }
62
+
63
+ /**
64
+ * A mesh entity that is ground of a surface.
65
+ */
66
+ class MemberEntry {
67
+ /** @type {number} */
68
+ entity = -1;
69
+
70
+ /** @type {GroundCover} */
71
+ cover = null;
72
+
73
+ /** @type {ShadedGeometry} */
74
+ shaded = null;
75
+
76
+ /** @type {Transform64} */
77
+ transform = null;
78
+
79
+ /**
80
+ * The species the entry is counted under, which is the cover's as of the last write.
81
+ * @type {GrassSpecies|null}
82
+ */
83
+ species = null;
84
+
85
+ /**
86
+ * Whether the entity's `mesh_ground_covers` row is written in the current context.
87
+ * @type {boolean}
88
+ */
89
+ written = false;
90
+
91
+ /**
92
+ * The geometry the row was last written for; null before the first write. The geometry itself and not
93
+ * its address on the device, which a geometry that replaces another can be given.
94
+ * @type {object|null}
95
+ */
96
+ geometry = null;
97
+
98
+ /**
99
+ * Whether the member's ground is marked on the device where {@link corners} say it is.
100
+ * @type {boolean}
101
+ */
102
+ placed = false;
103
+
104
+ /**
105
+ * The eight corners of the member's geometry box in its surface's frame, as last marked.
106
+ * @type {Float64Array}
107
+ */
108
+ corners = new Float64Array(24);
109
+
110
+ /**
111
+ * The rectangle of {@link corners} on the frame's x and z: low x, low z, high x, high z.
112
+ * @type {Float64Array}
113
+ */
114
+ rectangle = new Float64Array(4);
115
+
116
+ /** @type {function(): void} */
117
+ on_changed = null;
118
+
119
+ /** @type {function(): void} */
120
+ on_world = null;
121
+ }
122
+
123
+ /**
124
+ * How far a corner of a member's box may be from where it was, in its surface's frame, before the member
125
+ * counts as moved and the ground under it is baked again: a micron. A surface that moves as one moves its
126
+ * members by the arithmetic's noise and no more, which for a world kilometres across is a ten-thousandth
127
+ * of this; nothing a surfel holds can show a move this small.
128
+ *
129
+ * @type {number}
130
+ */
131
+ export const GRASS_MEMBER_MOVE_TOLERANCE = 1e-6;
132
+
133
+ const ONES = Float64Array.of(1, 1, 1);
134
+
135
+ const scratch_frame = new Float64Array(16);
136
+ const scratch_member = new Float64Array(16);
137
+ const scratch_low = new Float64Array(3);
138
+ const scratch_high = new Float64Array(3);
139
+ const scratch_corners = new Float64Array(24);
140
+
141
+ /**
142
+ * The corners of a member's geometry box, placed by the member's transform and read in its surface's frame.
143
+ *
144
+ * @param {Float64Array} corners the eight corners, written here
145
+ * @param {Float64Array} frame the surface's frame, frame to world: a rotation and a translation
146
+ * @param {MemberEntry} member
147
+ */
148
+ function member_corners(corners, frame, member) {
149
+ const box = member.shaded.geometry.bounding_box;
150
+ const t = member.transform;
151
+
152
+ const m = scratch_member;
153
+
154
+ m4_from_rotation_translation_scale_array(
155
+ m,
156
+ t, TRANSFORM64_ROTATION_OFFSET,
157
+ t, TRANSFORM64_TRANSLATION_OFFSET,
158
+ t, TRANSFORM64_SCALE_OFFSET
159
+ );
160
+
161
+ for (let corner = 0; corner < 8; corner++) {
162
+ const x = box[(corner & 1) === 0 ? 0 : 3];
163
+ const y = box[(corner & 2) === 0 ? 1 : 4];
164
+ const z = box[(corner & 4) === 0 ? 2 : 5];
165
+
166
+ // into the world, and from the frame's origin
167
+ const wx = m[0] * x + m[4] * y + m[8] * z + m[12] - frame[12];
168
+ const wy = m[1] * x + m[5] * y + m[9] * z + m[13] - frame[13];
169
+ const wz = m[2] * x + m[6] * y + m[10] * z + m[14] - frame[14];
170
+
171
+ // onto the frame's axes
172
+ for (let axis = 0; axis < 3; axis++) {
173
+ corners[corner * 3 + axis] = frame[axis * 4] * wx + frame[axis * 4 + 1] * wy + frame[axis * 4 + 2] * wz;
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Keeps the scene's grass surfaces on the device: which entities are surfaces, which mesh rows are ground
180
+ * of which, and a box and a tallest blade for each surface, so that views can ask for its tiles.
181
+ *
182
+ * A surface is an entity with [{@link GrassSurface}, {@link Transform64}]; this system links those. Its
183
+ * members are the mesh entities whose {@link GroundCover} names it, which {@link GroundCoverSystem} links
184
+ * and hands over through {@link add_member}. A member may arrive before its surface, and waits.
185
+ *
186
+ * **What is written, and when.** Nothing per frame: a link, an unlink, an edit announced by a component's
187
+ * `onChanged`, and a move announced by the dataset queue the surface or member they touch, and
188
+ * {@link update} writes what is queued.
189
+ *
190
+ * - A member's `mesh_ground_covers` row, at the entity's own row: its surface's `transforms` row, the
191
+ * geometry a column traces for it, its species and density. Written once the mesh row itself exists.
192
+ * - A surface's record in {@link GPUGrassSurfaces}: a box, in the surface's frame, that holds every member
193
+ * whose row is written, as placed, and the tallest blade of any of their species. A surface with no such
194
+ * member has no record.
195
+ * - The ground that changed ({@link GPUGrassSurfaces#invalidate}): the rectangle of a member that has come,
196
+ * gone, changed geometry, or moved in its surface's frame by more than
197
+ * {@link GRASS_MEMBER_MOVE_TOLERANCE}. A surface that moves as one marks nothing.
198
+ * - A surface's `transforms` row is held, so that a surface entity with no mesh of its own has a frame on
199
+ * the device.
200
+ *
201
+ * **Rows need a device.** While the scene has no GPU context nothing is written, and when one arrives or
202
+ * is replaced everything is queued again.
203
+ */
204
+ export class GrassSurfaceSystem extends System {
205
+ dependencies = [GrassSurface, Transform64];
206
+
207
+ /** @type {GraphicsEngine} */
208
+ #graphics;
209
+
210
+ /** @type {Scene} */
211
+ #scene;
212
+
213
+ /** @type {GPUSceneContext|null} */
214
+ #context = null;
215
+
216
+ /** @type {GPUGrassSurfaces|null} */
217
+ #gpu = null;
218
+
219
+ /** @type {GPUGrassBlades|null} */
220
+ #blades = null;
221
+
222
+ /** @type {GrassSurfaceExtension|null} */
223
+ #extension = null;
224
+
225
+ /** @type {GrassBladeExtension|null} */
226
+ #draw_extension = null;
227
+
228
+ /**
229
+ * The finest spacing the surfaces' `grass_surface_params` rows were written for.
230
+ * @type {number}
231
+ */
232
+ #params_spacing = 0;
233
+
234
+ /** @type {Map<number, SurfaceEntry>} */
235
+ #surfaces = new Map();
236
+
237
+ /** @type {Map<number, MemberEntry>} */
238
+ #members = new Map();
239
+
240
+ /**
241
+ * Members by the surface entity their cover names, whether or not that entity is a surface yet.
242
+ * @type {Map<number, Set<MemberEntry>>}
243
+ */
244
+ #members_of = new Map();
245
+
246
+ /**
247
+ * Surface entities whose record is to be written, or taken away if they are surfaces no longer.
248
+ * @type {Set<number>}
249
+ */
250
+ #dirty_surfaces = new Set();
251
+
252
+ /**
253
+ * Members whose row is to be written.
254
+ * @type {Set<MemberEntry>}
255
+ */
256
+ #dirty_members = new Set();
257
+
258
+ /**
259
+ * Each species in use: its index, how many members grow it, and the listener on its `onChanged`.
260
+ * @type {Map<GrassSpecies, {index: number, uses: number, on_changed: function(): void}>}
261
+ */
262
+ #species = new Map();
263
+
264
+ /** @type {number[]} species indices given back */
265
+ #species_free = [];
266
+
267
+ /** @type {number} */
268
+ #species_next = 0;
269
+
270
+ /**
271
+ * @param {GraphicsEngine} graphics
272
+ * @param {Scene} scene
273
+ */
274
+ constructor(graphics, scene) {
275
+ super();
276
+
277
+ assert.defined(graphics, 'graphics');
278
+ assert.defined(scene, 'scene');
279
+
280
+ this.#graphics = graphics;
281
+ this.#scene = scene;
282
+ }
283
+
284
+ /**
285
+ * @returns {Scene}
286
+ */
287
+ get scene() {
288
+ return this.#scene;
289
+ }
290
+
291
+ /**
292
+ * The surfaces on the device, or null while the scene has no GPU context.
293
+ *
294
+ * @returns {GPUGrassSurfaces|null}
295
+ */
296
+ get surfaces() {
297
+ return this.#gpu;
298
+ }
299
+
300
+ /**
301
+ * The blades on the device, or null while the scene has no GPU context.
302
+ *
303
+ * @returns {GPUGrassBlades|null}
304
+ */
305
+ get blades() {
306
+ return this.#blades;
307
+ }
308
+
309
+ async startup(entityManager) {
310
+ this.entityManager = entityManager;
311
+
312
+ this.#extension = this.#graphics.add_extension(new GrassSurfaceExtension(this));
313
+ this.#draw_extension = this.#graphics.add_extension(new GrassBladeExtension(this));
314
+ }
315
+
316
+ async shutdown(entityManager) {
317
+ for (const entry of Array.from(this.#surfaces.values())) {
318
+ this.unlink(null, entry.transform, entry.entity);
319
+ }
320
+
321
+ if (this.#extension !== null) {
322
+ this.#graphics.remove_extension(this.#extension);
323
+ this.#extension = null;
324
+ }
325
+
326
+ if (this.#draw_extension !== null) {
327
+ this.#graphics.remove_extension(this.#draw_extension);
328
+ this.#draw_extension = null;
329
+ }
330
+
331
+ this.#drop_context();
332
+ }
333
+
334
+ /** The scene's rows are this dataset's entities; the first system to place one says so. */
335
+ handleDatasetAttached(dataset) {
336
+ assert.ok(this.#scene.dataset == null || this.#scene.dataset === dataset, 'scene belongs to another dataset');
337
+
338
+ this.#scene.dataset = dataset;
339
+ }
340
+
341
+ handleDatasetDetached(dataset) {
342
+ if (this.#scene.dataset === dataset) {
343
+ this.#scene.dataset = null;
344
+ }
345
+ }
346
+
347
+ /**
348
+ * @param {GrassSurface} surface
349
+ * @param {Transform64} transform
350
+ * @param {number} entity
351
+ */
352
+ link(surface, transform, entity) {
353
+ const entry = new SurfaceEntry();
354
+
355
+ entry.entity = entity;
356
+ entry.transform = transform;
357
+
358
+ entry.on_world = () => {
359
+ if (entry.held) {
360
+ this.#context.rows.mark_world(entity);
361
+ }
362
+
363
+ // the members are where they were; what they are in the frame is not
364
+ this.#dirty_surfaces.add(entity);
365
+ };
366
+
367
+ entry.on_local = () => {
368
+ if (entry.held) {
369
+ this.#context.rows.mark_local(entity);
370
+ }
371
+ };
372
+
373
+ entry.on_components = ({ klass }) => {
374
+ if (klass === TransformAttachment) {
375
+ entry.on_local();
376
+ }
377
+ };
378
+
379
+ const dataset = this.entityManager.dataset;
380
+
381
+ dataset.addEntityEventListener(entity, TRANSFORM64_EVENT_CHANGE, entry.on_world);
382
+ dataset.addEntityEventListener(entity, TRANSFORM_ATTACHMENT_EVENT_CHANGE, entry.on_local);
383
+ dataset.addEntityEventListener(entity, EventType.ComponentAdded, entry.on_components);
384
+ dataset.addEntityEventListener(entity, EventType.ComponentRemoved, entry.on_components);
385
+
386
+ this.#surfaces.set(entity, entry);
387
+ this.#dirty_surfaces.add(entity);
388
+
389
+ // members that came first can be written now
390
+ this.#queue_members_of(entity);
391
+ }
392
+
393
+ /**
394
+ * @param {GrassSurface} surface
395
+ * @param {Transform64} transform
396
+ * @param {number} entity
397
+ */
398
+ unlink(surface, transform, entity) {
399
+ const entry = this.#surfaces.get(entity);
400
+
401
+ if (entry === undefined) {
402
+ return;
403
+ }
404
+
405
+ const dataset = this.entityManager.dataset;
406
+
407
+ dataset.removeEntityEventListener(entity, TRANSFORM64_EVENT_CHANGE, entry.on_world);
408
+ dataset.removeEntityEventListener(entity, TRANSFORM_ATTACHMENT_EVENT_CHANGE, entry.on_local);
409
+ dataset.removeEntityEventListener(entity, EventType.ComponentAdded, entry.on_components);
410
+ dataset.removeEntityEventListener(entity, EventType.ComponentRemoved, entry.on_components);
411
+
412
+ this.#surfaces.delete(entity);
413
+
414
+ // a context that has been replaced took its rows with it; only the one that is running is handed anything back
415
+ if (this.#context !== null && this.#context === this.#graphics.scene_context(this.#scene)) {
416
+ if (entry.held) {
417
+ this.#context.rows.release(entity);
418
+ }
419
+
420
+ this.#gpu.remove(row_of_entity(entity));
421
+ this.#clear_params(row_of_entity(entity), this.#context);
422
+ }
423
+
424
+ entry.held = false;
425
+
426
+ this.#dirty_surfaces.delete(entity);
427
+
428
+ // its members are ground of nothing until it is a surface again
429
+ this.#queue_members_of(entity);
430
+ }
431
+
432
+ /**
433
+ * A mesh entity becomes ground of the surface its cover names.
434
+ *
435
+ * @param {number} entity
436
+ * @param {GroundCover} cover
437
+ * @param {ShadedGeometry} shaded
438
+ * @param {Transform64} transform
439
+ */
440
+ add_member(entity, cover, shaded, transform) {
441
+ assert.isNonNegativeInteger(entity, 'entity');
442
+ assert.equal(this.#members.has(entity), false, 'entity is a member already');
443
+
444
+ const member = new MemberEntry();
445
+
446
+ member.entity = entity;
447
+ member.cover = cover;
448
+ member.shaded = shaded;
449
+ member.transform = transform;
450
+
451
+ member.on_changed = () => {
452
+ this.#dirty_members.add(member);
453
+ this.#dirty_surfaces.add(cover.surface);
454
+ };
455
+
456
+ member.on_world = () => {
457
+ this.#dirty_surfaces.add(cover.surface);
458
+ };
459
+
460
+ cover.onChanged.add(member.on_changed);
461
+
462
+ const dataset = this.entityManager.dataset;
463
+
464
+ dataset.addEntityEventListener(entity, TRANSFORM64_EVENT_CHANGE, member.on_world);
465
+ dataset.addEntityEventListener(entity, SHADED_GEOMETRY_EVENT_CHANGE, member.on_changed);
466
+
467
+ this.#members.set(entity, member);
468
+
469
+ let set = this.#members_of.get(cover.surface);
470
+
471
+ if (set === undefined) {
472
+ set = new Set();
473
+
474
+ this.#members_of.set(cover.surface, set);
475
+ }
476
+
477
+ set.add(member);
478
+
479
+ this.#species_take(member);
480
+
481
+ member.on_changed();
482
+ }
483
+
484
+ /**
485
+ * A mesh entity stops being ground.
486
+ *
487
+ * @param {number} entity one that is no member is left alone
488
+ */
489
+ remove_member(entity) {
490
+ const member = this.#members.get(entity);
491
+
492
+ if (member === undefined) {
493
+ return;
494
+ }
495
+
496
+ const cover = member.cover;
497
+
498
+ cover.onChanged.remove(member.on_changed);
499
+
500
+ const dataset = this.entityManager.dataset;
501
+
502
+ dataset.removeEntityEventListener(entity, TRANSFORM64_EVENT_CHANGE, member.on_world);
503
+ dataset.removeEntityEventListener(entity, SHADED_GEOMETRY_EVENT_CHANGE, member.on_changed);
504
+
505
+ this.#members.delete(entity);
506
+ this.#dirty_members.delete(member);
507
+
508
+ const set = this.#members_of.get(cover.surface);
509
+
510
+ set.delete(member);
511
+
512
+ if (set.size === 0) {
513
+ this.#members_of.delete(cover.surface);
514
+ }
515
+
516
+ this.#species_give(member);
517
+
518
+ if (member.written && this.#context !== null && this.#context === this.#graphics.scene_context(this.#scene)) {
519
+ this.#context.scene_database.get('mesh_ground_covers').remove(row_of_entity(entity));
520
+
521
+ this.#unplace(member);
522
+ }
523
+
524
+ member.written = false;
525
+ member.placed = false;
526
+
527
+ this.#dirty_surfaces.add(cover.surface);
528
+ }
529
+
530
+ /**
531
+ * @param {number} surface a surface entity
532
+ */
533
+ #queue_members_of(surface) {
534
+ const set = this.#members_of.get(surface);
535
+
536
+ if (set === undefined) {
537
+ return;
538
+ }
539
+
540
+ for (const member of set) {
541
+ this.#dirty_members.add(member);
542
+ }
543
+ }
544
+
545
+ /**
546
+ * Count a member under its cover's species, which gets an index when it is first used.
547
+ *
548
+ * @param {MemberEntry} member
549
+ */
550
+ #species_take(member) {
551
+ const species = member.cover.species;
552
+
553
+ member.species = species;
554
+
555
+ if (species === null) {
556
+ return;
557
+ }
558
+
559
+ let record = this.#species.get(species);
560
+
561
+ if (record === undefined) {
562
+ const index = this.#species_free.length > 0 ? this.#species_free.pop() : this.#species_next++;
563
+
564
+ record = {
565
+ index,
566
+ uses: 0,
567
+ // a blade's length reaches every surface it grows on
568
+ on_changed: () => {
569
+ this.#push_species(species, index);
570
+
571
+ for (const other of this.#members.values()) {
572
+ if (other.species === species) {
573
+ other.on_changed();
574
+ }
575
+ }
576
+ },
577
+ };
578
+
579
+ species.onChanged.add(record.on_changed);
580
+
581
+ this.#species.set(species, record);
582
+
583
+ this.#push_species(species, index);
584
+ }
585
+
586
+ record.uses++;
587
+ }
588
+
589
+ /**
590
+ * Write a species' record on the device, if there is a device.
591
+ *
592
+ * @param {GrassSpecies} species
593
+ * @param {number} index
594
+ */
595
+ #push_species(species, index) {
596
+ if (this.#blades !== null) {
597
+ this.#blades.set_species(index, species);
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Take a surface's `grass_surface_params` row away, if it has one.
603
+ *
604
+ * @param {number} row
605
+ * @param {GPUSceneContext} context
606
+ */
607
+ #clear_params(row, context) {
608
+ const table = context.scene_database.get('grass_surface_params');
609
+
610
+ if (table.occupancy.get(row)) {
611
+ table.remove(row);
612
+ }
613
+ }
614
+
615
+ /**
616
+ * @param {MemberEntry} member
617
+ */
618
+ #species_give(member) {
619
+ const species = member.species;
620
+
621
+ member.species = null;
622
+
623
+ if (species === null) {
624
+ return;
625
+ }
626
+
627
+ const record = this.#species.get(species);
628
+
629
+ if (--record.uses === 0) {
630
+ species.onChanged.remove(record.on_changed);
631
+
632
+ this.#species.delete(species);
633
+ this.#species_free.push(record.index);
634
+ }
635
+ }
636
+
637
+ /**
638
+ * The index a species in use is known by on the device.
639
+ *
640
+ * @param {GrassSpecies} species
641
+ * @returns {number} -1 for a species no member grows
642
+ */
643
+ species_index(species) {
644
+ const record = this.#species.get(species);
645
+
646
+ return record === undefined ? -1 : record.index;
647
+ }
648
+
649
+ #drop_context() {
650
+ if (this.#blades !== null) {
651
+ this.#blades.destroy();
652
+ this.#blades = null;
653
+ }
654
+
655
+ if (this.#gpu !== null) {
656
+ this.#gpu.destroy();
657
+ this.#gpu = null;
658
+ }
659
+
660
+ this.#context = null;
661
+
662
+ for (const entry of this.#surfaces.values()) {
663
+ entry.held = false;
664
+ }
665
+
666
+ for (const member of this.#members.values()) {
667
+ member.written = false;
668
+ member.placed = false;
669
+ }
670
+ }
671
+
672
+ /**
673
+ * A member's ground is no longer where it was marked: the tiles under it are to be baked again.
674
+ *
675
+ * @param {MemberEntry} member
676
+ */
677
+ #unplace(member) {
678
+ if (!member.placed) {
679
+ return;
680
+ }
681
+
682
+ member.placed = false;
683
+
684
+ if (this.#surfaces.has(member.cover.surface)) {
685
+ const r = member.rectangle;
686
+
687
+ this.#gpu.invalidate(row_of_entity(member.cover.surface), r[0], r[1], r[2], r[3]);
688
+ }
689
+ }
690
+
691
+ update(time_delta_seconds) {
692
+ const context = this.#graphics.scene_context(this.#scene);
693
+
694
+ if (context === null) {
695
+ // no device: before the first start, or after a stop
696
+ if (this.#context !== null) {
697
+ this.#drop_context();
698
+ }
699
+
700
+ return;
701
+ }
702
+
703
+ if (context !== this.#context) {
704
+ // a new device took every row and buffer with the old one: everything is written again
705
+ const settings = this.#gpu?.settings;
706
+ const press = this.#blades?.press;
707
+
708
+ this.#drop_context();
709
+
710
+ this.#context = context;
711
+ this.#gpu = new GPUGrassSurfaces(context.graphics);
712
+
713
+ if (settings !== undefined) {
714
+ this.#gpu.settings = settings;
715
+ }
716
+
717
+ this.#blades = new GPUGrassBlades(context.graphics, this.#gpu);
718
+
719
+ if (press !== undefined) {
720
+ this.#blades.press.enabled = press.enabled;
721
+ this.#blades.press.spacing = press.spacing;
722
+ }
723
+
724
+ for (const [species, record] of this.#species) {
725
+ this.#push_species(species, record.index);
726
+ }
727
+
728
+ for (const entity of this.#surfaces.keys()) {
729
+ this.#dirty_surfaces.add(entity);
730
+ }
731
+
732
+ for (const member of this.#members.values()) {
733
+ this.#dirty_members.add(member);
734
+ }
735
+ }
736
+
737
+ if (this.#params_spacing !== this.#gpu.settings.finest_spacing) {
738
+ // the lattices are cut from the surfels' spacing: every surface's row is written again
739
+ this.#params_spacing = this.#gpu.settings.finest_spacing;
740
+
741
+ for (const entity of this.#surfaces.keys()) {
742
+ this.#dirty_surfaces.add(entity);
743
+ }
744
+ }
745
+
746
+ for (const member of this.#dirty_members) {
747
+ if (this.#write_member(member, context)) {
748
+ this.#dirty_members.delete(member);
749
+ }
750
+ }
751
+
752
+ for (const entity of this.#dirty_surfaces) {
753
+ this.#write_surface(entity, context);
754
+ }
755
+
756
+ this.#dirty_surfaces.clear();
757
+
758
+ this.#gpu.update();
759
+ this.#blades.update(time_delta_seconds);
760
+ }
761
+
762
+ /**
763
+ * Bring a member's `mesh_ground_covers` row up to date.
764
+ *
765
+ * @param {MemberEntry} member
766
+ * @param {GPUSceneContext} context
767
+ * @returns {boolean} false when the row has to wait for the entity's mesh row
768
+ */
769
+ #write_member(member, context) {
770
+ const covers = context.scene_database.get('mesh_ground_covers');
771
+ const row = row_of_entity(member.entity);
772
+ const cover = member.cover;
773
+
774
+ if (member.species !== cover.species) {
775
+ this.#species_give(member);
776
+ this.#species_take(member);
777
+ }
778
+
779
+ if (!this.#surfaces.has(cover.surface)) {
780
+ // ground of nothing, for now
781
+ if (member.written) {
782
+ covers.remove(row);
783
+
784
+ member.written = false;
785
+ member.placed = false;
786
+ }
787
+
788
+ return true;
789
+ }
790
+
791
+ const geometry = member.shaded.geometry;
792
+
793
+ if (!context.rows.has_mesh(member.entity) || !context.geometries.has(geometry)) {
794
+ return false;
795
+ }
796
+
797
+ const species = cover.species;
798
+ const address = context.geometries.getAddress(geometry);
799
+
800
+ if (member.written && member.geometry !== geometry) {
801
+ // other ground, in the same place
802
+ this.#unplace(member);
803
+ }
804
+
805
+ covers.set(row, {
806
+ flags: MESH_GROUND_COVER_FLAG_MEMBER | (species !== null ? MESH_GROUND_COVER_FLAG_GROWS : 0),
807
+ surface: row_of_entity(cover.surface),
808
+ geometry: address,
809
+ species: species !== null ? this.#species.get(species).index : 0,
810
+ density: Math.min(1, Math.max(0, cover.density)),
811
+ });
812
+
813
+ member.written = true;
814
+ member.geometry = geometry;
815
+
816
+ // its surface's box holds it from now, and the ground under it is marked
817
+ this.#dirty_surfaces.add(cover.surface);
818
+
819
+ return true;
820
+ }
821
+
822
+ /**
823
+ * Bring a surface's record up to date, or take it away when the entity is a surface no longer or has no member.
824
+ *
825
+ * @param {number} entity
826
+ * @param {GPUSceneContext} context
827
+ */
828
+ #write_surface(entity, context) {
829
+ const entry = this.#surfaces.get(entity);
830
+ const row = row_of_entity(entity);
831
+
832
+ if (entry === undefined) {
833
+ // unlinked since it was queued, and taken away there
834
+ return;
835
+ }
836
+
837
+ if (!entry.held) {
838
+ context.rows.acquire(entity);
839
+
840
+ entry.held = true;
841
+ }
842
+
843
+ const members = this.#members_of.get(entity);
844
+
845
+ if (members === undefined) {
846
+ this.#gpu.remove(row);
847
+ this.#clear_params(row, context);
848
+
849
+ return;
850
+ }
851
+
852
+ const t = entry.transform;
853
+ const frame = scratch_frame;
854
+
855
+ // where the entity is and how it is turned; its scale is no part of the frame
856
+ m4_from_rotation_translation_scale_array(
857
+ frame,
858
+ t, TRANSFORM64_ROTATION_OFFSET,
859
+ t, TRANSFORM64_TRANSLATION_OFFSET,
860
+ ONES, 0
861
+ );
862
+
863
+ const low = scratch_low.fill(Infinity);
864
+ const high = scratch_high.fill(-Infinity);
865
+
866
+ const corners = scratch_corners;
867
+
868
+ let blade_length = 0;
869
+ let blade_length_min = Infinity;
870
+ let blade_width_min = Infinity;
871
+ let density = 0;
872
+ let cover = 0;
873
+ let held = 0;
874
+
875
+ for (const member of members) {
876
+ if (!member.written) {
877
+ // no column can find it yet
878
+ continue;
879
+ }
880
+
881
+ member_corners(corners, frame, member);
882
+
883
+ for (let i = 0; i < 24; i++) {
884
+ const axis = i % 3;
885
+ const v = corners[i];
886
+
887
+ if (v < low[axis]) {
888
+ low[axis] = v;
889
+ }
890
+
891
+ if (v > high[axis]) {
892
+ high[axis] = v;
893
+ }
894
+ }
895
+
896
+ this.#place(row, member, corners);
897
+
898
+ const species = member.cover.species;
899
+
900
+ if (species !== null) {
901
+ blade_length = Math.max(blade_length, species.blade_length);
902
+ blade_length_min = Math.min(blade_length_min, species.blade_length);
903
+ blade_width_min = Math.min(blade_width_min, species.blade_width);
904
+ density = Math.max(density, species.density);
905
+ cover = Math.max(cover, species.density * species.blade_width);
906
+ }
907
+
908
+ held++;
909
+ }
910
+
911
+ if (held === 0) {
912
+ this.#gpu.remove(row);
913
+ this.#clear_params(row, context);
914
+
915
+ return;
916
+ }
917
+
918
+ this.#gpu.set(row, low, high, blade_length);
919
+
920
+ if (density > 0) {
921
+ context.scene_database.get('grass_surface_params').set(row, {
922
+ lattice_bits: grass_surface_lattice_bits(density, this.#gpu.settings.finest_spacing),
923
+ density,
924
+ blade_length,
925
+ blade_length_min,
926
+ blade_width_min,
927
+ cover,
928
+ });
929
+ } else {
930
+ this.#clear_params(row, context);
931
+ }
932
+ }
933
+
934
+ /**
935
+ * Mark a member's ground where it is, if that is not where it was marked: the tiles under where it was
936
+ * and under where it is are baked again. A member that has moved with its surface is where it was.
937
+ *
938
+ * @param {number} surface the surface's `transforms` row
939
+ * @param {MemberEntry} member
940
+ * @param {Float64Array} corners the member's corners in the surface's frame
941
+ */
942
+ #place(surface, member, corners) {
943
+ const was = member.corners;
944
+
945
+ if (member.placed) {
946
+ let moved = false;
947
+
948
+ for (let i = 0; i < 24; i++) {
949
+ if (Math.abs(corners[i] - was[i]) > GRASS_MEMBER_MOVE_TOLERANCE) {
950
+ moved = true;
951
+
952
+ break;
953
+ }
954
+ }
955
+
956
+ if (!moved) {
957
+ return;
958
+ }
959
+
960
+ this.#unplace(member);
961
+ }
962
+
963
+ was.set(corners);
964
+
965
+ const r = member.rectangle;
966
+
967
+ r[0] = Infinity;
968
+ r[1] = Infinity;
969
+ r[2] = -Infinity;
970
+ r[3] = -Infinity;
971
+
972
+ for (let corner = 0; corner < 8; corner++) {
973
+ const x = corners[corner * 3];
974
+ const z = corners[corner * 3 + 2];
975
+
976
+ r[0] = Math.min(r[0], x);
977
+ r[1] = Math.min(r[1], z);
978
+ r[2] = Math.max(r[2], x);
979
+ r[3] = Math.max(r[3], z);
980
+ }
981
+
982
+ member.placed = true;
983
+
984
+ this.#gpu.invalidate(surface, r[0], r[1], r[2], r[3]);
985
+ }
986
+
987
+ /**
988
+ * Record the frame's asks and bakes for one view.
989
+ *
990
+ * @param {import("../../shade/renderer/extension/FrameContext.js").FrameContext} frame
991
+ */
992
+ record(frame) {
993
+ if (this.#gpu === null || frame.view.scene !== this.#context) {
994
+ return;
995
+ }
996
+
997
+ this.#gpu.graph_update({ graph: frame.graph, view: frame.view });
998
+ }
999
+
1000
+ /**
1001
+ * Record a view's blades, made and drawn into the frame's G-buffer. The blades are made here and not
1002
+ * at the frame's start because they are bent by the scene's wind, which its producer publishes at the
1003
+ * frame's start.
1004
+ *
1005
+ * The blades are pressed by the obstacles of the entity manager's {@link GPUFluidSystem}, when it has
1006
+ * one for this scene.
1007
+ *
1008
+ * @param {FrameContext} frame
1009
+ */
1010
+ draw(frame) {
1011
+ if (this.#blades === null || frame.view.scene !== this.#context) {
1012
+ return;
1013
+ }
1014
+
1015
+ const fluid = this.entityManager?.getSystem(GPUFluidSystem) ?? null;
1016
+ const forcing = fluid !== null && fluid.scene === this.#scene ? fluid.forcing : null;
1017
+
1018
+ this.#blades.graph_generate({ graph: frame.graph, view: frame.view, forcing });
1019
+ this.#blades.graph_draw(frame);
1020
+ }
1021
+ }
1022
+
1023
+ /**
1024
+ * The blades' seat in the frame: where the G-buffer is written into, ahead of what is laid over surfaces.
1025
+ */
1026
+ export class GrassBladeExtension extends RenderExtension {
1027
+ name = "grass blades";
1028
+
1029
+ phase = FramePhase.AfterGBuffer;
1030
+
1031
+ /** @type {GrassSurfaceSystem} */
1032
+ #system;
1033
+
1034
+ /**
1035
+ * @param {GrassSurfaceSystem} system
1036
+ */
1037
+ constructor(system) {
1038
+ super();
1039
+
1040
+ this.#system = system;
1041
+ }
1042
+
1043
+ record(frame) {
1044
+ this.#system.draw(frame);
1045
+ }
1046
+ }
1047
+
1048
+ /**
1049
+ * The grass surfaces' seat in the frame: at its start, where the compute that later phases read belongs.
1050
+ */
1051
+ export class GrassSurfaceExtension extends RenderExtension {
1052
+ name = "grass surfaces";
1053
+
1054
+ phase = FramePhase.FrameStart;
1055
+
1056
+ /** @type {GrassSurfaceSystem} */
1057
+ #system;
1058
+
1059
+ /**
1060
+ * @param {GrassSurfaceSystem} system
1061
+ */
1062
+ constructor(system) {
1063
+ super();
1064
+
1065
+ this.#system = system;
1066
+ }
1067
+
1068
+ record(frame) {
1069
+ this.#system.record(frame);
1070
+ }
1071
+
1072
+ enquirePipelines(enquiry) {
1073
+ GPUGrassSurfaces.enquirePipelines(enquiry);
1074
+ GPUGrassBlades.enquirePipelines(enquiry);
1075
+ }
1076
+ }