@zephyr3d/scene 0.9.26 → 0.10.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 (506) hide show
  1. package/README.md +60 -27
  2. package/dist/animation/animation_bank.js +634 -0
  3. package/dist/animation/animation_bank.js.map +1 -0
  4. package/dist/animation/animation_bank_gpu.js +256 -0
  5. package/dist/animation/animation_bank_gpu.js.map +1 -0
  6. package/dist/animation/animationset.js +146 -30
  7. package/dist/animation/animationset.js.map +1 -1
  8. package/dist/animation/cloth/gpu_cloth_component.js +406 -0
  9. package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
  10. package/dist/animation/cloth/gpu_cloth_system.js +332 -202
  11. package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
  12. package/dist/animation/gpu_animation_driver.js +289 -0
  13. package/dist/animation/gpu_animation_driver.js.map +1 -0
  14. package/dist/animation/joint_dynamics/collision.js +112 -1
  15. package/dist/animation/joint_dynamics/collision.js.map +1 -1
  16. package/dist/animation/joint_dynamics/controller.js +6 -0
  17. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  18. package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
  19. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  20. package/dist/animation/joint_dynamics/solver.js +50 -12
  21. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  22. package/dist/animation/joint_dynamics/types.js.map +1 -1
  23. package/dist/animation/skeleton.js +300 -56
  24. package/dist/animation/skeleton.js.map +1 -1
  25. package/dist/animation/skin_palette_atlas.js +252 -0
  26. package/dist/animation/skin_palette_atlas.js.map +1 -0
  27. package/dist/animation/spring/multi_chain_spring_system.js +298 -38
  28. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  29. package/dist/animation/spring/spring_chain.js +2 -0
  30. package/dist/animation/spring/spring_chain.js.map +1 -1
  31. package/dist/animation/spring/spring_collider.js +159 -21
  32. package/dist/animation/spring/spring_collider.js.map +1 -1
  33. package/dist/animation/spring/spring_particle.js +1 -0
  34. package/dist/animation/spring/spring_particle.js.map +1 -1
  35. package/dist/animation/spring/spring_solver.js +173 -0
  36. package/dist/animation/spring/spring_solver.js.map +1 -0
  37. package/dist/animation/spring/spring_system.js +249 -24
  38. package/dist/animation/spring/spring_system.js.map +1 -1
  39. package/dist/animation/spring_modifier.js +32 -1
  40. package/dist/animation/spring_modifier.js.map +1 -1
  41. package/dist/app/app.js +26 -0
  42. package/dist/app/app.js.map +1 -1
  43. package/dist/app/engine.js +86 -15
  44. package/dist/app/engine.js.map +1 -1
  45. package/dist/app/gamepad.js +191 -0
  46. package/dist/app/gamepad.js.map +1 -0
  47. package/dist/app/inputactions.js +271 -0
  48. package/dist/app/inputactions.js.map +1 -0
  49. package/dist/app/inputmgr.js +331 -22
  50. package/dist/app/inputmgr.js.map +1 -1
  51. package/dist/app/loading.js +138 -0
  52. package/dist/app/loading.js.map +1 -0
  53. package/dist/app/pipelinecache.js +209 -0
  54. package/dist/app/pipelinecache.js.map +1 -0
  55. package/dist/app/runtimescript.js +25 -0
  56. package/dist/app/runtimescript.js.map +1 -1
  57. package/dist/app/scriptingsystem.js +54 -0
  58. package/dist/app/scriptingsystem.js.map +1 -1
  59. package/dist/app/scriptregistry.js +7 -2
  60. package/dist/app/scriptregistry.js.map +1 -1
  61. package/dist/asset/asset_manifest.js +59 -0
  62. package/dist/asset/asset_manifest.js.map +1 -0
  63. package/dist/asset/asset_preload.js +258 -0
  64. package/dist/asset/asset_preload.js.map +1 -0
  65. package/dist/asset/assetmanager.js +117 -65
  66. package/dist/asset/assetmanager.js.map +1 -1
  67. package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
  68. package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  69. package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  70. package/dist/asset/loaders/ktx2/basis_location.js +17 -0
  71. package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
  72. package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
  73. package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
  74. package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
  75. package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
  76. package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
  77. package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
  78. package/dist/asset/mesh_lod_builder.js +104 -0
  79. package/dist/asset/mesh_lod_builder.js.map +1 -0
  80. package/dist/asset/mesh_settings.js +111 -0
  81. package/dist/asset/mesh_settings.js.map +1 -0
  82. package/dist/asset/meshopt_decoder.js +18 -0
  83. package/dist/asset/meshopt_decoder.js.map +1 -0
  84. package/dist/asset/meshopt_simplifier.js +26 -0
  85. package/dist/asset/meshopt_simplifier.js.map +1 -0
  86. package/dist/asset/model.js +177 -3
  87. package/dist/asset/model.js.map +1 -1
  88. package/dist/asset/texture_settings.js +231 -0
  89. package/dist/asset/texture_settings.js.map +1 -0
  90. package/dist/asset/zmsh_binary.js +131 -0
  91. package/dist/asset/zmsh_binary.js.map +1 -0
  92. package/dist/asset/zsdf_binary.js +111 -0
  93. package/dist/asset/zsdf_binary.js.map +1 -0
  94. package/dist/blitter/bilateralblur.js +1 -2
  95. package/dist/blitter/bilateralblur.js.map +1 -1
  96. package/dist/blitter/gaussianblur.js +1 -6
  97. package/dist/blitter/gaussianblur.js.map +1 -1
  98. package/dist/camera/base.js +31 -10
  99. package/dist/camera/base.js.map +1 -1
  100. package/dist/camera/camera.js +178 -200
  101. package/dist/camera/camera.js.map +1 -1
  102. package/dist/camera/follow.js +342 -0
  103. package/dist/camera/follow.js.map +1 -0
  104. package/dist/camera/fps.js +9 -7
  105. package/dist/camera/fps.js.map +1 -1
  106. package/dist/camera/orbit.js +46 -19
  107. package/dist/camera/orbit.js.map +1 -1
  108. package/dist/index.d.ts +30475 -19865
  109. package/dist/index.js +67 -12
  110. package/dist/index.js.map +1 -1
  111. package/dist/material/blinn.js +3 -0
  112. package/dist/material/blinn.js.map +1 -1
  113. package/dist/material/eye.js +1 -1
  114. package/dist/material/eye.js.map +1 -1
  115. package/dist/material/hair.js +2 -3
  116. package/dist/material/hair.js.map +1 -1
  117. package/dist/material/hairstrand.js +235 -235
  118. package/dist/material/hairstrand.js.map +1 -1
  119. package/dist/material/lambert.js +4 -1
  120. package/dist/material/lambert.js.map +1 -1
  121. package/dist/material/material.js +362 -300
  122. package/dist/material/material.js.map +1 -1
  123. package/dist/material/meshmaterial.js +118 -45
  124. package/dist/material/meshmaterial.js.map +1 -1
  125. package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
  126. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  127. package/dist/material/mixins/lightmodel/lambert.js +1 -1
  128. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  129. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
  130. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  131. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
  132. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  133. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
  134. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  135. package/dist/material/mixins/lit.js +223 -83
  136. package/dist/material/mixins/lit.js.map +1 -1
  137. package/dist/material/mixins/pbr/common.js +31 -206
  138. package/dist/material/mixins/pbr/common.js.map +1 -1
  139. package/dist/material/mtoon.js +1 -1
  140. package/dist/material/mtoon.js.map +1 -1
  141. package/dist/material/pbrmr.js +3 -0
  142. package/dist/material/pbrmr.js.map +1 -1
  143. package/dist/material/pbrsg.js +3 -0
  144. package/dist/material/pbrsg.js.map +1 -1
  145. package/dist/material/sdf_debug.js +78 -0
  146. package/dist/material/sdf_debug.js.map +1 -0
  147. package/dist/material/shader/helper.js +733 -190
  148. package/dist/material/shader/helper.js.map +1 -1
  149. package/dist/material/shader/terrain_blend.js +204 -43
  150. package/dist/material/shader/terrain_blend.js.map +1 -1
  151. package/dist/material/skin.js +331 -297
  152. package/dist/material/skin.js.map +1 -1
  153. package/dist/material/skinprofile.js +1058 -0
  154. package/dist/material/skinprofile.js.map +1 -0
  155. package/dist/material/sss.js +353 -0
  156. package/dist/material/sss.js.map +1 -0
  157. package/dist/material/sss_kernel.js +265 -0
  158. package/dist/material/sss_kernel.js.map +1 -0
  159. package/dist/material/sssprofile.js +879 -0
  160. package/dist/material/sssprofile.js.map +1 -0
  161. package/dist/material/subsurfaceprofile.js +4 -8
  162. package/dist/material/subsurfaceprofile.js.map +1 -1
  163. package/dist/material/terrain-cm.js +464 -188
  164. package/dist/material/terrain-cm.js.map +1 -1
  165. package/dist/material/unlit.js +3 -0
  166. package/dist/material/unlit.js.map +1 -1
  167. package/dist/material/water.js +2009 -201
  168. package/dist/material/water.js.map +1 -1
  169. package/dist/node_modules/meshoptimizer/LICENSE +21 -0
  170. package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
  171. package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
  172. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
  173. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
  174. package/dist/physics/character.js +226 -0
  175. package/dist/physics/character.js.map +1 -0
  176. package/dist/physics/collider.js +209 -0
  177. package/dist/physics/collider.js.map +1 -0
  178. package/dist/physics/events.js +79 -0
  179. package/dist/physics/events.js.map +1 -0
  180. package/dist/physics/joint.js +272 -0
  181. package/dist/physics/joint.js.map +1 -0
  182. package/dist/physics/node_physics.js +234 -0
  183. package/dist/physics/node_physics.js.map +1 -0
  184. package/dist/physics/part.js +91 -0
  185. package/dist/physics/part.js.map +1 -0
  186. package/dist/physics/rigid_body.js +379 -0
  187. package/dist/physics/rigid_body.js.map +1 -0
  188. package/dist/physics/vehicle.js +335 -0
  189. package/dist/physics/vehicle.js.map +1 -0
  190. package/dist/physics/world.js +25 -0
  191. package/dist/physics/world.js.map +1 -0
  192. package/dist/posteffect/bloom.js +49 -14
  193. package/dist/posteffect/bloom.js.map +1 -1
  194. package/dist/posteffect/motionblur.js +668 -63
  195. package/dist/posteffect/motionblur.js.map +1 -1
  196. package/dist/posteffect/posteffect.js +12 -1
  197. package/dist/posteffect/posteffect.js.map +1 -1
  198. package/dist/posteffect/postsss.js +572 -0
  199. package/dist/posteffect/postsss.js.map +1 -0
  200. package/dist/posteffect/sao.js +48 -50
  201. package/dist/posteffect/sao.js.map +1 -1
  202. package/dist/posteffect/skinsss.js +605 -355
  203. package/dist/posteffect/skinsss.js.map +1 -1
  204. package/dist/posteffect/ssgi.js +91 -162
  205. package/dist/posteffect/ssgi.js.map +1 -1
  206. package/dist/posteffect/ssr.js +3 -11
  207. package/dist/posteffect/ssr.js.map +1 -1
  208. package/dist/posteffect/sss.js +34 -15
  209. package/dist/posteffect/sss.js.map +1 -1
  210. package/dist/posteffect/taa.js +1 -0
  211. package/dist/posteffect/taa.js.map +1 -1
  212. package/dist/render/abuffer_oit.js +40 -39
  213. package/dist/render/abuffer_oit.js.map +1 -1
  214. package/dist/render/clipmap.js +328 -62
  215. package/dist/render/clipmap.js.map +1 -1
  216. package/dist/render/cluster_light.js +958 -224
  217. package/dist/render/cluster_light.js.map +1 -1
  218. package/dist/render/cull_visitor.js +26 -5
  219. package/dist/render/cull_visitor.js.map +1 -1
  220. package/dist/render/debug_views.js +37 -0
  221. package/dist/render/debug_views.js.map +1 -0
  222. package/dist/render/depthpass.js +5 -1
  223. package/dist/render/depthpass.js.map +1 -1
  224. package/dist/render/drawable_mixin.js +53 -6
  225. package/dist/render/drawable_mixin.js.map +1 -1
  226. package/dist/render/dualdepthpeeling_oit.js +53 -53
  227. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  228. package/dist/render/envlight.js +196 -113
  229. package/dist/render/envlight.js.map +1 -1
  230. package/dist/render/fbm_wavegenerator.js +8 -13
  231. package/dist/render/fbm_wavegenerator.js.map +1 -1
  232. package/dist/render/fft_wavegenerator.js +268 -136
  233. package/dist/render/fft_wavegenerator.js.map +1 -1
  234. package/dist/render/gerstner_wavegenerator.js +113 -28
  235. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  236. package/dist/render/gi/probe_volume.js +1334 -0
  237. package/dist/render/gi/probe_volume.js.map +1 -0
  238. package/dist/render/gi/scene_gi.js +282 -0
  239. package/dist/render/gi/scene_gi.js.map +1 -0
  240. package/dist/render/gi/voxel_atlas.js +198 -0
  241. package/dist/render/gi/voxel_atlas.js.map +1 -0
  242. package/dist/render/gi/voxel_lighting.js +652 -0
  243. package/dist/render/gi/voxel_lighting.js.map +1 -0
  244. package/dist/render/gi/voxel_scene.js +642 -0
  245. package/dist/render/gi/voxel_scene.js.map +1 -0
  246. package/dist/render/gi/voxelize_pass.js +223 -0
  247. package/dist/render/gi/voxelize_pass.js.map +1 -0
  248. package/dist/render/globalbindgroup_allocator.js +3 -2
  249. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  250. package/dist/render/gpu_picking.js +9 -36
  251. package/dist/render/gpu_picking.js.map +1 -1
  252. package/dist/render/hzb.js +80 -22
  253. package/dist/render/hzb.js.map +1 -1
  254. package/dist/render/instance_culling.js +499 -0
  255. package/dist/render/instance_culling.js.map +1 -0
  256. package/dist/render/lightpass.js +23 -9
  257. package/dist/render/lightpass.js.map +1 -1
  258. package/dist/render/lod.js +46 -0
  259. package/dist/render/lod.js.map +1 -0
  260. package/dist/render/oit.js +11 -11
  261. package/dist/render/oit.js.map +1 -1
  262. package/dist/render/primitive.js +301 -9
  263. package/dist/render/primitive.js.map +1 -1
  264. package/dist/render/primitive_readback.js +209 -0
  265. package/dist/render/primitive_readback.js.map +1 -0
  266. package/dist/render/render_queue.js +198 -11
  267. package/dist/render/render_queue.js.map +1 -1
  268. package/dist/render/renderer.js +14 -7
  269. package/dist/render/renderer.js.map +1 -1
  270. package/dist/render/rendergraph/blackboard.js +15 -1
  271. package/dist/render/rendergraph/blackboard.js.map +1 -1
  272. package/dist/render/rendergraph/executor.js +136 -0
  273. package/dist/render/rendergraph/executor.js.map +1 -1
  274. package/dist/render/rendergraph/forward_plus_builder.js +585 -59
  275. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  276. package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
  277. package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
  278. package/dist/render/rendergraph/history_resources.js +8 -1
  279. package/dist/render/rendergraph/history_resources.js.map +1 -1
  280. package/dist/render/rendergraph/scene_render_context.js +31 -2
  281. package/dist/render/rendergraph/scene_render_context.js.map +1 -1
  282. package/dist/render/rendergraph/types.js.map +1 -1
  283. package/dist/render/renderpass.js +28 -5
  284. package/dist/render/renderpass.js.map +1 -1
  285. package/dist/render/sdf/box_allocator.js +98 -0
  286. package/dist/render/sdf/box_allocator.js.map +1 -0
  287. package/dist/render/sdf/global_sdf.js +865 -0
  288. package/dist/render/sdf/global_sdf.js.map +1 -0
  289. package/dist/render/sdf/scene_sdf.js +562 -0
  290. package/dist/render/sdf/scene_sdf.js.map +1 -0
  291. package/dist/render/sdf/sdf_atlas.js +227 -0
  292. package/dist/render/sdf/sdf_atlas.js.map +1 -0
  293. package/dist/render/sdf/sdf_programs.js +42 -0
  294. package/dist/render/sdf/sdf_programs.js.map +1 -0
  295. package/dist/render/sdf/sdf_view.js +199 -0
  296. package/dist/render/sdf/sdf_view.js.map +1 -0
  297. package/dist/render/sdf/waterline.js +610 -0
  298. package/dist/render/sdf/waterline.js.map +1 -0
  299. package/dist/render/shadow_mask_pass.js +142 -25
  300. package/dist/render/shadow_mask_pass.js.map +1 -1
  301. package/dist/render/shore_wave_generator.js +1193 -0
  302. package/dist/render/shore_wave_generator.js.map +1 -0
  303. package/dist/render/sky.js +516 -86
  304. package/dist/render/sky.js.map +1 -1
  305. package/dist/render/transmission_thickness_pass.js +477 -0
  306. package/dist/render/transmission_thickness_pass.js.map +1 -0
  307. package/dist/render/underwater.js +524 -0
  308. package/dist/render/underwater.js.map +1 -0
  309. package/dist/render/virtualtexture/virtual_texture.js +1214 -0
  310. package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
  311. package/dist/render/volumetric_cloud/cloud_common.js +472 -0
  312. package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
  313. package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
  314. package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
  315. package/dist/render/volumetric_cloud/debug_view.js +131 -0
  316. package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
  317. package/dist/render/volumetric_cloud/resources.js +380 -0
  318. package/dist/render/volumetric_cloud/resources.js.map +1 -0
  319. package/dist/render/volumetric_cloud/sky_cache.js +380 -0
  320. package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
  321. package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
  322. package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
  323. package/dist/render/water_caustics.js +1195 -0
  324. package/dist/render/water_caustics.js.map +1 -0
  325. package/dist/render/water_foam.js +627 -0
  326. package/dist/render/water_foam.js.map +1 -0
  327. package/dist/render/water_interaction.js +1795 -0
  328. package/dist/render/water_interaction.js.map +1 -0
  329. package/dist/render/weightedblended_oit.js +36 -36
  330. package/dist/render/weightedblended_oit.js.map +1 -1
  331. package/dist/scene/basesprite.js +9 -4
  332. package/dist/scene/basesprite.js.map +1 -1
  333. package/dist/scene/batchgroup.js +108 -12
  334. package/dist/scene/batchgroup.js.map +1 -1
  335. package/dist/scene/environment.js +28 -7
  336. package/dist/scene/environment.js.map +1 -1
  337. package/dist/scene/foliage.js +1020 -0
  338. package/dist/scene/foliage.js.map +1 -0
  339. package/dist/scene/foliage_buckets.js +147 -0
  340. package/dist/scene/foliage_buckets.js.map +1 -0
  341. package/dist/scene/foliage_mask.js +358 -0
  342. package/dist/scene/foliage_mask.js.map +1 -0
  343. package/dist/scene/hair.js +15 -4
  344. package/dist/scene/hair.js.map +1 -1
  345. package/dist/scene/light.js +179 -13
  346. package/dist/scene/light.js.map +1 -1
  347. package/dist/scene/mesh.js +327 -45
  348. package/dist/scene/mesh.js.map +1 -1
  349. package/dist/scene/meshdrawable.js +6 -0
  350. package/dist/scene/meshdrawable.js.map +1 -1
  351. package/dist/scene/msdftext.js +9 -3
  352. package/dist/scene/msdftext.js.map +1 -1
  353. package/dist/scene/msdftextsprite.js +9 -3
  354. package/dist/scene/msdftextsprite.js.map +1 -1
  355. package/dist/scene/particlesys.js +8 -2
  356. package/dist/scene/particlesys.js.map +1 -1
  357. package/dist/scene/physics_settings.js +157 -0
  358. package/dist/scene/physics_settings.js.map +1 -0
  359. package/dist/scene/scene.js +190 -44
  360. package/dist/scene/scene.js.map +1 -1
  361. package/dist/scene/scene_node.js +147 -6
  362. package/dist/scene/scene_node.js.map +1 -1
  363. package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
  364. package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
  365. package/dist/scene/terrain-cm/grass.js +881 -42
  366. package/dist/scene/terrain-cm/grass.js.map +1 -1
  367. package/dist/scene/terrain-cm/grass_gpu.js +929 -0
  368. package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
  369. package/dist/scene/terrain-cm/grassmaterial.js +180 -9
  370. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  371. package/dist/scene/terrain-cm/terrain-cm.js +465 -36
  372. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  373. package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
  374. package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
  375. package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
  376. package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
  377. package/dist/scene/update_queue.js +54 -0
  378. package/dist/scene/update_queue.js.map +1 -0
  379. package/dist/scene/water.js +997 -96
  380. package/dist/scene/water.js.map +1 -1
  381. package/dist/scene/water_surface_sampler.js +324 -0
  382. package/dist/scene/water_surface_sampler.js.map +1 -0
  383. package/dist/scene/wind.js +317 -0
  384. package/dist/scene/wind.js.map +1 -0
  385. package/dist/shaders/atmosphere.js +388 -166
  386. package/dist/shaders/atmosphere.js.map +1 -1
  387. package/dist/shaders/fog.js +100 -32
  388. package/dist/shaders/fog.js.map +1 -1
  389. package/dist/shaders/gi.js +365 -0
  390. package/dist/shaders/gi.js.map +1 -0
  391. package/dist/shaders/ltc_rect.js +202 -0
  392. package/dist/shaders/ltc_rect.js.map +1 -0
  393. package/dist/shaders/misc.js +1 -10
  394. package/dist/shaders/misc.js.map +1 -1
  395. package/dist/shaders/noise.js +5 -1
  396. package/dist/shaders/noise.js.map +1 -1
  397. package/dist/shaders/probes.js +309 -0
  398. package/dist/shaders/probes.js.map +1 -0
  399. package/dist/shaders/sdf.js +537 -0
  400. package/dist/shaders/sdf.js.map +1 -0
  401. package/dist/shaders/shadow.js +104 -70
  402. package/dist/shaders/shadow.js.map +1 -1
  403. package/dist/shaders/skin_brdf.js +243 -0
  404. package/dist/shaders/skin_brdf.js.map +1 -0
  405. package/dist/shaders/ssr.js +6 -159
  406. package/dist/shaders/ssr.js.map +1 -1
  407. package/dist/shaders/temporal.js +38 -27
  408. package/dist/shaders/temporal.js.map +1 -1
  409. package/dist/shaders/water.js +40 -87
  410. package/dist/shaders/water.js.map +1 -1
  411. package/dist/shaders/water_medium.js +42 -0
  412. package/dist/shaders/water_medium.js.map +1 -0
  413. package/dist/shadow/dom.js +110 -19
  414. package/dist/shadow/dom.js.map +1 -1
  415. package/dist/shadow/esm.js +3 -3
  416. package/dist/shadow/esm.js.map +1 -1
  417. package/dist/shadow/pcf_opt.js +15 -48
  418. package/dist/shadow/pcf_opt.js.map +1 -1
  419. package/dist/shadow/pcf_pd.js +15 -53
  420. package/dist/shadow/pcf_pd.js.map +1 -1
  421. package/dist/shadow/pcss.js +34 -9
  422. package/dist/shadow/pcss.js.map +1 -1
  423. package/dist/shadow/shadow_impl.js +3 -8
  424. package/dist/shadow/shadow_impl.js.map +1 -1
  425. package/dist/shadow/shadow_region.js +87 -49
  426. package/dist/shadow/shadow_region.js.map +1 -1
  427. package/dist/shadow/shadowmapper.js +93 -53
  428. package/dist/shadow/shadowmapper.js.map +1 -1
  429. package/dist/shadow/ssm.js +18 -72
  430. package/dist/shadow/ssm.js.map +1 -1
  431. package/dist/shadow/vsm.js +3 -3
  432. package/dist/shadow/vsm.js.map +1 -1
  433. package/dist/shapes/shape.js +44 -1
  434. package/dist/shapes/shape.js.map +1 -1
  435. package/dist/utility/buoyancy.js +287 -0
  436. package/dist/utility/buoyancy.js.map +1 -0
  437. package/dist/utility/physical.js +10 -1
  438. package/dist/utility/physical.js.map +1 -1
  439. package/dist/utility/pmrem.js +17 -43
  440. package/dist/utility/pmrem.js.map +1 -1
  441. package/dist/utility/sdf/bake.js +435 -0
  442. package/dist/utility/sdf/bake.js.map +1 -0
  443. package/dist/utility/sdf/bvh.js +576 -0
  444. package/dist/utility/sdf/bvh.js.map +1 -0
  445. package/dist/utility/serialization/manager.js +36 -6
  446. package/dist/utility/serialization/manager.js.map +1 -1
  447. package/dist/utility/serialization/scene/animation.js +77 -7
  448. package/dist/utility/serialization/scene/animation.js.map +1 -1
  449. package/dist/utility/serialization/scene/batch.js +11 -4
  450. package/dist/utility/serialization/scene/batch.js.map +1 -1
  451. package/dist/utility/serialization/scene/camera.js +140 -279
  452. package/dist/utility/serialization/scene/camera.js.map +1 -1
  453. package/dist/utility/serialization/scene/cloth.js +25 -0
  454. package/dist/utility/serialization/scene/cloth.js.map +1 -0
  455. package/dist/utility/serialization/scene/common.js +14 -6
  456. package/dist/utility/serialization/scene/common.js.map +1 -1
  457. package/dist/utility/serialization/scene/foliage.js +821 -0
  458. package/dist/utility/serialization/scene/foliage.js.map +1 -0
  459. package/dist/utility/serialization/scene/hair.js +11 -4
  460. package/dist/utility/serialization/scene/hair.js.map +1 -1
  461. package/dist/utility/serialization/scene/light.js +115 -7
  462. package/dist/utility/serialization/scene/light.js.map +1 -1
  463. package/dist/utility/serialization/scene/material.js +388 -220
  464. package/dist/utility/serialization/scene/material.js.map +1 -1
  465. package/dist/utility/serialization/scene/mesh.js +69 -4
  466. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  467. package/dist/utility/serialization/scene/node.js +140 -5
  468. package/dist/utility/serialization/scene/node.js.map +1 -1
  469. package/dist/utility/serialization/scene/node_physics.js +1318 -0
  470. package/dist/utility/serialization/scene/node_physics.js.map +1 -0
  471. package/dist/utility/serialization/scene/particle.js +11 -4
  472. package/dist/utility/serialization/scene/particle.js.map +1 -1
  473. package/dist/utility/serialization/scene/physics.js +120 -0
  474. package/dist/utility/serialization/scene/physics.js.map +1 -0
  475. package/dist/utility/serialization/scene/primitive.js +124 -10
  476. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  477. package/dist/utility/serialization/scene/scene.js +1687 -51
  478. package/dist/utility/serialization/scene/scene.js.map +1 -1
  479. package/dist/utility/serialization/scene/sprite.js +11 -4
  480. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  481. package/dist/utility/serialization/scene/terrain.js +163 -33
  482. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  483. package/dist/utility/serialization/scene/text.js +11 -4
  484. package/dist/utility/serialization/scene/text.js.map +1 -1
  485. package/dist/utility/serialization/scene/water.js +2473 -114
  486. package/dist/utility/serialization/scene/water.js.map +1 -1
  487. package/dist/utility/serialization/types.js.map +1 -1
  488. package/dist/utility/shprojector.js +97 -109
  489. package/dist/utility/shprojector.js.map +1 -1
  490. package/dist/utility/textures/foam.js +170 -0
  491. package/dist/utility/textures/foam.js.map +1 -0
  492. package/dist/utility/textures/ggxlut.js +17 -43
  493. package/dist/utility/textures/ggxlut.js.map +1 -1
  494. package/dist/utility/textures/ltcdata.js +4 -4
  495. package/dist/utility/textures/ltcdata.js.map +1 -1
  496. package/dist/utility/textures/ltclut.js +16 -6
  497. package/dist/utility/textures/ltclut.js.map +1 -1
  498. package/dist/utility/unsupported.js +24 -0
  499. package/dist/utility/unsupported.js.map +1 -0
  500. package/dist/values.js +13 -1
  501. package/dist/values.js.map +1 -1
  502. package/package.json +25 -12
  503. package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
  504. package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  505. package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  506. package/src/asset/meshoptimizer/LICENSE +21 -0
@@ -0,0 +1,1193 @@
1
+ import { Disposable, Vector4, float2half } from '@zephyr3d/base';
2
+ import { FOAM_WAVE_GAIN } from './water_foam.js';
3
+ import { getDevice } from '../app/api.js';
4
+ import { fetchSampler } from '../utility/misc.js';
5
+ import { valueNoise, hash11 } from '../shaders/noise.js';
6
+
7
+ /** Texels of the inverse table along y = x - b sin x over one period, and along b. */ const INVERSE_SAMPLES = 256;
8
+ const INVERSE_SHARPNESS = 32;
9
+ /** Largest b the inverse table holds; at 1 the crest becomes a cusp. */ const SHARPNESS_MAX = 0.95;
10
+ /** Wave sets: noise per metre along the shore, its drift in cycles per wave, and how deep the sets cut. */ const SET_SCALE = 0.02;
11
+ const SET_DRIFT = 0.02;
12
+ const SET_DEPTH = 0.6;
13
+ /**
14
+ * Irregular waves. A point d out sees at time t the crest that left the outer
15
+ * edge tau(d) earlier, so the phase is Psi(t / T + tau(d) / T), with
16
+ * Psi(s) = s + J(s, foot): noise J in s gives each wave its own period, and
17
+ * noise along the shore (at the foot of each waterline point) its own bend.
18
+ * Psi must keep rising (1 + dJ/ds > 0) or crests would pass each other; at full
19
+ * irregularity dJ/ds is at most IRREGULAR_AMPLITUDE * IRREGULAR_RATE * 1.5 = 0.56.
20
+ *
21
+ * IRREGULAR_RATE: noise cycles per wave; IRREGULAR_SHORE_SCALE: noise cycles per
22
+ * metre along the shore; IRREGULAR_AMPLITUDE: wave cycles J spans at full irregularity.
23
+ */ const IRREGULAR_RATE = 0.15;
24
+ const IRREGULAR_SHORE_SCALE = 0.035;
25
+ const IRREGULAR_AMPLITUDE = 2.5;
26
+ /** Step in wave cycles over which dJ/ds is taken. */ const IRREGULAR_STEP = 0.05;
27
+ /**
28
+ * Crest segments: each wave rises and falls along the shore by its own noise.
29
+ * Noise per metre along the shore, and how deep the gaps cut.
30
+ */ const SEGMENT_SCALE = 0.06;
31
+ const SEGMENT_DEPTH = 0.85;
32
+ /**
33
+ * Band limit of the displacement against the mesh, after Crest's per-LOD
34
+ * wavelength filtering (Bowles, "Crest: Novel ocean rendering techniques in an
35
+ * open source framework", SIGGRAPH 2017): as mesh vertices per crest spacing
36
+ * fall from SHARP_SAMPLES_FULL to SHARP_SAMPLES_MIN the sharpened profile eases
37
+ * back to a sine; from AMP_SAMPLES_FULL to AMP_SAMPLES_MIN the wave flattens.
38
+ * The fragment stage keeps the full profile in its normals.
39
+ */ const SHARP_SAMPLES_MIN = 4;
40
+ const SHARP_SAMPLES_FULL = 12;
41
+ const AMP_SAMPLES_MIN = 2;
42
+ const AMP_SAMPLES_FULL = 4;
43
+ /**
44
+ * The swash, after the ballistic run-up of Shen & Meyer ("Climb of a bore on a
45
+ * beach", JFM 16, 1963) in the form of Peregrine & Williams ("Swash overtopping
46
+ * a truncated plane beach", JFM 440, 2001): the tip leaves the waterline at u0
47
+ * and slows under g tan(beta), s_f = u0 t - g tan(beta) t^2 / 2, with depth
48
+ * h = (s_f - s)^2 / (36 g t^2) behind it. u0 = sqrt(2 g height); the run up and
49
+ * back lasts 2 u0 / (g tan(beta)), at most SWASH_DURATION_MAX wave periods.
50
+ *
51
+ * Water velocity u = ds_f/dt + (2/3)(s - s_f) / t, from continuity
52
+ * h_t + (u h)_s = 0.
53
+ */ const SWASH_GRAVITY = 9.81;
54
+ const SWASH_DURATION_MAX = 0.8;
55
+ /** Metres inland over which the crest collapsing at the waterline sinks exponentially into the swash. */ const SWASH_COLLAPSE = 0.6;
56
+ /**
57
+ * Depth share of the zone (distance over zone width) past which the waves stop
58
+ * shortening, so crests keep at least sqrt of this times the wavelength apart.
59
+ */ const MIN_COMPRESSION = 0.15;
60
+ /**
61
+ * Range of the waterline model's B (vertical over horizontal field slope,
62
+ * about 1 / tan of the bed angle) over which the waves fade in: none against
63
+ * anything steeper than about 35 degrees, full on beaches gentler than 15.
64
+ */ const SHELVING_MIN = 1.5;
65
+ const SHELVING_FULL = 4;
66
+ /**
67
+ * Within FOAM_SLIP_BAND metres of a waterline too steep for surf (rocks, cliffs,
68
+ * piers; see SHELVING_MIN) foam is carried out along the wall's normal at the
69
+ * wall foam's drift speed, blending into the water's own velocity over that
70
+ * distance, so it rings the wall evenly whatever the wind. Not on a beach.
71
+ */ const FOAM_SLIP_BAND = 2;
72
+ /**
73
+ * FOAM_SETTLE: the shallower the water, the less of the foam's drift it
74
+ * carries, by a settling coefficient k of the depth under the displaced
75
+ * surface, 1 at or below the settle depth (and on dry sand), 0 at and beyond
76
+ * the drift depth, smoothly between; the foam moves at (1 - k) of its
77
+ * velocity. One field and one fade over the open water, the shallows, the
78
+ * swash and the sand: the depth is continuous across them, so the foam is.
79
+ * Settled foam also fades slower, at a share of the fade rate that goes from 1
80
+ * to the settled foam fade with k. An empirical model with the two depths and
81
+ * that share as its parameters, not from a paper.
82
+ */ /**
83
+ * The near-shore zone, see nearShore: still water shallower than the shore
84
+ * wave height over NEAR_SHORE_BREAKER (a wave breaks once its height reaches
85
+ * about 0.78 of the depth), blended into the open water over a further
86
+ * NEAR_SHORE_BLEND of that depth. There the surf's foam is
87
+ * NEAR_SHORE_FOAM_EDGE just behind a crest at the threshold, rising to
88
+ * NEAR_SHORE_FOAM_SHORE at the waterline and on the wet beach.
89
+ */ const NEAR_SHORE_BREAKER = 0.78;
90
+ const NEAR_SHORE_BLEND = 0.5;
91
+ const NEAR_SHORE_FOAM_EDGE = 0.6;
92
+ const NEAR_SHORE_FOAM_SHORE = 0.85;
93
+ /**
94
+ * SURF_FOAM: within the near-shore zone each broken crest comes in as a bore
95
+ * with foam on its front, which fades behind it, exp(-age / duration), age
96
+ * being the seconds since the crest passed. Inland the swash runs on the
97
+ * waterline's clock, so the bore's foam goes on up the beach with it and
98
+ * fades as it runs up and drains: the foam surges up and back with each wave
99
+ * rather than lying along the shore, after the foam that rises and falls with
100
+ * the waves in Yang Chao's "near-shore waves" (zhuanlan.zhihu.com/p/495906664).
101
+ * Over the last SURF_FOAM_FRONT of a wave before its crest the foam rises,
102
+ * softening the bore's front. A broken wave is white water whatever its
103
+ * height, which sets how far it runs rather than how white it is: the foam is
104
+ * full from SURF_FOAM_FULL of the full height up, the wave sets and segments
105
+ * that weaken a wave below that only thinning it towards nothing.
106
+ */ const SURF_FOAM_FRONT = 0.04;
107
+ const SURF_FOAM_FULL = 0.25;
108
+ /**
109
+ * Seconds over which the water's speed at a wall is averaged before it makes
110
+ * foam there, about a wave period: see the wall foam step.
111
+ */ const WALL_SPEED_AVERAGE = 3;
112
+ /**
113
+ * Metres past FOAM_SLIP_BAND within which a cell's water is followed to where
114
+ * the waves carry it, about the largest horizontal displacement of choppy waves.
115
+ */ const WALL_DISPLACEMENT_REACH = 6;
116
+ /**
117
+ * FOAM_WALL: foam the water makes dashing against a wall, after the trapped-air
118
+ * potential of Ihmsen, Akinci, Akinci & Teschner, "Unified Spray, Foam and
119
+ * Bubbles for Particle-Based Fluids" (CGI 2012), eq. (1)-(3) and (8):
120
+ * v_diff = sum |v_ij| (1 - v_ij . x_ij) W(x_ij, h), W = 1 - |x| / h, clamped by
121
+ * Phi(I, tmin, tmax) = (min(I, tmax) - min(I, tmin)) / (tmax - tmin), and foam
122
+ * made at a rate k I_k I_ta with I_k = Phi(E_k).
123
+ *
124
+ * Here the neighbour is the wall at rest, h = FOAM_SLIP_BAND along its normal n:
125
+ * v_diff = (|v| - v . n) W, averaged over a wave's orbit to |v| W so every face
126
+ * of a wall foams alike. |v| is the surface's rate of rise, which also works for
127
+ * generators that only displace vertically (FBM). The wall-at-rest neighbour,
128
+ * the orbit average, the thresholds, the per-unit-mass kinetic energy and the
129
+ * weighting by wall steepness are adaptations, not from the paper.
130
+ *
131
+ * The foam goes to a layer of its own (see FoamFieldSource), shown whole with
132
+ * its strength as opacity rather than cut from the foam texture. It is made at
133
+ * FOAM_WAVE_GAIN regardless of the strength, so it saturates against the wall.
134
+ */ /** Largest B anything here is worked out with, a bed at about 3 degrees. */ const B_MAX = 20;
135
+ /** Distance in metres out from the waterline over which the wrapped generator's chop dies away. */ const CHOP_FADE = 3;
136
+ /**
137
+ * The water's edge on the beach is cut per pixel (see calcSurfaceCoverage)
138
+ * where the water meets the bed height of the waterline field; near the edge
139
+ * the mesh is held SWASH_CLEARANCE metres over the bed. That applies where the
140
+ * bed is at most SWASH_LIFT_MARGIN above the highest water, up to
141
+ * SWASH_EDGE_EXTEND metres past the farthest the water reaches; the edge is
142
+ * softened over SWASH_EDGE_SOFT metres.
143
+ */ const SWASH_CLEARANCE = 0.06;
144
+ const SWASH_LIFT_MARGIN = 0.1;
145
+ const SWASH_EDGE_EXTEND = 2;
146
+ const SWASH_EDGE_SOFT = 0.08;
147
+ /**
148
+ * Share of the grid, from each edge inwards, over which the waves fade out,
149
+ * so the grid following the camera has no visible border.
150
+ */ const GRID_EDGE_FADE = 0.15;
151
+ /**
152
+ * Levels of the shore field the waves read, at most. A level's distance to
153
+ * the waterline is exact only while it is shorter than the way to the level's
154
+ * edge (a shore off the grid may lie nearer); over SHORE_TRUST_START to 1 of
155
+ * that ratio a level hands over to the next coarser one, the coarsest taking
156
+ * what is left.
157
+ */ const SHORE_LEVELS_MAX = 4;
158
+ const SHORE_TRUST_START = 0.5;
159
+ /**
160
+ * The foam the shore waves make feeds the water's foam field (see
161
+ * {@link WaterFoamField}), as Crest's UpdateFoam.compute (Wave Harmonic, MIT)
162
+ * is fed: by the waves where their displacement is squeezed (the determinant
163
+ * of its Jacobian below a coverage threshold) and by shallow water.
164
+ */ let inverseTexture = null;
165
+ /** Names the level weights of callers that do not want them. */ let weightLocals = 0;
166
+ /** Names the surf out parameter of evaluate's callers that do not want it. */ let surfLocals = 0;
167
+ const COMPONENTS = [
168
+ 'x',
169
+ 'y',
170
+ 'z',
171
+ 'w'
172
+ ];
173
+ /**
174
+ * The inverse of y = x - b sin x over one period, which carries the wave
175
+ * profile from its parameter to the distance along the wave: u is y / 2pi in
176
+ * [0, 1], v is b / SHARPNESS_MAX; the texel holds x / 2pi. y increases with x
177
+ * for b below 1, so the inverse is unique, solved here by bisection once.
178
+ */ function getInverseTexture() {
179
+ if (!inverseTexture) {
180
+ const data = new Uint16Array(INVERSE_SAMPLES * INVERSE_SHARPNESS * 4);
181
+ for(let j = 0; j < INVERSE_SHARPNESS; j++){
182
+ const b = j / (INVERSE_SHARPNESS - 1) * SHARPNESS_MAX;
183
+ for(let i = 0; i < INVERSE_SAMPLES; i++){
184
+ const y = i / (INVERSE_SAMPLES - 1) * Math.PI * 2;
185
+ let lo = 0;
186
+ let hi = Math.PI * 2;
187
+ for(let k = 0; k < 40; k++){
188
+ const mid = (lo + hi) * 0.5;
189
+ if (mid - b * Math.sin(mid) < y) {
190
+ lo = mid;
191
+ } else {
192
+ hi = mid;
193
+ }
194
+ }
195
+ const o = (j * INVERSE_SAMPLES + i) * 4;
196
+ data[o] = float2half((lo + hi) * 0.5 / (Math.PI * 2));
197
+ data[o + 3] = float2half(1);
198
+ }
199
+ }
200
+ inverseTexture = getDevice().createTexture2D('rgba16f', INVERSE_SAMPLES, INVERSE_SHARPNESS, {
201
+ mipmapping: false
202
+ });
203
+ inverseTexture.name = '@ShoreWaveInverse';
204
+ inverseTexture.update(data, 0, 0, INVERSE_SAMPLES, INVERSE_SHARPNESS);
205
+ }
206
+ return inverseTexture;
207
+ }
208
+ /**
209
+ * Waves that roll in to the shore and wash up the beach, layered over another
210
+ * generator. After the "near-shore waves" articles by Yang Chao: a wave train
211
+ * whose distance along the way in is the distance to the waterline, taken from
212
+ * the scene's waterline field, so the crests come square on to the coast;
213
+ * whose wavelength shortens towards the shore; and whose profile is a
214
+ * trochoid, Y = A (cos x + 1) / 2 at X = (x - b sin x) / 2pi raised to the
215
+ * power r, sharpening (b) and leaning shoreward (r below 1) as it shoals.
216
+ *
217
+ * The profile never drops below the rest level, so each crest lifts the
218
+ * water over the beach and each trough lets it drain: the water's edge, cut
219
+ * per pixel where the water meets the bed, runs up the sand and back with the
220
+ * waves, along the beach's own shape.
221
+ *
222
+ * Needs the waterline field, which the water hands over every frame with
223
+ * {@link ShoreWaveGenerator.setFrame}; without it the waves are off and the
224
+ * wrapped generator shows unchanged.
225
+ *
226
+ * @public
227
+ */ class ShoreWaveGenerator extends Disposable {
228
+ _base;
229
+ _settings;
230
+ /** The shore field's levels, and the wall field, see {@link ShoreWaveGenerator.setFrame}. */ _shore;
231
+ _wall;
232
+ _version;
233
+ _dummy;
234
+ /** Of the wall field: (grid origin x, origin z, 1 / grid size, rest level) */ _grid;
235
+ /** Per shore level: (grid origin x, origin z, 1 / grid size, cell size); 0 size for none. */ _shoreLevels;
236
+ /** (shore levels, 0, 0, 0) */ _shoreInfo;
237
+ /** (height, zone, crests across the zone: 2 zone / wavelength, time / period) */ _wave;
238
+ /** (sharpness at the shore, lean at the shore, wave clock of the previous frame, period in seconds) */ _shape;
239
+ /** (wave cycles the irregularity spans, wall foam drift speed, 1 / surf foam duration, 0) */ _vary;
240
+ /** (foam settle depth, foam drift depth, settled foam fade, 0), see FOAM_SETTLE */ _settle;
241
+ /** What the open water feeds the foam field with, which the shore waves add theirs to. */ _baseFoamSource;
242
+ /** (wave foam strength, wave foam coverage, shoreline foam strength, shoreline foam max depth) */ _foamSourceParams;
243
+ /** (wall foam strength, min impact speed, max impact speed, 0) */ _wallFoamParams;
244
+ /** The foam field step reads the shore waves under this key. */ foamSourceKey;
245
+ calcUnresolvedSlopeVariance;
246
+ constructor(base, settings, baseFoamSource){
247
+ super();
248
+ this._base = base;
249
+ // The shore waves are smooth at any footprint; the slopes lost are the open sea's
250
+ if (base.calcUnresolvedSlopeVariance) {
251
+ this.calcUnresolvedSlopeVariance = base.calcUnresolvedSlopeVariance.bind(base);
252
+ }
253
+ this._settings = settings;
254
+ this._baseFoamSource = baseFoamSource;
255
+ this.foamSourceKey = `SW:${baseFoamSource.foamSourceKey}`;
256
+ this._shore = null;
257
+ this._wall = null;
258
+ this._version = 0;
259
+ this._dummy = null;
260
+ this._grid = new Vector4(0, 0, 0, 0);
261
+ this._shoreLevels = Array.from({
262
+ length: SHORE_LEVELS_MAX
263
+ }, ()=>new Vector4(0, 0, 0, 0));
264
+ this._shoreInfo = new Vector4(0, 0, 0, 0);
265
+ this._wave = new Vector4(0, 1, 1, 0);
266
+ this._shape = new Vector4(0, 0, 0, 0);
267
+ this._vary = new Vector4(0, 0, 0, 0);
268
+ this._settle = new Vector4(0, 0, 0, 0);
269
+ this._foamSourceParams = new Vector4();
270
+ this._wallFoamParams = new Vector4();
271
+ }
272
+ /** The generator the shore waves are layered over. */ get base() {
273
+ return this._base;
274
+ }
275
+ /** The settings, held by reference; changes apply at the next {@link setFrame}. */ get settings() {
276
+ return this._settings;
277
+ }
278
+ /**
279
+ * Hands over the waterline fields and the clock for this frame. The shore
280
+ * field, in levels, shapes the waves and the beach's foam; without it the
281
+ * waves are off. The wall field, a single level, makes the foam against
282
+ * walls, rocks and piers; without it there is none.
283
+ * @param shore - The shore field's smoothed array, a layer per level.
284
+ * @param shoreRegions - Per level as the field reports it: (origin x, origin z, cell size, cells); 0 cells for none.
285
+ * @param wall - The wall field's smoothed array, read at layer 0.
286
+ * @param wallRegion - Its grid, as the shore levels'.
287
+ */ setFrame(shore, shoreRegions, wall, wallRegion, level, timeInSeconds) {
288
+ const s = this._settings;
289
+ this._shore = shore;
290
+ this._wall = wall;
291
+ const levels = shore ? Math.min(shoreRegions.length, SHORE_LEVELS_MAX) : 0;
292
+ for(let i = 0; i < SHORE_LEVELS_MAX; i++){
293
+ const r = i < levels ? shoreRegions[i] : null;
294
+ const size = r ? r.z * r.w : 0;
295
+ this._shoreLevels[i].setXYZW(r?.x ?? 0, r?.y ?? 0, size > 0 ? 1 / size : 0, r?.z ?? 0);
296
+ }
297
+ this._shoreInfo.x = levels;
298
+ const shoreReady = levels > 0 && this._shoreLevels[0].z > 0;
299
+ const wallSize = wallRegion.z * wallRegion.w;
300
+ this._grid.setXYZW(wallRegion.x, wallRegion.y, wall && wallSize > 0 ? 1 / wallSize : 0, level);
301
+ const zone = Math.max(0.1, s.zone);
302
+ this._shape.setXYZW(Math.min(Math.max(s.sharpness, 0), SHARPNESS_MAX), Math.min(Math.max(s.lean, 0), 1), this._wave.w, Math.max(0.1, s.period));
303
+ this._wave.setXYZW(shoreReady ? Math.max(0, s.height) : 0, zone, 2 * zone / Math.max(0.1, s.wavelength), timeInSeconds / Math.max(0.1, s.period));
304
+ this._vary.x = Math.min(Math.max(s.irregularity ?? 0.6, 0), 1) * IRREGULAR_AMPLITUDE;
305
+ this._vary.y = Math.max(0, s.wallFoamDrift ?? 0.3);
306
+ this._vary.z = 1 / Math.max(0.05, s.surfFoamDuration ?? 1.5);
307
+ const settleDepth = Math.max(0, s.foamSettleDepth ?? 0.02);
308
+ this._settle.setXYZW(settleDepth, Math.max(settleDepth + 1e-3, s.foamDriftDepth ?? 0.3), Math.min(Math.max(s.settledFoamFade ?? 0.25, 0.01), 1), 0);
309
+ this._version++;
310
+ }
311
+ setupFoamSourceUniforms(scope) {
312
+ const pb = scope.$builder;
313
+ this._baseFoamSource.setupFoamSourceUniforms(scope);
314
+ this.setupShoreUniforms(scope, 0);
315
+ scope.swFoamSource = pb.vec4().uniform(0);
316
+ // (strength, min impact speed, max impact speed, 0)
317
+ scope.swWallFoam = pb.vec4().uniform(0);
318
+ }
319
+ applyFoamSourceBindGroup(bindGroup) {
320
+ const s = this._settings;
321
+ this._baseFoamSource.applyFoamSourceBindGroup(bindGroup);
322
+ this.applyShoreBindGroup(bindGroup);
323
+ this._foamSourceParams.setXYZW(s.waveFoamStrength ?? 1, s.waveFoamCoverage ?? 0.55, s.shorelineFoamStrength ?? 2, Math.max(0.01, s.shorelineFoamMaxDepth ?? 0.65));
324
+ bindGroup.setValue('swFoamSource', this._foamSourceParams);
325
+ const minSpeed = Math.max(0, s.wallFoamMinSpeed ?? 0.2);
326
+ this._wallFoamParams.setXYZW(Math.min(Math.max(s.wallFoamStrength ?? 1, 0), 1), minSpeed, Math.max(minSpeed + 1e-3, s.wallFoamMaxSpeed ?? 1.5), 0);
327
+ bindGroup.setValue('swWallFoam', this._wallFoamParams);
328
+ }
329
+ /**
330
+ * The velocity carrying the foam (the drift, led along walls, slowed in
331
+ * shallow water, see FOAM_SETTLE) and the foam the shore waves make this
332
+ * step: from the waves where their displacement squeezes the water and from
333
+ * shallow water past the surf, both prewarmed as Crest does, and from the
334
+ * water dashing against walls (see FOAM_WALL); added to the open water's.
335
+ * Raises the foam to the surf's (see SURF_FOAM) through the state's z, and
336
+ * keeps the wrapped generator's displacement in the field's state near walls.
337
+ */ calcFoamStep(scope, xz, dt, fadeRate, prewarm, state, shape) {
338
+ const pb = scope.$builder;
339
+ const that = this;
340
+ pb.func('swFoamStep', [
341
+ pb.vec2('xz'),
342
+ pb.float('dt'),
343
+ pb.float('fadeRate'),
344
+ pb.bool('prewarm'),
345
+ pb.vec4('state').inout(),
346
+ pb.vec2('shape').inout()
347
+ ], function() {
348
+ this.$l.baseStep = that._baseFoamSource.calcFoamStep(this, this.xz, this.dt, this.fadeRate, this.prewarm, this.state, this.shape);
349
+ this.$l.waveStrength = this.swFoamSource.x;
350
+ this.$l.dir = pb.vec4(0);
351
+ this.$l.spacing = pb.float(0);
352
+ this.$l.coverage = pb.float(1);
353
+ this.$l.surf = pb.vec4(0);
354
+ this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage, undefined, undefined, this.surf);
355
+ this.$l.wall = that.wall(this, this.xz);
356
+ this.$l.near = that.nearShore(this, this.xz);
357
+ this.$l.wl = pb.vec4(0);
358
+ this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
359
+ this.$l.onField = pb.and(pb.greaterThan(this.swWave.x, 0), pb.greaterThanEqual(this.gridFade, 0));
360
+ // Depth under the displaced surface, 0 on dry sand; deep off the field
361
+ this.$l.depth = pb.float(1e4);
362
+ this.$if(this.onField, function() {
363
+ this.depth = pb.max(pb.sub(this.sw.x, this.wl.z), 0);
364
+ });
365
+ // The drift, led along walls, as much of it as the depth lets carry the
366
+ // foam (see FOAM_SETTLE), faded out towards the edge of the field
367
+ this.$l.settle = pb.mul(pb.sub(1, pb.smoothStep(this.swSettle.x, this.swSettle.y, this.depth)), pb.max(this.gridFade, 0));
368
+ this.$l.velocity = pb.mul(that.slip(this, this.wall, this.baseStep.xy), pb.sub(1, this.settle));
369
+ this.$l.waveDt = this.$choice(this.prewarm, pb.max(this.dt, pb.div(pb.min(1, pb.sub(this.waveStrength, 1)), this.fadeRate)), this.dt);
370
+ this.$l.foam = pb.mul(FOAM_WAVE_GAIN, this.waveDt, this.waveStrength, pb.clamp(pb.sub(this.swFoamSource.y, this.sw.z), 0, 1));
371
+ // Foam from shallow water, by the depth under the displaced surface, as
372
+ // Crest makes it, scaled by the kinetic-energy potential of Ihmsen et al.
373
+ // (eq. (8), I_k = Phi(E_k)) of the water's speed under the shore waves,
374
+ // per unit mass between the wall foam's speeds: made while a crest surges
375
+ // in, not where the water lies still. Only where there is water, only on
376
+ // the waterline field, faded towards its edge as the waves are, and not
377
+ // in the surf, whose foam is the bores' (see SURF_FOAM): there it would
378
+ // gather most where the sheet lingers, at the swash's highest reach.
379
+ this.$if(pb.and(this.onField, pb.and(pb.greaterThan(this.depth, 0), pb.lessThan(this.near.x, 1))), function() {
380
+ this.$l.shoreDt = this.$choice(this.prewarm, pb.div(1, this.fadeRate), this.dt);
381
+ this.shoreDt = pb.mul(this.shoreDt, this.gridFade, pb.sub(1, this.near.x));
382
+ this.$l.shoreEnergy = pb.mul(this.sw.w, this.sw.w, 0.5);
383
+ this.$l.shoreEMin = pb.mul(this.swWallFoam.y, this.swWallFoam.y, 0.5);
384
+ this.$l.shoreEMax = pb.mul(this.swWallFoam.z, this.swWallFoam.z, 0.5);
385
+ this.$l.shoreKinetic = pb.div(pb.sub(pb.min(this.shoreEnergy, this.shoreEMax), pb.min(this.shoreEnergy, this.shoreEMin)), pb.max(pb.sub(this.shoreEMax, this.shoreEMin), 1e-6));
386
+ this.foam = pb.add(this.foam, pb.mul(this.swFoamSource.z, this.shoreDt, this.shoreKinetic, pb.clamp(pb.sub(1, pb.div(this.depth, this.swFoamSource.w)), 0, 1)));
387
+ });
388
+ // Foam from the water dashing against a wall: the trapped-air potential
389
+ // by the surface water's velocity (see FOAM_WALL).
390
+ // Cells whose water the waves can carry to within the band of a wall
391
+ this.$l.wallFoam = pb.float(0);
392
+ this.$if(pb.and(pb.greaterThan(this.wall.w, 0), pb.lessThan(this.wall.z, FOAM_SLIP_BAND + WALL_DISPLACEMENT_REACH)), function() {
393
+ this.$l.restPos = pb.vec3(this.xz.x, this.swGrid.w, this.xz.y);
394
+ this.$l.wavePos = pb.vec3(0);
395
+ this.$l.waveNormal = pb.vec3(0, 1, 0);
396
+ that._base.calcVertexPositionAndNormal(this, this.restPos, this.wavePos, this.waveNormal);
397
+ this.$l.rise = pb.sub(this.wavePos.y, this.restPos.y);
398
+ // The wall as seen from where the waves carry this water, as Crest takes
399
+ // the shallow water foam's depth at the displaced position
400
+ // (UpdateFoam.compute): the field is kept at undisplaced positions
401
+ this.$l.wallAt = that.wall(this, pb.add(this.xz, pb.sub(this.wavePos.xz, this.restPos.xz)));
402
+ this.$l.wallReach = pb.max(0, pb.sub(1, pb.div(this.wallAt.z, FOAM_SLIP_BAND)));
403
+ this.$l.wallW = this.$choice(pb.greaterThan(this.wallAt.z, 0), pb.mul(this.wallAt.w, this.wallReach), pb.float(0));
404
+ this.$l.avgSpeed = this.state.y;
405
+ this.$if(pb.greaterThan(this.state.w, 0), function() {
406
+ // Averaged over WALL_SPEED_AVERAGE seconds: the instantaneous speed
407
+ // varies per cell with ripples shorter than a cell
408
+ this.avgSpeed = pb.mix(this.state.y, pb.abs(pb.div(pb.sub(this.rise, this.state.x), this.state.w)), pb.min(1, pb.div(this.state.w, WALL_SPEED_AVERAGE)));
409
+ this.$l.speed = this.avgSpeed;
410
+ // |v| W, the trapped-air potential averaged over a wave's orbit, with
411
+ // W = 1 - d / h at the displaced position
412
+ this.$l.vDiff = pb.mul(this.speed, this.wallReach);
413
+ this.$l.tMin = this.swWallFoam.y;
414
+ this.$l.tMax = this.swWallFoam.z;
415
+ this.$l.trapped = pb.div(pb.sub(pb.min(this.vDiff, this.tMax), pb.min(this.vDiff, this.tMin)), pb.sub(this.tMax, this.tMin));
416
+ this.$l.energy = pb.mul(this.speed, this.speed, 0.5);
417
+ this.$l.eMin = pb.mul(this.tMin, this.tMin, 0.5);
418
+ this.$l.eMax = pb.mul(this.tMax, this.tMax, 0.5);
419
+ this.$l.kinetic = pb.div(pb.sub(pb.min(this.energy, this.eMax), pb.min(this.energy, this.eMin)), pb.max(pb.sub(this.eMax, this.eMin), 1e-6));
420
+ this.$if(pb.greaterThan(this.wallW, 0), function() {
421
+ this.wallFoam = pb.mul(FOAM_WAVE_GAIN, this.kinetic, this.trapped, this.wallAt.w, this.dt);
422
+ });
423
+ });
424
+ this.state = pb.vec4(this.rise, this.avgSpeed, 0, this.state.w);
425
+ });
426
+ // The surf's foam on the water there is (see SURF_FOAM): the field holds
427
+ // it, and where the sheet drains, keeps it on the sand (see FOAM_SETTLE),
428
+ // where it fades the slower the more it has settled
429
+ this.shape = pb.vec2(pb.mul(that.surfFoam(this, this.near, this.surf), this.coverage), pb.mix(1, this.swSettle.z, this.settle));
430
+ // Foam is only made where there is water: the open sea's crests run on
431
+ // under the beach, and their foam would surface through the sand that
432
+ // the field's foam is shown on (FOAM_SETTLE)
433
+ this.$return(pb.vec4(this.velocity, pb.mul(pb.add(this.baseStep.z, this.foam), this.coverage), pb.mul(pb.add(this.baseStep.w, this.wallFoam), this.coverage)));
434
+ });
435
+ return scope.swFoamStep(xz, dt, fadeRate, prewarm, state, shape);
436
+ }
437
+ /**
438
+ * Restricts the foam projected onto the ground to the beach bed. The mesh
439
+ * above it only carries the draw; a scene point supplies the actual ground
440
+ * position.
441
+ */ calcFoamGroundMask(scope, worldPos) {
442
+ const pb = scope.$builder;
443
+ const that = this;
444
+ pb.func('swFoamGroundMask', [
445
+ pb.vec3('position')
446
+ ], function() {
447
+ this.$l.wl = pb.vec4(0);
448
+ this.$l.gridFade = that.waterlineAt(this, this.position.xz, this.wl);
449
+ this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
450
+ this.$return(pb.float(0));
451
+ });
452
+ this.$l.bed = pb.add(this.swGrid.w, this.wl.z);
453
+ this.$l.reach = pb.mul(this.swWave.x, pb.clamp(this.wl.y, 1, B_MAX));
454
+ // A bed-support mask. Not cut at the still waterline: a raised, dry
455
+ // carrier can project to wet ground just seaward of it, leaving neither
456
+ // the water nor the ground showing foam there. Sheet thickness controls
457
+ // the handover.
458
+ this.$return(pb.mul(pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y), this.gridFade, pb.sub(1, pb.smoothStep(0.08, 0.2, pb.abs(pb.sub(this.position.y, this.bed)))), this.$choice(pb.and(pb.lessThan(this.wl.z, pb.add(this.swWave.x, SWASH_LIFT_MARGIN)), pb.greaterThan(this.wl.x, pb.neg(pb.add(pb.mul(this.reach, 1.5), SWASH_EDGE_EXTEND)))), pb.float(1), pb.float(0))));
459
+ });
460
+ return scope.swFoamGroundMask(worldPos);
461
+ }
462
+ /**
463
+ * The velocity the foam texture slides with at an undisplaced XZ in the
464
+ * water's shaders: the drift it is given, led along the walls.
465
+ */ calcFoamFlow(scope, xz, drift) {
466
+ const pb = scope.$builder;
467
+ const that = this;
468
+ pb.func('swFoamFlow', [
469
+ pb.vec2('xz'),
470
+ pb.vec2('drift')
471
+ ], function() {
472
+ // The drift only, not the waves' water: flow mapping (Vlachos, "Water Flow
473
+ // in Portal 2", SIGGRAPH 2010) needs a smooth, slowly varying flow, as
474
+ // Crest's currents are, and the surge and swash jump at the waterline and
475
+ // shear along the beach. The field still carries the foam with the waves.
476
+ // Slowed in shallow water as the field's foam is (see FOAM_SETTLE), so
477
+ // the water's foam pattern and the beach's are one; by the still-water
478
+ // depth, which a pattern sliding at the waves' pace would tear on.
479
+ this.$l.wl = pb.vec4(0);
480
+ this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
481
+ this.$l.settle = pb.float(0);
482
+ this.$if(pb.and(pb.greaterThan(this.swWave.x, 0), pb.greaterThanEqual(this.gridFade, 0)), function() {
483
+ this.settle = pb.mul(pb.sub(1, pb.smoothStep(this.swSettle.x, this.swSettle.y, pb.max(pb.neg(this.wl.z), 0))), this.gridFade);
484
+ });
485
+ this.$return(pb.mul(that.slip(this, that.wall(this, this.xz), this.drift), pb.sub(1, this.settle)));
486
+ });
487
+ return scope.swFoamFlow(xz, drift);
488
+ }
489
+ /**
490
+ * The wall at an undisplaced XZ, from the wall field: (normal out of the
491
+ * land x, z, distance to the waterline, how much of a wall the waterline
492
+ * is, 0 on a beach the surf rolls up and off the field). The normal is only
493
+ * worked out within FOAM_SLIP_BAND of a wall, else 0.
494
+ * @internal
495
+ */ wall(scope, xz) {
496
+ const pb = scope.$builder;
497
+ pb.func('swWall', [
498
+ pb.vec2('xz')
499
+ ], function() {
500
+ this.$l.uv = pb.mul(pb.sub(this.xz, this.swGrid.xy), this.swGrid.z);
501
+ this.$if(pb.or(pb.lessThanEqual(this.swGrid.z, 0), pb.or(pb.any(pb.lessThan(this.uv, pb.vec2(0))), pb.any(pb.greaterThan(this.uv, pb.vec2(1))))), function() {
502
+ this.$return(pb.vec4(0, 0, 1e4, 0));
503
+ });
504
+ this.$l.wl = pb.textureArraySampleLevel(this.swWallField, this.uv, 0, 0);
505
+ this.$l.edge = pb.min(pb.min(this.uv.x, this.uv.y), pb.min(pb.sub(1, this.uv.x), pb.sub(1, this.uv.y)));
506
+ // The complement of the shelving the surf rolls in on, faded out towards
507
+ // the edge of the field
508
+ this.$l.steep = pb.mul(pb.sub(1, pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y)), pb.smoothStep(0, GRID_EDGE_FADE, this.edge));
509
+ this.$if(pb.or(pb.lessThanEqual(this.steep, 0), pb.greaterThanEqual(this.wl.x, FOAM_SLIP_BAND)), function() {
510
+ this.$return(pb.vec4(0, 0, this.wl.x, this.steep));
511
+ });
512
+ // Out of the land: up the gradient of the distance to the waterline, a metre either side
513
+ this.$l.c = this.swGrid.z;
514
+ this.$l.g = pb.vec2(pb.sub(pb.textureArraySampleLevel(this.swWallField, pb.add(this.uv, pb.vec2(this.c, 0)), 0, 0).x, pb.textureArraySampleLevel(this.swWallField, pb.sub(this.uv, pb.vec2(this.c, 0)), 0, 0).x), pb.sub(pb.textureArraySampleLevel(this.swWallField, pb.add(this.uv, pb.vec2(0, this.c)), 0, 0).x, pb.textureArraySampleLevel(this.swWallField, pb.sub(this.uv, pb.vec2(0, this.c)), 0, 0).x));
515
+ this.$return(pb.vec4(pb.div(this.g, pb.max(pb.length(this.g), 1e-5)), this.wl.x, this.steep));
516
+ });
517
+ return scope.swWall(xz);
518
+ }
519
+ /**
520
+ * The shore field at an undisplaced XZ: its shore coordinates (see
521
+ * WaterlineField.smoothTexture) into `wl`, and returned, the weight the
522
+ * shore effects fade by towards the edge of the field, or -1 off it or with
523
+ * no field. The finest level that holds the point and can vouch for its
524
+ * distance answers (see SHORE_LEVELS_MAX), blended into the next coarser
525
+ * over the last GRID_EDGE_FADE of it; only the coarsest fades out. The
526
+ * levels' shares go to `weights`, for {@link shoreGradient}.
527
+ * @internal
528
+ */ waterlineAt(scope, xz, wl, weights) {
529
+ const pb = scope.$builder;
530
+ pb.func('swWaterlineAt', [
531
+ pb.vec2('xz'),
532
+ pb.vec4('wl').out(),
533
+ pb.vec4('weights').out()
534
+ ], function() {
535
+ this.wl = pb.vec4(0);
536
+ this.weights = pb.vec4(0);
537
+ // Share of the answer still to come from coarser levels
538
+ this.$l.remaining = pb.float(1);
539
+ this.$l.last = pb.sub(this.swShoreInfo.x, 1);
540
+ for(let i = 0; i < SHORE_LEVELS_MAX; i++){
541
+ const r = this[`swShore${i}`];
542
+ this.$l[`uv${i}`] = pb.mul(pb.sub(this.xz, r.xy), r.z);
543
+ const uv = this[`uv${i}`];
544
+ this.$l[`edge${i}`] = pb.min(pb.min(uv.x, uv.y), pb.min(pb.sub(1, uv.x), pb.sub(1, uv.y)));
545
+ const edge = this[`edge${i}`];
546
+ this.$if(pb.and(pb.greaterThan(this.remaining, 0), pb.and(pb.greaterThan(r.z, 0), pb.greaterThan(edge, 0))), function() {
547
+ this.$l.t = pb.textureArraySampleLevel(this.swWaterline, uv, i, 0);
548
+ // Distance over the way to the edge, both in metres; the coarsest
549
+ // level present vouches for everything
550
+ this.$l.trust = this.$choice(pb.lessThan(pb.float(i), this.last), pb.sub(1, pb.smoothStep(SHORE_TRUST_START, 1, pb.div(pb.mul(pb.abs(this.t.x), r.z), edge))), pb.float(1));
551
+ this.$l.w = pb.mul(this.remaining, pb.smoothStep(0, GRID_EDGE_FADE, edge), this.trust);
552
+ this.wl = pb.add(this.wl, pb.mul(this.t, this.w));
553
+ this.weights[COMPONENTS[i]] = this.w;
554
+ this.remaining = pb.sub(this.remaining, this.w);
555
+ });
556
+ }
557
+ this.$l.sum = pb.sub(1, this.remaining);
558
+ this.$if(pb.lessThanEqual(this.sum, 0), function() {
559
+ this.$return(pb.float(-1));
560
+ });
561
+ this.wl = pb.div(this.wl, this.sum);
562
+ this.$return(this.sum);
563
+ });
564
+ if (!weights) {
565
+ const name = `swLevelWeights${weightLocals++}`;
566
+ scope.$l[name] = pb.vec4(0);
567
+ weights = scope[name];
568
+ }
569
+ return scope.swWaterlineAt(xz, wl, weights);
570
+ }
571
+ /**
572
+ * Gradient of the distance to the waterline at an undisplaced XZ, per metre,
573
+ * from the levels by the shares {@link waterlineAt} gave them: each level's
574
+ * difference a metre or a cell, whichever is more, either side.
575
+ * @internal
576
+ */ shoreGradient(scope, xz, weights) {
577
+ const pb = scope.$builder;
578
+ pb.func('swShoreGradient', [
579
+ pb.vec2('xz'),
580
+ pb.vec4('weights')
581
+ ], function() {
582
+ this.$l.g = pb.vec2(0);
583
+ for(let i = 0; i < SHORE_LEVELS_MAX; i++){
584
+ const w = this.weights[COMPONENTS[i]];
585
+ this.$if(pb.greaterThan(w, 0), function() {
586
+ const r = this[`swShore${i}`];
587
+ this.$l[`guv${i}`] = pb.mul(pb.sub(this.xz, r.xy), r.z);
588
+ this.$l[`step${i}`] = pb.max(r.w, 1);
589
+ this.$l[`c${i}`] = pb.mul(this[`step${i}`], r.z);
590
+ const uv = this[`guv${i}`];
591
+ const c = this[`c${i}`];
592
+ this.$l[`d${i}`] = pb.vec2(pb.sub(pb.textureArraySampleLevel(this.swWaterline, pb.add(uv, pb.vec2(c, 0)), i, 0).x, pb.textureArraySampleLevel(this.swWaterline, pb.sub(uv, pb.vec2(c, 0)), i, 0).x), pb.sub(pb.textureArraySampleLevel(this.swWaterline, pb.add(uv, pb.vec2(0, c)), i, 0).x, pb.textureArraySampleLevel(this.swWaterline, pb.sub(uv, pb.vec2(0, c)), i, 0).x));
593
+ this.g = pb.add(this.g, pb.mul(this[`d${i}`], pb.div(w, pb.mul(this[`step${i}`], 2))));
594
+ });
595
+ }
596
+ this.$return(this.g);
597
+ });
598
+ return scope.swShoreGradient(xz, weights);
599
+ }
600
+ /**
601
+ * A velocity near a waterline too steep for surf, given the wall there (see
602
+ * {@link wall}), handed over to the wall foam's drift out along the wall's
603
+ * normal. See FOAM_SLIP_BAND.
604
+ * @internal
605
+ */ slip(scope, wall, v) {
606
+ const pb = scope.$builder;
607
+ pb.func('swFoamSlip', [
608
+ pb.vec4('wall'),
609
+ pb.vec2('v')
610
+ ], function() {
611
+ this.$l.weight = pb.mul(this.wall.w, pb.sub(1, pb.smoothStep(0, FOAM_SLIP_BAND, this.wall.z)));
612
+ this.$return(pb.mix(this.v, pb.mul(this.wall.xy, this.swVary.y), this.weight));
613
+ });
614
+ return scope.swFoamSlip(wall, v);
615
+ }
616
+ clone() {
617
+ return new ShoreWaveGenerator(this._base.clone(), {
618
+ ...this._settings
619
+ }, this._baseFoamSource);
620
+ }
621
+ get version() {
622
+ return this._base.version + this._version;
623
+ }
624
+ setupUniforms(scope, uniformGroup) {
625
+ this._base.setupUniforms(scope, uniformGroup);
626
+ this.setupShoreUniforms(scope, uniformGroup);
627
+ }
628
+ /** The uniforms {@link ShoreWaveGenerator.evaluate} reads. @internal */ setupShoreUniforms(scope, uniformGroup) {
629
+ const pb = scope.$builder;
630
+ // The shore field's levels, and the wall field's, see setFrame
631
+ scope.swWaterline = pb.tex2DArray().uniform(uniformGroup);
632
+ scope.swWallField = pb.tex2DArray().uniform(uniformGroup);
633
+ scope.swInverse = pb.tex2D().uniform(uniformGroup);
634
+ for(let i = 0; i < SHORE_LEVELS_MAX; i++){
635
+ // (origin x, origin z, 1 / size, cell size); 0 size for no level
636
+ scope[`swShore${i}`] = pb.vec4().uniform(uniformGroup);
637
+ }
638
+ // (shore levels, 0, 0, 0)
639
+ scope.swShoreInfo = pb.vec4().uniform(uniformGroup);
640
+ // The wall field: (origin x, origin z, 1 / size, rest level)
641
+ scope.swGrid = pb.vec4().uniform(uniformGroup);
642
+ scope.swWave = pb.vec4().uniform(uniformGroup);
643
+ scope.swShape = pb.vec4().uniform(uniformGroup);
644
+ // (irregularity: wave cycles J spans, wall foam drift speed, 1 / surf foam duration, 0)
645
+ scope.swVary = pb.vec4().uniform(uniformGroup);
646
+ // (foam settle depth, foam drift depth, settled foam fade, 0)
647
+ scope.swSettle = pb.vec4().uniform(uniformGroup);
648
+ }
649
+ /** @internal */ applyShoreBindGroup(bindGroup) {
650
+ bindGroup.setTexture('swWaterline', this._shore ?? this.getDummy(), fetchSampler('clamp_linear_nomip'));
651
+ bindGroup.setTexture('swWallField', this._wall ?? this.getDummy(), fetchSampler('clamp_linear_nomip'));
652
+ for(let i = 0; i < SHORE_LEVELS_MAX; i++){
653
+ bindGroup.setValue(`swShore${i}`, this._shoreLevels[i]);
654
+ }
655
+ bindGroup.setValue('swShoreInfo', this._shoreInfo);
656
+ bindGroup.setTexture('swInverse', getInverseTexture(), fetchSampler('clamp_linear_nomip'));
657
+ bindGroup.setValue('swGrid', this._grid);
658
+ bindGroup.setValue('swWave', this._wave);
659
+ bindGroup.setValue('swShape', this._shape);
660
+ bindGroup.setValue('swVary', this._vary);
661
+ bindGroup.setValue('swSettle', this._settle);
662
+ }
663
+ /**
664
+ * Shore wave at an undisplaced world XZ, at a wave clock (time over period):
665
+ * (height over the rest level, slope of the height along the distance to
666
+ * the shore, determinant of the Jacobian of the displacement, speed of the
667
+ * water carrying the foam towards the shore in metres per second, negative
668
+ * in the backwash).
669
+ * Out parameters: `dir` (direction to the shore xy, weight of the wrapped
670
+ * generator's chop dying near the shore z, bed height the surface is held
671
+ * over w, far below the water where it is not), `spacing`, the distance
672
+ * between crests here, and `coverage`, the water's share of the surface.
673
+ * `surf`, when given, gets (seconds since the last crest passed here, on
674
+ * the waterline's clock inland, which the swash keeps (see SURF_FOAM); the
675
+ * wave's share of the full height; X, the share of a wave since the crest;
676
+ * the depth of the water over the bed), and (1e4, 0, 0, 0) off the shore zone.
677
+ * `cell` is the distance between mesh vertices to
678
+ * band-limit to, 0 for the full profile.
679
+ * @internal
680
+ */ evaluate(scope, xz, dir, spacing, coverage, tPhase, cell, surf) {
681
+ const pb = scope.$builder;
682
+ const that = this;
683
+ pb.func('swEvaluate', [
684
+ pb.vec2('xz'),
685
+ pb.vec4('dir').out(),
686
+ pb.float('spacing').out(),
687
+ pb.float('coverage').out(),
688
+ pb.float('tPhase'),
689
+ pb.float('cell'),
690
+ pb.vec4('surf').out()
691
+ ], function() {
692
+ // No bed to hold the surface over, unless the shore says otherwise
693
+ this.dir = pb.vec4(0, 0, 0, -1e4);
694
+ this.spacing = pb.float(1);
695
+ this.coverage = pb.float(1);
696
+ this.surf = pb.vec4(1e4, 0, 0, 0);
697
+ // Shore coordinates: (signed distance to the waterline, negative inland;
698
+ // bed B; bed height over the rest level)
699
+ this.$l.wl = pb.vec4(0);
700
+ this.$l.levelWeights = pb.vec4(0);
701
+ this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl, this.levelWeights);
702
+ this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
703
+ this.$return(pb.vec4(0, 0, 1, 0));
704
+ });
705
+ this.$l.d = this.wl.x;
706
+ this.$l.zone = this.swWave.y;
707
+ this.$if(pb.greaterThan(this.d, this.zone), function() {
708
+ this.$return(pb.vec4(0, 0, 1, 0));
709
+ });
710
+ // Direction to the shore: down the gradient of the distance
711
+ this.$l.g = that.shoreGradient(this, this.xz, this.levelWeights);
712
+ this.dir = pb.vec4(pb.neg(pb.div(this.g, pb.max(pb.length(this.g), 1e-5))), 0, 0);
713
+ // Phase: the integral of 1 / wavelength, with the wavelength shrinking as
714
+ // sqrt of the depth. Below `dMin` (past breaking) it runs on linearly;
715
+ // inland it is frozen at its shoreline value.
716
+ this.$l.dc = pb.max(this.d, 0);
717
+ this.$l.dMin = pb.mul(this.zone, MIN_COMPRESSION);
718
+ this.$l.rootMin = pb.sqrt(MIN_COMPRESSION);
719
+ this.$l.rootTerm = this.$choice(pb.greaterThan(this.dc, this.dMin), pb.sqrt(pb.div(this.dc, this.zone)), pb.add(this.rootMin, pb.div(pb.sub(this.dc, this.dMin), pb.mul(2, this.rootMin, this.zone))));
720
+ // Distance between crests here, 1 / the phase's rate of change along the way in
721
+ this.spacing = pb.div(pb.mul(2, pb.sqrt(pb.mul(pb.max(this.dc, this.dMin), this.zone))), pb.max(this.swWave.z, 1e-4));
722
+ // Where this point's way in meets the waterline: noise there varies along
723
+ // the shore only
724
+ this.$l.foot = pb.add(this.xz, pb.mul(this.dir.xy, this.d));
725
+ // Inland every noise is taken at the foot, so the swash keeps one clock
726
+ // and one height all the way up the beach
727
+ this.$l.noisePos = this.$choice(pb.greaterThan(this.d, 0), this.xz, this.foot);
728
+ // The crests' clock, in waves, and the irregular phase it reads as:
729
+ // s + J(s, foot), see IRREGULAR_RATE
730
+ this.$l.s = pb.add(this.tPhase, pb.mul(this.swWave.z, this.rootTerm));
731
+ this.$l.shoreUV = pb.mul(this.foot, IRREGULAR_SHORE_SCALE);
732
+ this.$l.j0 = valueNoise(this, pb.add(this.shoreUV, pb.vec2(pb.mul(this.s, IRREGULAR_RATE), 0)));
733
+ this.$l.j1 = valueNoise(this, pb.add(this.shoreUV, pb.vec2(pb.mul(pb.add(this.s, IRREGULAR_STEP), IRREGULAR_RATE), 0)));
734
+ this.$l.phase = pb.add(this.s, pb.mul(this.swVary.x, pb.sub(this.j0, 0.5)));
735
+ // dPsi/ds: a wave of a longer period here is that much farther apart
736
+ this.$l.rate = pb.max(pb.add(1, pb.div(pb.mul(this.swVary.x, pb.sub(this.j1, this.j0)), IRREGULAR_STEP)), 0.1);
737
+ // The crests travel at the speed of the water depth whatever their
738
+ // period: set before the spacing takes the period in
739
+ this.$l.crestSpeed = pb.max(pb.div(this.spacing, this.swShape.w), 0.1);
740
+ this.spacing = pb.div(this.spacing, this.rate);
741
+ // Distance along the wave from a crest, seaward: 0 at the crest, its front
742
+ // (shoreward) face just below 1
743
+ this.$l.X = pb.fract(this.phase);
744
+ // Shoaling, 0 at the outer edge of the zone, 1 at the waterline and inland
745
+ this.$l.shoal = pb.sub(1, pb.div(this.dc, this.zone));
746
+ // Mesh vertices per crest spacing, where a mesh is being displaced
747
+ this.$l.sharpLod = pb.float(1);
748
+ this.$l.ampLod = pb.float(1);
749
+ this.$if(pb.greaterThan(this.cell, 0), function() {
750
+ this.$l.samples = pb.div(this.spacing, this.cell);
751
+ this.sharpLod = pb.smoothStep(SHARP_SAMPLES_MIN, SHARP_SAMPLES_FULL, this.samples);
752
+ this.ampLod = pb.smoothStep(AMP_SAMPLES_MIN, AMP_SAMPLES_FULL, this.samples);
753
+ });
754
+ // The profile: X = (g / 2pi)^r with g = x - b sin x, Y = (cos x + 1) / 2.
755
+ // Sharper (b) and leaning further shoreward (r below 1) as it shoals.
756
+ this.$l.b = pb.mul(this.swShape.x, pb.smoothStep(0, 0.8, this.shoal), this.sharpLod);
757
+ this.$l.r = pb.div(1, pb.add(1, pb.mul(this.swShape.y, 2, pb.smoothStep(0.2, 1, this.shoal), this.sharpLod)));
758
+ this.$l.gu = pb.pow(pb.max(this.X, 1e-5), pb.div(1, this.r));
759
+ this.$l.xu = pb.textureSampleLevel(this.swInverse, pb.vec2(pb.add(pb.mul(this.gu, (INVERSE_SAMPLES - 1) / INVERSE_SAMPLES), 0.5 / INVERSE_SAMPLES), pb.add(pb.mul(pb.div(this.b, SHARPNESS_MAX), (INVERSE_SHARPNESS - 1) / INVERSE_SHARPNESS), 0.5 / INVERSE_SHARPNESS)), 0).r;
760
+ this.$l.x = pb.mul(this.xu, Math.PI * 2);
761
+ this.$l.cx = pb.cos(this.x);
762
+ this.$l.profile = pb.mul(pb.add(this.cx, 1), 0.5);
763
+ // dY/dX = (dY/dx) / (dX/dx), dX/dx = r (g / 2pi)^(r - 1) (1 - b cos x) / 2pi
764
+ this.$l.dXdx = pb.div(pb.mul(this.r, pb.pow(pb.max(this.gu, 1e-4), pb.sub(this.r, 1)), pb.max(pb.sub(1, pb.mul(this.b, this.cx)), 1e-3)), Math.PI * 2);
765
+ this.$l.dYdX = pb.div(pb.mul(pb.sin(this.x), -0.5), pb.max(this.dXdx, 1e-3));
766
+ // Sets: the waves swell and fade along the coast and over time
767
+ this.$l.waveSet = pb.sub(1, pb.mul(SET_DEPTH, pb.sub(1, pb.smoothStep(0.3, 0.7, valueNoise(this, pb.add(pb.mul(this.noisePos, SET_SCALE), pb.vec2(pb.mul(this.tPhase, SET_DRIFT), 0)))))));
768
+ // Segments of a wave, picked by its count: each rises and falls along the
769
+ // shore by its own noise
770
+ const segment = (scope, index)=>{
771
+ const seed = pb.mul(pb.vec2(hash11(scope, index), hash11(scope, pb.add(index, 17.31))), 97);
772
+ return pb.sub(1, pb.mul(SEGMENT_DEPTH, pb.sub(1, pb.smoothStep(0.2, 0.6, valueNoise(scope, pb.add(pb.mul(this.foot, SEGMENT_SCALE), seed))))));
773
+ };
774
+ // The swash belongs to the crest that threw it, counted from its arrival
775
+ // at the waterline, for the whole of its run up and back
776
+ this.$l.swashSet = pb.mul(this.waveSet, segment(this, pb.floor(this.phase)));
777
+ // Elsewhere counted from trough to trough, where the height and its slope
778
+ // are 0, so switching to the next wave's noise shows no seam
779
+ this.$l.crestSegment = segment(this, pb.floor(pb.add(this.phase, 0.5)));
780
+ this.waveSet = pb.mul(this.waveSet, this.crestSegment);
781
+ // Bed B the surf here comes in on. Inland the water comes up from the
782
+ // waterline, so the bed there counts too: the top of a rock or a pier
783
+ // standing out of the water is flat, but the water meets it at a wall
784
+ // and never runs up onto it as onto a beach
785
+ this.$l.bedSlope = this.wl.y;
786
+ this.$if(pb.lessThan(this.d, 0), function() {
787
+ this.$l.footWl = pb.vec4(0);
788
+ that.waterlineAt(this, this.foot, this.footWl);
789
+ this.bedSlope = pb.min(this.bedSlope, this.footWl.y);
790
+ });
791
+ // Only where the bed shelves (surf rolls in on a beach, not against a
792
+ // cliff, pier or hull), faded out towards the edge of the grid
793
+ this.$l.shelving = pb.mul(pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.bedSlope), this.gridFade);
794
+ // Rising out of the sea over the outer part of the zone
795
+ this.$l.amp = pb.mul(this.swWave.x, this.waveSet, this.shelving, pb.smoothStep(0, 0.4, this.shoal), this.ampLod);
796
+ this.$l.height = pb.mul(this.profile, this.amp);
797
+ // Keep the whole soft edge on the wet side of the bed intersection.
798
+ this.$l.edgeSoft = pb.div(SWASH_EDGE_SOFT, pb.clamp(this.bedSlope, 1, B_MAX));
799
+ this.coverage = pb.smoothStep(0, this.edgeSoft, pb.sub(this.height, this.wl.z));
800
+ // Slope along the distance to the shore: X runs seaward at 1 / spacing,
801
+ // and stands still inland where the phase is frozen
802
+ this.$l.slope = this.$choice(pb.greaterThan(this.d, 0), pb.div(pb.mul(this.dYdX, this.amp), this.spacing), pb.float(0));
803
+ // Determinant of the Jacobian of the horizontal displacement the profile
804
+ // stands for (dX/dx 2pi), below 1 where the water is squeezed at the crest's
805
+ // front; scaled by the wave's share of the full height.
806
+ this.$l.det = pb.add(1, pb.mul(pb.div(this.amp, pb.max(this.swWave.x, 1e-4)), pb.sub(pb.mul(this.dXdx, Math.PI * 2), 1)));
807
+ // The wrapped generator's chop dies near the shore
808
+ this.dir.z = pb.mul(pb.sub(1, pb.smoothStep(0, CHOP_FADE, this.d)), this.shelving);
809
+ // Seconds since the crest passed: the phase advances at rate / period a
810
+ // second, and X counts it from the crest. Inland the phase is the
811
+ // waterline's, so the swash's age goes on from the bore's.
812
+ this.surf = pb.vec4(pb.div(pb.mul(this.X, this.swShape.w), this.rate), this.$choice(pb.greaterThan(this.d, 0), pb.div(this.amp, pb.max(this.swWave.x, 1e-4)), pb.mul(this.swashSet, this.shelving)), this.X, 0);
813
+ // The water rises straight up and is wet wherever it stands over the bed.
814
+ // Near that edge the mesh is held clear of the bed and cut per pixel, only
815
+ // where the water could reach (not on dunes higher than the highest water)
816
+ // A conservative, time-independent support region: a weaker next
817
+ // crest must not drop the mesh under a swash that is still draining.
818
+ // Actual water is bounded by coverage, independently of this mesh.
819
+ this.$l.highest = this.swWave.x;
820
+ this.$l.reach = pb.mul(this.highest, pb.clamp(this.bedSlope, 1, B_MAX));
821
+ this.dir.w = this.$choice(pb.and(pb.and(pb.greaterThan(this.shelving, 0), pb.lessThan(this.wl.z, pb.add(this.highest, SWASH_LIFT_MARGIN))), pb.greaterThan(this.d, pb.neg(pb.add(pb.mul(this.reach, 1.5), SWASH_EDGE_EXTEND)))), this.wl.z, pb.float(-1e4));
822
+ // Speed of the water carrying the foam along the way in. In place of Crest's
823
+ // flow simulation, the velocity under a long wave, u = g eta / c with c the
824
+ // crest speed, towards the shore.
825
+ this.$l.flow = pb.clamp(pb.div(pb.mul(SWASH_GRAVITY, this.height), this.crestSpeed), 0, this.crestSpeed);
826
+ // Inland: the swash, in wave periods since its crest reached the waterline
827
+ // (X, the phase there being frozen at its waterline value)
828
+ this.$if(pb.lessThan(this.d, 0), function() {
829
+ this.$l.s = pb.neg(this.d);
830
+ this.$l.bedB = pb.clamp(this.bedSlope, 1, B_MAX);
831
+ this.$l.runUp = pb.mul(this.swWave.x, this.swashSet, this.shelving);
832
+ this.$l.runUpH = pb.max(pb.mul(this.runUp, this.bedB), 1e-3);
833
+ // Up and back in 2 u0 / (g tan(beta)) = 2 B sqrt(2 height / g) seconds
834
+ this.$l.duration = pb.clamp(pb.div(pb.mul(2, this.bedB, pb.sqrt(pb.div(pb.mul(2, this.runUp), SWASH_GRAVITY))), this.swShape.w), 1e-3, SWASH_DURATION_MAX);
835
+ this.$l.tt = pb.div(this.X, this.duration);
836
+ this.$l.front = this.$choice(pb.lessThan(this.tt, 1), pb.mul(4, this.runUpH, this.tt, pb.sub(1, this.tt)), pb.float(0));
837
+ // h = (s_f - s)^2 / (36 g' t^2) with g' tan(beta) = 8 runUpH / duration^2.
838
+ // The sheet climbs a bed of slope 1 / B, so a bed standing higher
839
+ // than that would put it here lies as far up as its height says: on
840
+ // a plain beach the same as the way in, and past the front on a
841
+ // ledge or a rock top above the reach of the swash
842
+ this.$l.behind = pb.sub(this.front, pb.max(this.s, pb.mul(this.wl.z, this.bedB)));
843
+ this.$l.depth = pb.div(pb.mul(this.behind, this.behind), pb.mul(288, this.runUpH, this.bedB, pb.max(pb.mul(this.tt, this.tt), 1e-6)));
844
+ // Where the sheet is not, below the rest level and the bed: never
845
+ // lifting the mesh, which a bed high above the water would otherwise
846
+ // pull up through whatever stands there
847
+ this.$l.swash = this.$choice(pb.greaterThan(this.behind, 0), pb.add(this.wl.z, this.depth), pb.sub(pb.min(this.wl.z, 0), 1));
848
+ // Its water's velocity, u = ds_f/dt + (2/3)(s - s_f) / t, in metres per
849
+ // second: t is tt durations of periods
850
+ this.$l.seconds = pb.mul(this.duration, this.swShape.w);
851
+ this.flow = this.$choice(pb.and(pb.greaterThan(this.behind, 0), pb.lessThan(this.tt, 1)), pb.div(pb.sub(pb.mul(4, this.runUpH, pb.sub(1, pb.mul(2, this.tt))), pb.div(pb.mul(this.behind, 2 / 3), pb.max(this.tt, 1e-3))), this.seconds), pb.float(0));
852
+ // The crest collapsing at the waterline
853
+ this.$l.collapse = pb.mul(this.height, pb.exp(pb.div(pb.neg(this.s), SWASH_COLLAPSE)));
854
+ // The sheet becomes quadratically thin at its front: fading by depth
855
+ // leaves half coverage just inside it, then cuts to zero outside.
856
+ // Fade over the last centimetres behind the front instead, keeping
857
+ // the collapsing crest's own wet region where it reaches farther.
858
+ this.$l.swashCoverage = this.$choice(pb.and(pb.greaterThan(this.runUp, 0), pb.and(pb.greaterThan(this.tt, 0), pb.lessThan(this.tt, 1))), pb.smoothStep(0, SWASH_EDGE_SOFT, this.behind), pb.float(0));
859
+ this.coverage = pb.max(this.swashCoverage, pb.smoothStep(0, this.edgeSoft, pb.sub(this.collapse, this.wl.z)));
860
+ this.height = pb.max(this.swash, this.collapse);
861
+ // The swash runs up as a sheet, not squeezed
862
+ this.det = pb.float(1);
863
+ });
864
+ // No surf where the bed does not shelve (a wall, a hull, a rock or box
865
+ // standing out of the sea): the depth test meets the water with what
866
+ // stands there, as without shore waves. Cutting by the bed there opens
867
+ // holes along the object, whose cells read a bed above the rest level
868
+ // up to a cell outside it.
869
+ this.$if(pb.lessThanEqual(this.shelving, 0), function() {
870
+ this.coverage = pb.float(1);
871
+ });
872
+ this.surf.w = pb.max(pb.sub(this.height, this.wl.z), 0);
873
+ this.$return(pb.vec4(this.height, this.slope, this.det, this.flow));
874
+ });
875
+ if (!surf) {
876
+ const name = `swSurfUnused${surfLocals++}`;
877
+ scope.$l[name] = pb.vec4(0);
878
+ surf = scope[name];
879
+ }
880
+ return scope.swEvaluate(xz, dir, spacing, coverage, tPhase ?? scope.swWave.w, cell ?? pb.float(0), surf);
881
+ }
882
+ /**
883
+ * Adds the shore wave to a position the wrapped generator displaced, straight
884
+ * up, at a wave clock (time over period). Near the shore the wrapped
885
+ * generator's chop dies and the surface is held clear of the bed.
886
+ * @internal
887
+ */ displace(scope, inPos, basePos, tPhase, cell) {
888
+ const pb = scope.$builder;
889
+ const that = this;
890
+ pb.func('swDisplace', [
891
+ pb.vec3('inPos'),
892
+ pb.vec3('basePos'),
893
+ pb.float('tPhase'),
894
+ pb.float('cell')
895
+ ], function() {
896
+ this.$l.dir = pb.vec4(0);
897
+ this.$l.spacing = pb.float(0);
898
+ this.$l.coverage = pb.float(1);
899
+ // At the undisplaced position, where the fragment stage evaluates it too
900
+ this.$l.sw = that.evaluate(this, this.inPos.xz, this.dir, this.spacing, this.coverage, this.tPhase, this.cell);
901
+ this.$l.out = pb.mix(this.basePos, this.inPos, this.dir.z);
902
+ this.out.y = pb.max(pb.add(this.out.y, this.sw.x), pb.add(this.swGrid.w, this.dir.w, SWASH_CLEARANCE));
903
+ this.$return(this.out);
904
+ });
905
+ return scope.swDisplace(inPos, basePos, tPhase, cell);
906
+ }
907
+ calcVertexPositionAndNormal(scope, inPos, outPos, outNormal, vertexSpacing) {
908
+ const pb = scope.$builder;
909
+ const that = this;
910
+ pb.func('swCalcPositionAndNormal', [
911
+ pb.vec3('inPos'),
912
+ pb.vec3('outPos').out(),
913
+ pb.vec3('outNormal').out(),
914
+ pb.float('cell')
915
+ ], function() {
916
+ that._base.calcVertexPositionAndNormal(this, this.inPos, this.outPos, this.outNormal);
917
+ this.outPos = that.displace(this, this.inPos, this.outPos, this.swWave.w, this.cell);
918
+ });
919
+ scope.swCalcPositionAndNormal(inPos, outPos, outNormal, vertexSpacing ?? pb.float(0));
920
+ }
921
+ /**
922
+ * Where a vertex was in the previous frame, for motion vectors: the shore wave
923
+ * at the previous frame's clock. The wrapped generator is taken as it is now.
924
+ */ calcPrevVertexPosition(scope, inPos, outPos, vertexSpacing) {
925
+ const pb = scope.$builder;
926
+ const that = this;
927
+ pb.func('swCalcPrevPosition', [
928
+ pb.vec3('inPos'),
929
+ pb.vec3('outPos').out(),
930
+ pb.float('cell')
931
+ ], function() {
932
+ this.$l.n = pb.vec3(0, 1, 0);
933
+ that._base.calcVertexPositionAndNormal(this, this.inPos, this.outPos, this.n);
934
+ this.outPos = that.displace(this, this.inPos, this.outPos, this.swShape.z, this.cell);
935
+ });
936
+ scope.swCalcPrevPosition(inPos, outPos, vertexSpacing ?? pb.float(0));
937
+ }
938
+ /**
939
+ * Depth of the water over the bed of the waterline field, stable under the
940
+ * camera jitter. -1 where there is no bed to measure from, off the grid or
941
+ * away from the beach.
942
+ */ calcSheetThickness(scope, xz) {
943
+ const pb = scope.$builder;
944
+ const that = this;
945
+ pb.func('swSheetThickness', [
946
+ pb.vec2('xz')
947
+ ], function() {
948
+ this.$l.dir = pb.vec4(0);
949
+ this.$l.spacing = pb.float(0);
950
+ this.$l.coverage = pb.float(1);
951
+ this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
952
+ this.$if(pb.lessThan(this.dir.w, -1e3), function() {
953
+ this.$return(pb.float(-1));
954
+ });
955
+ this.$return(pb.max(pb.sub(this.sw.x, this.dir.w), 0));
956
+ });
957
+ return scope.swSheetThickness(xz);
958
+ }
959
+ /**
960
+ * The near-shore zone at an undisplaced XZ, as vec2(weight, foam density):
961
+ * weight 1 where the still water is shallower than the breaking depth and
962
+ * on the beach, 0 in the open water, off a beach or off the field; the
963
+ * density of the surf's foam just behind a crest there (see SURF_FOAM).
964
+ * Both depend only on the still-water depth, which is continuous from the
965
+ * open water across the waterline (0 inland).
966
+ */ nearShore(scope, xz) {
967
+ const pb = scope.$builder;
968
+ const that = this;
969
+ pb.func('swNearShore', [
970
+ pb.vec2('xz')
971
+ ], function() {
972
+ this.$l.wl = pb.vec4(0);
973
+ this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
974
+ this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
975
+ this.$return(pb.vec2(0));
976
+ });
977
+ // Still-water depth: the bed below the rest level, 0 inland
978
+ this.$l.h = pb.max(pb.neg(this.wl.z), 0);
979
+ this.$l.hn = pb.div(this.swWave.x, NEAR_SHORE_BREAKER);
980
+ this.$l.weight = pb.mul(pb.sub(1, pb.smoothStep(this.hn, pb.mul(this.hn, 1 + NEAR_SHORE_BLEND), this.h)), pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y), this.gridFade);
981
+ this.$return(pb.vec2(this.weight, pb.mix(NEAR_SHORE_FOAM_SHORE, NEAR_SHORE_FOAM_EDGE, pb.smoothStep(0, this.hn, this.h))));
982
+ });
983
+ return scope.swNearShore(xz);
984
+ }
985
+ /**
986
+ * Foam density shown at an undisplaced XZ from the field's: in the
987
+ * near-shore zone at least the surf's foam, which each crest brings in and
988
+ * which fades behind it (see SURF_FOAM).
989
+ */ calcNearShoreFoam(scope, xz, density) {
990
+ const pb = scope.$builder;
991
+ const that = this;
992
+ pb.func('swNearShoreFoam', [
993
+ pb.vec2('xz'),
994
+ pb.float('density')
995
+ ], function() {
996
+ this.$l.ns = that.nearShore(this, this.xz);
997
+ this.$if(pb.lessThanEqual(this.ns.x, 0), function() {
998
+ this.$return(this.density);
999
+ });
1000
+ this.$l.dir = pb.vec4(0);
1001
+ this.$l.spacing = pb.float(0);
1002
+ this.$l.coverage = pb.float(1);
1003
+ this.$l.surf = pb.vec4(0);
1004
+ that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage, undefined, undefined, this.surf);
1005
+ this.$return(pb.max(this.density, that.surfFoam(this, this.ns, this.surf)));
1006
+ });
1007
+ return scope.swNearShoreFoam(xz, density);
1008
+ }
1009
+ /**
1010
+ * Density of the surf's foam (see SURF_FOAM), from the near-shore zone (see
1011
+ * nearShore) and the wave's `surf` output of evaluate.
1012
+ * @internal
1013
+ */ surfFoam(scope, near, surf) {
1014
+ const pb = scope.$builder;
1015
+ pb.func('swSurfFoam', [
1016
+ pb.vec2('near'),
1017
+ pb.vec4('surf')
1018
+ ], function() {
1019
+ // The roller rises over the front face just before the crest, then fades;
1020
+ // the slower the shallower the water it rides up the beach on, as foam
1021
+ // settled there does in the field (see FOAM_SETTLE), so the swash keeps
1022
+ // the bore's foam rather than losing it on the way up
1023
+ this.$l.front = pb.smoothStep(1 - SURF_FOAM_FRONT, 1, this.surf.z);
1024
+ this.$l.settle = pb.sub(1, pb.smoothStep(this.swSettle.x, this.swSettle.y, this.surf.w));
1025
+ this.$l.rate = pb.mul(this.swVary.z, pb.mix(1, this.swSettle.z, this.settle));
1026
+ this.$return(pb.mul(this.near.x, this.near.y, pb.smoothStep(0, SURF_FOAM_FULL, this.surf.y), pb.max(pb.exp(pb.neg(pb.mul(this.surf.x, this.rate))), this.front)));
1027
+ });
1028
+ return scope.swSurfFoam(near, surf);
1029
+ }
1030
+ /**
1031
+ * The water's edge on the beach, per pixel: 1 where the water stands over the
1032
+ * bed, 0 where the mesh is only held clear of the sand or, past where it is
1033
+ * held, runs down into it.
1034
+ */ calcSurfaceCoverage(scope, xz) {
1035
+ const pb = scope.$builder;
1036
+ const that = this;
1037
+ pb.func('swSurfaceCoverage', [
1038
+ pb.vec2('xz')
1039
+ ], function() {
1040
+ this.$l.dir = pb.vec4(0);
1041
+ this.$l.spacing = pb.float(0);
1042
+ this.$l.coverage = pb.float(1);
1043
+ that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
1044
+ this.$return(this.coverage);
1045
+ });
1046
+ return scope.swSurfaceCoverage(xz);
1047
+ }
1048
+ /**
1049
+ * Debug classification on the same undisplaced coordinates as the mesh:
1050
+ * (shore envelope weight, inland flag, current wet coverage). The envelope
1051
+ * excludes the moving crest profile and wave-set strength, so its boundary
1052
+ * remains legible throughout a wave cycle. This is one displaced mesh.
1053
+ * @internal
1054
+ */ calcDebugRegion(scope, xz) {
1055
+ const pb = scope.$builder;
1056
+ const that = this;
1057
+ pb.func('swDebugRegion', [
1058
+ pb.vec2('xz')
1059
+ ], function() {
1060
+ this.$l.wl = pb.vec4(0);
1061
+ this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
1062
+ this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
1063
+ this.$return(pb.vec3(0, 0, 1));
1064
+ });
1065
+ // Match evaluate's shelving, grid-edge and outer-zone amplitude fades;
1066
+ // dir.z alone only measures the much narrower chop-suppression band.
1067
+ this.$l.weight = pb.mul(pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y), this.gridFade, pb.smoothStep(0, 0.4, pb.sub(1, pb.div(pb.max(this.wl.x, 0), this.swWave.y))));
1068
+ this.$l.dir = pb.vec4(0);
1069
+ this.$l.spacing = pb.float(0);
1070
+ this.$l.coverage = pb.float(1);
1071
+ that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
1072
+ this.$return(pb.vec3(this.weight, this.$choice(pb.lessThan(this.wl.x, 0), pb.float(1), pb.float(0)), this.coverage));
1073
+ });
1074
+ return scope.swDebugRegion(xz);
1075
+ }
1076
+ /**
1077
+ * Debug colour of the shore field's levels at an undisplaced XZ: red, green,
1078
+ * blue and yellow from the finest by their shares, times the fade out of
1079
+ * the coarsest, banded every 2 metres of the distance to the waterline.
1080
+ * @internal
1081
+ */ calcDebugLevels(scope, xz) {
1082
+ const pb = scope.$builder;
1083
+ const that = this;
1084
+ pb.func('swDebugLevels', [
1085
+ pb.vec2('xz')
1086
+ ], function() {
1087
+ this.$l.wl = pb.vec4(0);
1088
+ this.$l.levelWeights = pb.vec4(0);
1089
+ this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl, this.levelWeights);
1090
+ this.$if(pb.lessThan(this.gridFade, 0), function() {
1091
+ this.$return(pb.vec3(0.1));
1092
+ });
1093
+ this.$l.c = pb.div(pb.add(pb.add(pb.mul(pb.vec3(1, 0.1, 0.1), this.levelWeights.x), pb.mul(pb.vec3(0.1, 1, 0.1), this.levelWeights.y)), pb.add(pb.mul(pb.vec3(0.15, 0.3, 1), this.levelWeights.z), pb.mul(pb.vec3(1, 0.9, 0.1), this.levelWeights.w))), this.gridFade);
1094
+ this.$l.band = pb.step(0.5, pb.fract(pb.mul(this.wl.x, 0.5)));
1095
+ this.$return(pb.mul(this.c, this.gridFade, pb.mix(0.55, 1, this.band)));
1096
+ });
1097
+ return scope.swDebugLevels(xz);
1098
+ }
1099
+ /**
1100
+ * Normal tilted by the shore wave's slope, which runs along the direction to
1101
+ * the shore: the analytic dY/dX of the profile, so no extra evaluations.
1102
+ * @internal
1103
+ */ tilt(scope, n, sw, dir) {
1104
+ const pb = scope.$builder;
1105
+ // Gradient of the height in XZ: the slope along the distance, which grows seaward (-dir)
1106
+ const grad = pb.mul(pb.neg(dir.xy), sw.y);
1107
+ return pb.normalize(pb.add(pb.normalize(n), pb.vec3(pb.neg(grad.x), 0, pb.neg(grad.y))));
1108
+ }
1109
+ calcFragmentNormal(scope, xz, vertexNormal) {
1110
+ const pb = scope.$builder;
1111
+ const that = this;
1112
+ pb.func('swCalcFragmentNormal', [
1113
+ pb.vec2('xz'),
1114
+ pb.vec3('vn')
1115
+ ], function() {
1116
+ this.$l.dir = pb.vec4(0);
1117
+ this.$l.spacing = pb.float(0);
1118
+ this.$l.coverage = pb.float(1);
1119
+ this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
1120
+ this.$l.n = that._base.calcFragmentNormal(this, this.xz, this.vn);
1121
+ this.$return(that.tilt(this, this.n, this.sw, this.dir));
1122
+ });
1123
+ return scope.swCalcFragmentNormal(xz, vertexNormal);
1124
+ }
1125
+ calcFragmentNormalAndFoam(scope, xz, vertexNormal, foamSample, footprint) {
1126
+ const pb = scope.$builder;
1127
+ const that = this;
1128
+ const params = [
1129
+ pb.vec2('xz'),
1130
+ pb.vec3('vn')
1131
+ ];
1132
+ const args = [
1133
+ xz,
1134
+ vertexNormal
1135
+ ];
1136
+ if (foamSample) {
1137
+ params.push(pb.vec3('fs').inout());
1138
+ args.push(foamSample);
1139
+ }
1140
+ if (footprint) {
1141
+ params.push(pb.vec4('fp'));
1142
+ args.push(footprint);
1143
+ }
1144
+ const funcName = `swCalcNormalAndFoam${foamSample ? 'S' : ''}${footprint ? 'F' : ''}`;
1145
+ pb.func(funcName, params, function() {
1146
+ this.$l.dir = pb.vec4(0);
1147
+ this.$l.spacing = pb.float(0);
1148
+ this.$l.coverage = pb.float(1);
1149
+ this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
1150
+ // The foam the shore waves make is in the foam field, which the wrapped
1151
+ // generator reads
1152
+ this.$l.nf = that._base.calcFragmentNormalAndFoam(this, this.xz, this.vn, foamSample ? this.fs : undefined, footprint ? this.fp : undefined);
1153
+ this.$return(pb.vec4(that.tilt(this, this.nf.xyz, this.sw, this.dir), this.nf.w));
1154
+ });
1155
+ return scope[funcName](...args);
1156
+ }
1157
+ applyWaterBindGroup(bindGroup) {
1158
+ this._base.applyWaterBindGroup(bindGroup);
1159
+ this.applyShoreBindGroup(bindGroup);
1160
+ }
1161
+ calcClipmapTileAABB(minX, maxX, minZ, maxZ, y, outAABB) {
1162
+ this._base.calcClipmapTileAABB(minX, maxX, minZ, maxZ, y, outAABB);
1163
+ // The water rises by up to the wave height, and near its edge on the beach
1164
+ // the surface is held over the bed, a little higher still
1165
+ outAABB.maxPoint.y += this._settings.height + SWASH_LIFT_MARGIN + SWASH_CLEARANCE;
1166
+ }
1167
+ /** The material steps the base generator; the clock comes with {@link setFrame}. */ update() {}
1168
+ getHash() {
1169
+ return `${this._base.getHash()}:SW`;
1170
+ }
1171
+ needUpdate() {
1172
+ return true;
1173
+ }
1174
+ isOk(device) {
1175
+ return this._base.isOk(device);
1176
+ }
1177
+ onDispose() {
1178
+ super.onDispose();
1179
+ this._dummy?.dispose();
1180
+ this._dummy = null;
1181
+ }
1182
+ /** @internal */ getDummy() {
1183
+ if (!this._dummy) {
1184
+ this._dummy = getDevice().createTexture2DArray('rgba16f', 1, 1, 1, {
1185
+ mipmapping: false
1186
+ });
1187
+ }
1188
+ return this._dummy;
1189
+ }
1190
+ }
1191
+
1192
+ export { ShoreWaveGenerator };
1193
+ //# sourceMappingURL=shore_wave_generator.js.map