@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,865 @@
1
+ import { Vector3, Vector4 } from '@zephyr3d/base';
2
+ import { getDevice } from '../../app/api.js';
3
+ import { fetchSampler } from '../../utility/misc.js';
4
+ import { getDefaultTexture2D } from '../../utility/blueprint/material/texture.js';
5
+ import { MAX_DYNAMIC_SDF_INSTANCES, SDF_INSTANCE_VEC4S, globalSDFFormat, SDF_ATLAS_TEXTURE, SDF_DYNAMIC_INSTANCES, SDF_DYNAMIC_INFO, GLOBAL_SDF_TEXTURE, isGlobalSDFFilterable, GLOBAL_SDF_TAGS, GLOBAL_SDF_LEVELS, GLOBAL_SDF_PARAMS, GLOBAL_SDF_BRICK, sampleInstancesSDF, GLOBAL_SDF_TAG_NOT_COVERED, declareGlobalSDFUniforms, declareDynamicSDFUniforms, sampleSceneSDF } from '../../shaders/sdf.js';
6
+ import { WaterlineField, SDFWaterlineSource } from './waterline.js';
7
+ import { getSDFProgramErrors, buildSDFProgram } from './sdf_programs.js';
8
+ import { sampleInstanceMaterial } from '../../shaders/gi.js';
9
+ import { RenderGraphExecutor } from '../rendergraph/executor.js';
10
+
11
+ /** Voxels along a brick edge, the unit of compositing. */ const BRICK = GLOBAL_SDF_BRICK;
12
+ /** vec4s per brick in the brick buffer. */ const BRICK_VEC4S = 3;
13
+ /** Most points per {@link GlobalSDF.queryDistances} call. */ const MAX_QUERY_POINTS = 4096;
14
+ /**
15
+ * Writes an instance in the layout of {@link SDF_INSTANCE_VEC4S}: world-to-local rows, field box
16
+ * with voxel size and world scale, atlas slot with the two-sided half thickness (half a field
17
+ * voxel in world units; -1 for one-sided). The scale is the smallest axis scale so scaled field
18
+ * values never overshoot under non-uniform scale.
19
+ */ function packSDFInstance(inst, atlas, out, offset) {
20
+ const header = inst.asset.header;
21
+ const slot = atlas.getSlot(inst.asset);
22
+ const inv = inst.mesh.invWorldMatrix;
23
+ const m = inst.mesh.worldMatrix;
24
+ const scale = Math.min(Math.hypot(m.m00, m.m10, m.m20), Math.hypot(m.m01, m.m11, m.m21), Math.hypot(m.m02, m.m12, m.m22));
25
+ out.set([
26
+ inv.m00,
27
+ inv.m01,
28
+ inv.m02,
29
+ inv.m03
30
+ ], offset);
31
+ out.set([
32
+ inv.m10,
33
+ inv.m11,
34
+ inv.m12,
35
+ inv.m13
36
+ ], offset + 4);
37
+ out.set([
38
+ inv.m20,
39
+ inv.m21,
40
+ inv.m22,
41
+ inv.m23
42
+ ], offset + 8);
43
+ out.set([
44
+ ...header.boxMin,
45
+ header.voxelSize
46
+ ], offset + 12);
47
+ out.set([
48
+ ...header.boxMax,
49
+ scale
50
+ ], offset + 16);
51
+ out.set([
52
+ ...slot.offset,
53
+ header.twoSided ? 0.5 * header.voxelSize * scale : -1
54
+ ], offset + 20);
55
+ }
56
+ const tmpDirtyMin = new Vector3();
57
+ const tmpDirtyMax = new Vector3();
58
+ /**
59
+ * Camera-centred clipmap of signed distance (`levels` cubes of `resolution`³ voxels, each level
60
+ * doubling the voxel size), composited on the GPU from the static instances of a {@link SceneSDF}
61
+ * and the terrain height map. WebGPU only.
62
+ *
63
+ * Levels are toroidally addressed (world voxel `v` at texel `v mod resolution`) and updated
64
+ * incrementally in dirty 8³ bricks, a budget per frame, coarsest level first. Distances are
65
+ * clamped to a band of a few voxels per level; {@link sampleGlobalSDF} falls back to coarser
66
+ * levels outside it.
67
+ *
68
+ * @public
69
+ */ class GlobalSDF {
70
+ levels;
71
+ resolution;
72
+ baseVoxelSize;
73
+ bandVoxels;
74
+ brickBudget;
75
+ _sdf;
76
+ _texture;
77
+ _tags;
78
+ /** Window minimum of each level in voxels of that level; null before the first update. */ _origins;
79
+ /** Dirty bricks of each level, keyed by world brick coordinates. */ _dirty;
80
+ _lastBricks;
81
+ _freshBricks;
82
+ _freshCount;
83
+ _totalBricks;
84
+ _terrain;
85
+ _terrainVersion;
86
+ _program;
87
+ _bindGroup;
88
+ _instanceBuffer;
89
+ _brickBuffer;
90
+ _indexBuffer;
91
+ _materialBuffer;
92
+ _materials;
93
+ _dummyHeightMap;
94
+ _queryProgram;
95
+ _queryBindGroup;
96
+ _queryPoints;
97
+ _queryOutput;
98
+ _queryChain;
99
+ _levelInfo;
100
+ _dynamicData;
101
+ _dynamicInfo;
102
+ _waterline;
103
+ _contentVersion;
104
+ _staticVersion;
105
+ _prevDynamic;
106
+ _params;
107
+ constructor(sdf, options){
108
+ this._sdf = sdf;
109
+ this.levels = Math.max(1, Math.min(8, Math.floor(options?.levels ?? 4)));
110
+ this.resolution = Math.max(BRICK * 2, Math.ceil((options?.resolution ?? 128) / BRICK) * BRICK);
111
+ this.baseVoxelSize = Math.max(1e-3, options?.baseVoxelSize ?? 0.25);
112
+ this.bandVoxels = Math.max(1, options?.bandVoxels ?? 4);
113
+ this.brickBudget = Math.max(1, Math.floor(options?.brickBudget ?? 512));
114
+ this._texture = null;
115
+ this._tags = null;
116
+ this._origins = new Array(this.levels).fill(null);
117
+ this._dirty = Array.from({
118
+ length: this.levels
119
+ }, ()=>new Set());
120
+ this._lastBricks = 0;
121
+ this._freshBricks = new Float32Array(this.brickBudget * 4);
122
+ this._freshCount = 0;
123
+ this._totalBricks = 0;
124
+ this._terrain = null;
125
+ this._terrainVersion = -1;
126
+ this._program = null;
127
+ this._bindGroup = null;
128
+ this._instanceBuffer = null;
129
+ this._brickBuffer = null;
130
+ this._indexBuffer = null;
131
+ this._materialBuffer = null;
132
+ this._materials = null;
133
+ this._dummyHeightMap = null;
134
+ this._queryProgram = null;
135
+ this._queryBindGroup = null;
136
+ this._queryPoints = null;
137
+ this._queryOutput = null;
138
+ this._queryChain = Promise.resolve();
139
+ this._levelInfo = new Float32Array(this.levels * 4);
140
+ this._dynamicData = new Float32Array(MAX_DYNAMIC_SDF_INSTANCES * SDF_INSTANCE_VEC4S * 4);
141
+ this._dynamicInfo = new Vector4();
142
+ this._waterline = null;
143
+ this._contentVersion = 0;
144
+ this._staticVersion = 0;
145
+ this._prevDynamic = new Float32Array(this._dynamicData.length + 1);
146
+ this._params = new Vector4(this.resolution, this.levels, this.bandVoxels, 0);
147
+ }
148
+ /** True on a device that can build the field. */ static isSupported() {
149
+ return getDevice().type === 'webgpu';
150
+ }
151
+ /**
152
+ * The levels stacked along z in {@link globalSDFFormat}, `resolution + 1` slices each; the last
153
+ * copies the first so filtering wraps across the toroidal seam in z as repeat does in x and y.
154
+ */ get texture() {
155
+ if (!this._texture) {
156
+ const n = this.resolution;
157
+ this._texture = getDevice().createTexture3D(globalSDFFormat(), n, n, (n + 1) * this.levels, {
158
+ mipmapping: false,
159
+ writable: true
160
+ });
161
+ this._texture.name = '@GlobalSDF';
162
+ }
163
+ return this._texture;
164
+ }
165
+ /**
166
+ * One texel per brick, levels stacked along z: (world brick x, y, z, 1) of
167
+ * the brick last composited there. Starts all zero, which no brick matches.
168
+ */ get tags() {
169
+ if (!this._tags) {
170
+ const nb = this.resolution / BRICK;
171
+ this._tags = getDevice().createTexture3D('rgba32f', nb, nb, nb * this.levels, {
172
+ mipmapping: false,
173
+ writable: true
174
+ });
175
+ this._tags.name = '@GlobalSDFTags';
176
+ }
177
+ return this._tags;
178
+ }
179
+ /**
180
+ * Bricks whose materials the last update composited, for immediate voxel lighting: per brick
181
+ * (first material voxel within its level, level), `count` of them.
182
+ */ get freshMaterialBricks() {
183
+ return {
184
+ data: this._freshBricks,
185
+ count: this._freshCount
186
+ };
187
+ }
188
+ /** Voxel size of a level in world units. */ voxelSize(level) {
189
+ return this.baseVoxelSize * 2 ** level;
190
+ }
191
+ /** (window origin in voxels, voxel size) per level, the `levels` uniform of {@link sampleGlobalSDF}. */ get levelInfo() {
192
+ return this._levelInfo;
193
+ }
194
+ /**
195
+ * (resolution, levels, band voxels, current mask), the `params` uniform of
196
+ * {@link sampleGlobalSDF}; bit i of the mask set when every brick of level i
197
+ * is composited for its window, so readers can skip the tag checks there.
198
+ */ get params() {
199
+ return this._params;
200
+ }
201
+ /**
202
+ * Horizontal distance to the waterline at a water level, for shoreline
203
+ * effects; built on demand, see {@link WaterlineField.request}.
204
+ */ get waterline() {
205
+ if (!this._waterline) {
206
+ this._waterline = new WaterlineField(new SDFWaterlineSource(this));
207
+ }
208
+ return this._waterline;
209
+ }
210
+ /**
211
+ * Increments whenever the field may have changed (dirty region, bricks composited, dynamic
212
+ * instances moved), so derived data such as the waterline can skip rebuilding.
213
+ */ get contentVersion() {
214
+ return this._contentVersion;
215
+ }
216
+ /** As {@link GlobalSDF.contentVersion}, but for the clipmap only (static geometry and terrain). */ get staticVersion() {
217
+ return this._staticVersion;
218
+ }
219
+ /** Dynamic instances merged at query time this frame. */ get dynamicCount() {
220
+ return this._dynamicInfo.x;
221
+ }
222
+ /**
223
+ * The materials composited alongside the distances, or null for distances
224
+ * alone. Changing it rebuilds the whole field.
225
+ */ get materials() {
226
+ return this._materials;
227
+ }
228
+ set materials(val) {
229
+ if (val !== this._materials) {
230
+ this._materials = val;
231
+ this._program?.dispose();
232
+ this._program = null;
233
+ this._bindGroup?.dispose();
234
+ this._bindGroup = null;
235
+ this.invalidate();
236
+ }
237
+ }
238
+ /**
239
+ * Binds the field for {@link sampleSceneSDF} in a program that declared the
240
+ * global and the dynamic uniforms.
241
+ */ applyBindGroup(bindGroup) {
242
+ const atlas = this._sdf.atlas;
243
+ bindGroup.setTexture(SDF_ATLAS_TEXTURE, atlas.texture, fetchSampler('clamp_linear_nomip'));
244
+ bindGroup.setValue(SDF_DYNAMIC_INSTANCES, this._dynamicData);
245
+ bindGroup.setValue(SDF_DYNAMIC_INFO, this._dynamicInfo);
246
+ // Read with textureLoad, but the unfilterable binding still has a sampler
247
+ // slot, which a filtering sampler fails to validate against
248
+ bindGroup.setTexture(GLOBAL_SDF_TEXTURE, this.texture, fetchSampler(isGlobalSDFFilterable() ? 'repeat_linear_nomip' : 'clamp_nearest_nomip'));
249
+ bindGroup.setTexture(GLOBAL_SDF_TAGS, this.tags, fetchSampler('clamp_nearest_nomip'));
250
+ bindGroup.setValue(GLOBAL_SDF_LEVELS, this._levelInfo);
251
+ bindGroup.setValue(GLOBAL_SDF_PARAMS, this._params);
252
+ }
253
+ getStats() {
254
+ let dirty = 0;
255
+ for (const set of this._dirty){
256
+ dirty += set.size;
257
+ }
258
+ return {
259
+ levels: this.levels,
260
+ resolution: this.resolution,
261
+ voxelSizes: Array.from({
262
+ length: this.levels
263
+ }, (_, i)=>this.voxelSize(i)),
264
+ origins: this._origins.map((o, i)=>o ? o.map((v)=>v * this.voxelSize(i)) : [
265
+ 0,
266
+ 0,
267
+ 0
268
+ ]),
269
+ dirtyBricks: dirty,
270
+ lastBricks: this._lastBricks,
271
+ totalBricks: this._totalBricks,
272
+ terrain: !!this._terrain,
273
+ waterlineRebuilds: this._waterline?.rebuilds ?? 0
274
+ };
275
+ }
276
+ /** Shader compile errors of the composite and query programs, for diagnosis. */ getProgramErrors() {
277
+ return getSDFProgramErrors();
278
+ }
279
+ /** Marks every brick within band reach of a world box for rebuilding. */ markDirty(min, max) {
280
+ this._contentVersion++;
281
+ this._staticVersion++;
282
+ for(let level = 0; level < this.levels; level++){
283
+ const origin = this._origins[level];
284
+ if (!origin) {
285
+ continue;
286
+ }
287
+ const vs = this.voxelSize(level);
288
+ const reach = this.bandVoxels * vs;
289
+ const b = BRICK * vs;
290
+ const lo = [
291
+ min.x - reach,
292
+ min.y - reach,
293
+ min.z - reach
294
+ ].map((v)=>Math.floor(v / b));
295
+ const hi = [
296
+ max.x + reach,
297
+ max.y + reach,
298
+ max.z + reach
299
+ ].map((v)=>Math.floor(v / b));
300
+ const wlo = origin.map((v)=>v / BRICK);
301
+ const whi = wlo.map((v)=>v + this.resolution / BRICK - 1);
302
+ for(let z = Math.max(lo[2], wlo[2]); z <= Math.min(hi[2], whi[2]); z++){
303
+ for(let y = Math.max(lo[1], wlo[1]); y <= Math.min(hi[1], whi[1]); y++){
304
+ for(let x = Math.max(lo[0], wlo[0]); x <= Math.min(hi[0], whi[0]); x++){
305
+ this._dirty[level].add(`${x},${y},${z}`);
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ /** Marks everything for rebuilding. */ invalidate() {
312
+ for(let level = 0; level < this.levels; level++){
313
+ this._origins[level] = null;
314
+ this._dirty[level].clear();
315
+ }
316
+ }
317
+ /**
318
+ * Follows the camera and composites up to {@link GlobalSDF.brickBudget} dirty
319
+ * bricks. Called by the scene field once a frame, after its atlas uploads.
320
+ */ update(cameraPos, terrain) {
321
+ if (terrain !== this._terrain || terrain && terrain.heightVersion !== this._terrainVersion) {
322
+ const old = this._terrain;
323
+ this._terrain = terrain;
324
+ this._terrainVersion = terrain?.heightVersion ?? -1;
325
+ for (const t of [
326
+ old,
327
+ terrain
328
+ ]){
329
+ const box = t?.getWorldBoundingVolume()?.toAABB();
330
+ if (box) {
331
+ this.markDirty(box.minPoint, box.maxPoint);
332
+ }
333
+ }
334
+ }
335
+ if (this._materials && terrain) {
336
+ // Recapture the terrain albedo where it changed and recomposite the materials there
337
+ const rect = terrain.giAlbedo.update();
338
+ const box = rect ? terrain.getWorldBoundingVolume()?.toAABB() : null;
339
+ if (rect && box) {
340
+ this.markDirty(tmpDirtyMin.setXYZ(rect[0], box.minPoint.y, rect[1]), tmpDirtyMax.setXYZ(rect[2], box.maxPoint.y, rect[3]));
341
+ }
342
+ }
343
+ for(let level = 0; level < this.levels; level++){
344
+ this.moveWindow(level, cameraPos);
345
+ }
346
+ this.composite();
347
+ // Levels with nothing left to composite hold their whole window
348
+ let current = 0;
349
+ for(let level = 0; level < this.levels; level++){
350
+ if (this._origins[level] && this._dirty[level].size === 0) {
351
+ current |= 1 << level;
352
+ }
353
+ }
354
+ this._params.w = current;
355
+ this.gatherDynamic(cameraPos);
356
+ if (this._waterline) {
357
+ RenderGraphExecutor.profileScope('Waterline', ()=>this._waterline.update(cameraPos));
358
+ }
359
+ }
360
+ /** Packs the ready dynamic instances nearest the camera for query-time merging. @internal */ gatherDynamic(cameraPos) {
361
+ const reach = this.resolution * 0.5 * this.voxelSize(this.levels - 1);
362
+ const near = [];
363
+ for (const inst of this._sdf.instances){
364
+ if (inst.mode === 'dynamic' && this._sdf.isReady(inst)) {
365
+ const lo = inst.bounds.minPoint;
366
+ const hi = inst.bounds.maxPoint;
367
+ const d = Math.hypot(Math.max(lo.x - cameraPos.x, 0, cameraPos.x - hi.x), Math.max(lo.y - cameraPos.y, 0, cameraPos.y - hi.y), Math.max(lo.z - cameraPos.z, 0, cameraPos.z - hi.z));
368
+ if (d <= reach) {
369
+ near.push({
370
+ inst,
371
+ d
372
+ });
373
+ }
374
+ }
375
+ }
376
+ near.sort((a, b)=>a.d - b.d);
377
+ const count = Math.min(near.length, MAX_DYNAMIC_SDF_INSTANCES);
378
+ const atlas = this._sdf.atlas;
379
+ for(let i = 0; i < count; i++){
380
+ packSDFInstance(near[i].inst, atlas, this._dynamicData, i * SDF_INSTANCE_VEC4S * 4);
381
+ }
382
+ this._dynamicInfo.setXYZW(count, 1 / atlas.size[0], 1 / atlas.size[1], 1 / atlas.size[2]);
383
+ // Changed when an instance moved or the set changed
384
+ const used = count * SDF_INSTANCE_VEC4S * 4;
385
+ let changed = this._prevDynamic[0] !== count;
386
+ for(let i = 0; i < used && !changed; i++){
387
+ changed = this._prevDynamic[i + 1] !== this._dynamicData[i];
388
+ }
389
+ if (changed) {
390
+ this._prevDynamic[0] = count;
391
+ this._prevDynamic.set(this._dynamicData.subarray(0, used), 1);
392
+ this._contentVersion++;
393
+ }
394
+ }
395
+ /**
396
+ * Signed distances at world positions, read back from the GPU; resolves a
397
+ * frame or two later. For tools and gameplay queries, not per-frame use.
398
+ */ queryDistances(points) {
399
+ const run = async ()=>{
400
+ const n = Math.min(points.length, MAX_QUERY_POINTS);
401
+ const out = new Float32Array(points.length);
402
+ if (n === 0 || !this._texture) {
403
+ return out;
404
+ }
405
+ const device = getDevice();
406
+ const pixels = new Float32Array(MAX_QUERY_POINTS);
407
+ // Recorded at the start of a frame, like the water surface queries:
408
+ // outside one the dispatch has no command stream to land in
409
+ await device.runNextFrameAsync(async ()=>{
410
+ if (!this._queryProgram) {
411
+ this._queryProgram = this.createQueryProgram();
412
+ this._queryBindGroup = device.createBindGroup(this._queryProgram.bindGroupLayouts[0]);
413
+ this._queryPoints = device.createBuffer(MAX_QUERY_POINTS * 16, {
414
+ usage: 'uniform',
415
+ storage: true
416
+ });
417
+ this._queryOutput = device.createTexture2D('r32f', MAX_QUERY_POINTS, 1, {
418
+ mipmapping: false,
419
+ writable: true
420
+ });
421
+ }
422
+ const data = new Float32Array(MAX_QUERY_POINTS * 4);
423
+ for(let i = 0; i < n; i++){
424
+ data[i * 4] = points[i].x;
425
+ data[i * 4 + 1] = points[i].y;
426
+ data[i * 4 + 2] = points[i].z;
427
+ }
428
+ this._queryPoints.bufferSubData(0, data);
429
+ const bg = this._queryBindGroup;
430
+ bg.setBuffer('points', this._queryPoints);
431
+ bg.setTexture('result', this._queryOutput);
432
+ bg.setValue('count', new Vector4(n, 0, 0, 0));
433
+ this.applyBindGroup(bg);
434
+ device.setProgram(this._queryProgram);
435
+ device.setBindGroup(0, bg);
436
+ device.compute(Math.ceil(n / 64), 1, 1);
437
+ // The copy for the readback is recorded right after the dispatch, and
438
+ // flushed so it does not wait behind the frame's own passes
439
+ const read = this._queryOutput.readPixels(0, 0, MAX_QUERY_POINTS, 1, 0, 0, pixels);
440
+ device.flush();
441
+ await read;
442
+ });
443
+ out.set(pixels.subarray(0, n));
444
+ return out;
445
+ };
446
+ const next = this._queryChain.then(run, run);
447
+ this._queryChain = next.catch(()=>undefined);
448
+ return next;
449
+ }
450
+ dispose() {
451
+ this._waterline?.dispose();
452
+ this._waterline = null;
453
+ this._texture?.dispose();
454
+ this._texture = null;
455
+ this._tags?.dispose();
456
+ this._tags = null;
457
+ this._instanceBuffer?.dispose();
458
+ this._brickBuffer?.dispose();
459
+ this._indexBuffer?.dispose();
460
+ this._materialBuffer?.dispose();
461
+ this._dummyHeightMap?.dispose();
462
+ this._queryPoints?.dispose();
463
+ this._queryOutput?.dispose();
464
+ this._bindGroup?.dispose();
465
+ this._queryBindGroup?.dispose();
466
+ this._instanceBuffer = this._brickBuffer = this._indexBuffer = this._queryPoints = null;
467
+ this._materialBuffer = null;
468
+ this._dummyHeightMap = this._queryOutput = null;
469
+ this._bindGroup = this._queryBindGroup = null;
470
+ this.invalidate();
471
+ }
472
+ /** Re-centres a level on the camera, dirtying the bricks that entered. @internal */ moveWindow(level, cameraPos) {
473
+ const vs = this.voxelSize(level);
474
+ const n = this.resolution;
475
+ const nb = n / BRICK;
476
+ // Snapped to bricks so a brick always maps to one contiguous block of texels
477
+ const next = [
478
+ cameraPos.x,
479
+ cameraPos.y,
480
+ cameraPos.z
481
+ ].map((c)=>(Math.floor(c / (vs * BRICK)) - nb / 2) * BRICK);
482
+ const prev = this._origins[level];
483
+ if (prev && prev[0] === next[0] && prev[1] === next[1] && prev[2] === next[2]) {
484
+ return;
485
+ }
486
+ this._origins[level] = next;
487
+ this._levelInfo.set([
488
+ next[0],
489
+ next[1],
490
+ next[2],
491
+ vs
492
+ ], level * 4);
493
+ const lo = next.map((v)=>v / BRICK);
494
+ const plo = prev?.map((v)=>v / BRICK);
495
+ const dirty = this._dirty[level];
496
+ // Drop dirty bricks that left the window
497
+ for (const key of dirty){
498
+ const [x, y, z] = key.split(',').map(Number);
499
+ if (x < lo[0] || y < lo[1] || z < lo[2] || x >= lo[0] + nb || y >= lo[1] + nb || z >= lo[2] + nb) {
500
+ dirty.delete(key);
501
+ }
502
+ }
503
+ for(let z = lo[2]; z < lo[2] + nb; z++){
504
+ for(let y = lo[1]; y < lo[1] + nb; y++){
505
+ for(let x = lo[0]; x < lo[0] + nb; x++){
506
+ const inPrev = plo && x >= plo[0] && y >= plo[1] && z >= plo[2] && x < plo[0] + nb && y < plo[1] + nb && z < plo[2] + nb;
507
+ if (!inPrev) {
508
+ dirty.add(`${x},${y},${z}`);
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ /** Composites a budget of dirty bricks in one dispatch. @internal */ composite() {
515
+ // Coarse levels first: a coarse answer everywhere beats a fine one somewhere
516
+ const batch = [];
517
+ for(let level = this.levels - 1; level >= 0 && batch.length < this.brickBudget; level--){
518
+ const dirty = this._dirty[level];
519
+ for (const key of dirty){
520
+ if (batch.length >= this.brickBudget) {
521
+ break;
522
+ }
523
+ const [x, y, z] = key.split(',').map(Number);
524
+ batch.push({
525
+ level,
526
+ x,
527
+ y,
528
+ z
529
+ });
530
+ dirty.delete(key);
531
+ }
532
+ }
533
+ this._lastBricks = batch.length;
534
+ this._freshCount = 0;
535
+ // The budget can be raised at run time
536
+ if (this._freshBricks.length < batch.length * 4) {
537
+ this._freshBricks = new Float32Array(batch.length * 4);
538
+ }
539
+ if (batch.length === 0) {
540
+ return;
541
+ }
542
+ this._contentVersion++;
543
+ this._staticVersion++;
544
+ this._totalBricks += batch.length;
545
+ // Static instances whose field is on the GPU
546
+ const instances = [];
547
+ for (const inst of this._sdf.instances){
548
+ if (inst.mode === 'static' && this._sdf.isReady(inst)) {
549
+ instances.push(inst);
550
+ }
551
+ }
552
+ const atlas = this._sdf.atlas;
553
+ const instData = new Float32Array(Math.max(1, instances.length) * SDF_INSTANCE_VEC4S * 4);
554
+ instances.forEach((inst, i)=>packSDFInstance(inst, atlas, instData, i * SDF_INSTANCE_VEC4S * 4));
555
+ // Per brick: the instances whose bounds come within band reach of it
556
+ const brickData = new Float32Array(batch.length * BRICK_VEC4S * 4);
557
+ const indices = [];
558
+ const terrainBox = this._terrain?.getWorldBoundingVolume()?.toAABB() ?? null;
559
+ const region = this._terrain?.worldRegion ?? null;
560
+ const n = this.resolution;
561
+ batch.forEach((brick, b)=>{
562
+ const vs = this.voxelSize(brick.level);
563
+ const reach = this.bandVoxels * vs;
564
+ const size = BRICK * vs;
565
+ const min = [
566
+ brick.x * size - reach,
567
+ brick.y * size - reach,
568
+ brick.z * size - reach
569
+ ];
570
+ const max = [
571
+ min[0] + size + 2 * reach,
572
+ min[1] + size + 2 * reach,
573
+ min[2] + size + 2 * reach
574
+ ];
575
+ const start = indices.length;
576
+ // Whether a two-sided mesh comes near, which narrows the trace's surface
577
+ // expand in the brick (see SDF_NOT_COVERED_EXPAND_SCALE)
578
+ let twoSided = false;
579
+ instances.forEach((inst, i)=>{
580
+ const lo = inst.bounds.minPoint;
581
+ const hi = inst.bounds.maxPoint;
582
+ if (lo.x <= max[0] && hi.x >= min[0] && lo.y <= max[1] && hi.y >= min[1] && lo.z <= max[2] && hi.z >= min[2]) {
583
+ indices.push(i);
584
+ twoSided ||= !!inst.asset.header.twoSided;
585
+ }
586
+ });
587
+ const terrainHit = !!terrainBox && !!region && terrainBox.minPoint.y <= max[1] && terrainBox.maxPoint.y >= min[1] && region.x <= max[0] && region.z >= min[0] && region.y <= max[2] && region.w >= min[2];
588
+ const wrap = (v)=>(v * BRICK % n + n) % n;
589
+ const o = b * BRICK_VEC4S * 4;
590
+ brickData.set([
591
+ brick.x * BRICK,
592
+ brick.y * BRICK,
593
+ brick.z * BRICK,
594
+ vs
595
+ ], o);
596
+ if (this._materials) {
597
+ // Material voxels are 2³ field voxels
598
+ this._freshBricks.set([
599
+ wrap(brick.x) / 2,
600
+ wrap(brick.y) / 2,
601
+ wrap(brick.z) / 2,
602
+ brick.level
603
+ ], this._freshCount++ * 4);
604
+ }
605
+ brickData.set([
606
+ wrap(brick.x),
607
+ wrap(brick.y),
608
+ wrap(brick.z) + brick.level * n,
609
+ start
610
+ ], o + 4);
611
+ // y: bit 0 the terrain comes near, bit 1 a two-sided mesh does
612
+ brickData.set([
613
+ indices.length - start,
614
+ (terrainHit ? 1 : 0) + (twoSided ? 2 : 0),
615
+ reach,
616
+ brick.level
617
+ ], o + 8);
618
+ });
619
+ const device = getDevice();
620
+ const program = this.getProgram();
621
+ const bg = this._bindGroup;
622
+ this._instanceBuffer = this.ensureBuffer(this._instanceBuffer, instData.byteLength);
623
+ this._instanceBuffer.bufferSubData(0, instData);
624
+ this._brickBuffer = this.ensureBuffer(this._brickBuffer, brickData.byteLength);
625
+ this._brickBuffer.bufferSubData(0, brickData);
626
+ const indexData = new Uint32Array(Math.max(4, indices.length));
627
+ indexData.set(indices);
628
+ this._indexBuffer = this.ensureBuffer(this._indexBuffer, indexData.byteLength);
629
+ this._indexBuffer.bufferSubData(0, indexData);
630
+ bg.setBuffer('instances', this._instanceBuffer);
631
+ bg.setBuffer('bricks', this._brickBuffer);
632
+ bg.setBuffer('indices', this._indexBuffer);
633
+ const materials = this._materials;
634
+ if (materials) {
635
+ // Per instance: (atlas slot offset, 1 when resident), (slot resolution, 0)
636
+ const matData = new Float32Array(Math.max(1, instances.length) * 8);
637
+ instances.forEach((inst, i)=>{
638
+ const slot = materials.getSlot(inst);
639
+ if (slot) {
640
+ matData.set([
641
+ slot.offset[0],
642
+ slot.offset[1],
643
+ slot.offset[2],
644
+ 1
645
+ ], i * 8);
646
+ matData.set([
647
+ slot.resolution[0],
648
+ slot.resolution[1],
649
+ slot.resolution[2],
650
+ 0
651
+ ], i * 8 + 4);
652
+ }
653
+ });
654
+ this._materialBuffer = this.ensureBuffer(this._materialBuffer, matData.byteLength);
655
+ this._materialBuffer.bufferSubData(0, matData);
656
+ bg.setBuffer('materialSlots', this._materialBuffer);
657
+ bg.setTexture('matAlbedoAtlas', materials.atlasAlbedo, fetchSampler('clamp_linear_nomip'));
658
+ // RGBE must not be filtered
659
+ bg.setTexture('matEmissiveAtlas', materials.atlasEmissive, fetchSampler('clamp_nearest_nomip'));
660
+ const s = materials.atlasSize;
661
+ bg.setValue('matAtlasInvSize', new Vector4(1 / s[0], 1 / s[1], 1 / s[2], 0));
662
+ const terrainAlbedo = this._terrain?.giAlbedo.texture ?? null;
663
+ bg.setTexture('terrainAlbedo', terrainAlbedo ?? getDefaultTexture2D(), fetchSampler('clamp_linear'));
664
+ bg.setValue('terrainAlbedoSize', new Vector4(terrainAlbedo?.width ?? 1, terrainAlbedo?.height ?? 1, 0, 0));
665
+ bg.setTexture('albedoOut', materials.albedo);
666
+ bg.setTexture('emissiveOut', materials.emissive);
667
+ }
668
+ bg.setTexture('atlas', atlas.texture, fetchSampler('clamp_linear_nomip'));
669
+ bg.setValue('atlasInvSize', new Vector4(1 / atlas.size[0], 1 / atlas.size[1], 1 / atlas.size[2], 0));
670
+ const terrain = this._terrain;
671
+ const heightMap = terrain?.heightMap ?? null;
672
+ bg.setTexture('heightMap', heightMap ?? this.getDummyHeightMap(), fetchSampler('clamp_linear_nomip'));
673
+ if (terrain && heightMap && region) {
674
+ bg.setValue('terrainRegion', new Vector4(region.x, region.y, region.z, region.w));
675
+ bg.setValue('terrainParams', new Vector4(Math.abs(terrain.scale.y), terrain.worldMatrix.m13, 1 / heightMap.width, 1 / heightMap.height));
676
+ } else {
677
+ bg.setValue('terrainRegion', new Vector4(0, 0, 1, 1));
678
+ bg.setValue('terrainParams', new Vector4(0, 0, 1, 1));
679
+ }
680
+ bg.setTexture('field', this.texture);
681
+ bg.setTexture('tagsOut', this.tags);
682
+ device.setProgram(program);
683
+ device.setBindGroup(0, bg);
684
+ device.compute(batch.length * 8, 1, 1);
685
+ }
686
+ /** @internal */ ensureBuffer(buffer, bytes) {
687
+ if (buffer && buffer.byteLength >= bytes) {
688
+ return buffer;
689
+ }
690
+ buffer?.dispose();
691
+ // Grow by 1.5x to avoid per-frame reallocation; whole vec4s since the storage binding covers
692
+ // the whole buffer
693
+ return getDevice().createBuffer(Math.max(256, Math.ceil(bytes * 1.5 / 16) * 16), {
694
+ usage: 'uniform',
695
+ storage: true
696
+ });
697
+ }
698
+ /** @internal */ getDummyHeightMap() {
699
+ if (!this._dummyHeightMap) {
700
+ this._dummyHeightMap = getDevice().createTexture2D('r16f', 1, 1, {
701
+ mipmapping: false
702
+ });
703
+ }
704
+ return this._dummyHeightMap;
705
+ }
706
+ /** @internal */ getProgram() {
707
+ if (!this._program) {
708
+ const device = getDevice();
709
+ const materials = !!this._materials;
710
+ const fieldRes = this.resolution;
711
+ this._program = buildSDFProgram({
712
+ label: materials ? 'GlobalSDFCompositeMaterials' : 'GlobalSDFComposite',
713
+ workgroupSize: [
714
+ 4,
715
+ 4,
716
+ 4
717
+ ],
718
+ compute (pb) {
719
+ this.instances = pb.vec4[0]().storageBufferReadonly(0);
720
+ this.bricks = pb.vec4[0]().storageBufferReadonly(0);
721
+ this.indices = pb.uint[0]().storageBufferReadonly(0);
722
+ this.atlas = pb.tex3D().uniform(0);
723
+ this.atlasInvSize = pb.vec4().uniform(0);
724
+ this.heightMap = pb.tex2D().uniform(0);
725
+ this.terrainRegion = pb.vec4().uniform(0);
726
+ // (height scale, world y, 1 / width, 1 / height)
727
+ this.terrainParams = pb.vec4().uniform(0);
728
+ this.field = globalSDFFormat() === 'r16f' ? pb.texStorage3D.r16float().storage(0) : pb.texStorage3D.r32float().storage(0);
729
+ this.tagsOut = pb.texStorage3D.rgba32float().storage(0);
730
+ if (materials) {
731
+ this.materialSlots = pb.vec4[0]().storageBufferReadonly(0);
732
+ this.matAlbedoAtlas = pb.tex3D().uniform(0);
733
+ this.matEmissiveAtlas = pb.tex3D().uniform(0);
734
+ this.matAtlasInvSize = pb.vec4().uniform(0);
735
+ // Top-down capture of the terrain material albedo, see TerrainGIAlbedo
736
+ this.terrainAlbedo = pb.tex2D().uniform(0);
737
+ // (capture width, capture height, 0, 0)
738
+ this.terrainAlbedoSize = pb.vec4().uniform(0);
739
+ this.albedoOut = pb.texStorage3D.rgba8unorm().storage(0);
740
+ this.emissiveOut = pb.texStorage3D.rgba16float().storage(0);
741
+ }
742
+ pb.func('terrainHeight', [
743
+ pb.vec2('xz')
744
+ ], function() {
745
+ this.$l.uv = pb.div(pb.sub(this.xz, this.terrainRegion.xy), pb.sub(this.terrainRegion.zw, this.terrainRegion.xy));
746
+ this.$return(pb.add(pb.mul(pb.textureSampleLevel(this.heightMap, this.uv, 0).r, this.terrainParams.x), this.terrainParams.y));
747
+ });
748
+ pb.main(function() {
749
+ this.$l.brick = pb.div(this.$builtins.workGroupId.x, 8);
750
+ this.$l.sub = pb.sub(this.$builtins.workGroupId.x, pb.mul(this.brick, 8));
751
+ this.$l.local = pb.add(pb.mul(pb.uvec3(pb.mod(this.sub, 2), pb.mod(pb.div(this.sub, 2), 2), pb.div(this.sub, 4)), 4), this.$builtins.localInvocationId);
752
+ this.$l.b0 = this.bricks.at(pb.mul(this.brick, BRICK_VEC4S));
753
+ this.$l.b1 = this.bricks.at(pb.add(pb.mul(this.brick, BRICK_VEC4S), 1));
754
+ this.$l.b2 = this.bricks.at(pb.add(pb.mul(this.brick, BRICK_VEC4S), 2));
755
+ this.$l.vs = this.b0.w;
756
+ this.$l.band = this.b2.z;
757
+ this.$l.p = pb.mul(pb.add(this.b0.xyz, pb.add(pb.vec3(this.local), pb.vec3(0.5))), this.vs);
758
+ this.$l.d = this.band;
759
+ this.$l.start = pb.uint(this.b1.w);
760
+ this.$l.count = pb.uint(this.b2.x);
761
+ this.d = sampleInstancesSDF(this, this.p, this.count, this.d, (scope, k)=>pb.mul(scope.indices.at(pb.add(scope.start, k)), SDF_INSTANCE_VEC4S), (scope, i)=>scope.instances.at(i), this.atlas, this.atlasInvSize.xyz, pb.mul(this.vs, 0.5));
762
+ this.$l.flags = pb.uint(this.b2.y);
763
+ this.$l.terrainD = this.band;
764
+ this.$if(pb.notEqual(pb.compAnd(this.flags, pb.uint(1)), pb.uint(0)), function() {
765
+ this.$l.inRegion = pb.and(pb.all(pb.greaterThanEqual(this.p.xz, this.terrainRegion.xy)), pb.all(pb.lessThanEqual(this.p.xz, this.terrainRegion.zw)));
766
+ this.$if(this.inRegion, function() {
767
+ this.$l.h = this.terrainHeight(this.p.xz);
768
+ // Central differences one height map texel apart, in world units
769
+ this.$l.cell = pb.mul(pb.sub(this.terrainRegion.zw, this.terrainRegion.xy), this.terrainParams.zw);
770
+ this.$l.hx = pb.sub(this.terrainHeight(pb.add(this.p.xz, pb.vec2(this.cell.x, 0))), this.terrainHeight(pb.sub(this.p.xz, pb.vec2(this.cell.x, 0))));
771
+ this.$l.hz = pb.sub(this.terrainHeight(pb.add(this.p.xz, pb.vec2(0, this.cell.y))), this.terrainHeight(pb.sub(this.p.xz, pb.vec2(0, this.cell.y))));
772
+ this.$l.grad = pb.div(pb.vec2(this.hx, this.hz), pb.mul(this.cell, 2));
773
+ // Height above the surface over the slope: exact on a plane
774
+ this.terrainD = pb.min(this.terrainD, pb.div(pb.sub(this.p.y, this.h), pb.sqrt(pb.add(1, pb.dot(this.grad, this.grad)))));
775
+ });
776
+ });
777
+ this.d = pb.clamp(pb.min(this.d, this.terrainD), pb.neg(this.band), this.band);
778
+ // b1 addresses the level as `resolution` slices; the field has one more each
779
+ this.$l.level = pb.uint(this.b2.w);
780
+ this.$l.ft = pb.add(pb.uvec3(this.b1.xyz), this.local);
781
+ this.ft.z = pb.add(this.ft.z, this.level);
782
+ pb.textureStore(this.field, this.ft, pb.vec4(this.d, 0, 0, 0));
783
+ // The first slice of the window again past the last, the seam the filter crosses
784
+ this.$l.res = pb.uint(fieldRes);
785
+ this.$if(pb.and(pb.equal(this.local.z, 0), pb.equal(pb.uint(this.b1.z), pb.mul(this.level, this.res))), function() {
786
+ pb.textureStore(this.field, pb.uvec3(this.ft.x, this.ft.y, pb.add(pb.mul(this.level, pb.add(this.res, 1)), this.res)), pb.vec4(this.d, 0, 0, 0));
787
+ });
788
+ // Tag the brick with its world brick coords (visible to readers next pass); alpha
789
+ // flags two-sided meshes
790
+ this.$if(pb.and(pb.equal(this.sub, 0), pb.equal(this.$builtins.localInvocationId, pb.uvec3(0))), function() {
791
+ pb.textureStore(this.tagsOut, pb.div(pb.uvec3(this.b1.xyz), BRICK), pb.vec4(pb.div(this.b0.xyz, BRICK), this.$choice(pb.notEqual(pb.compAnd(this.flags, pb.uint(2)), pb.uint(0)), pb.float(GLOBAL_SDF_TAG_NOT_COVERED), pb.float(1))));
792
+ });
793
+ if (materials) {
794
+ // Material voxels are 2³ field voxels: the even-corner thread writes it, sampled at
795
+ // its centre from the nearest instance (or the terrain where nearer)
796
+ this.$if(pb.equal(pb.mod(this.local, pb.uvec3(2)), pb.uvec3(0)), function() {
797
+ this.$l.pc = pb.add(this.p, pb.vec3(pb.mul(this.vs, 0.5)));
798
+ this.$l.bestK = pb.int(-1);
799
+ this.$l.instD = sampleInstancesSDF(this, this.pc, this.count, this.band, (scope, k)=>pb.mul(scope.indices.at(pb.add(scope.start, k)), SDF_INSTANCE_VEC4S), (scope, i)=>scope.instances.at(i), this.atlas, this.atlasInvSize.xyz, pb.mul(this.vs, 0.5), 'bestK');
800
+ this.$l.terrainWins = pb.and(pb.lessThan(this.terrainD, this.band), pb.lessThan(this.terrainD, this.instD));
801
+ this.$l.useInst = pb.and(pb.greaterThanEqual(this.bestK, 0), pb.lessThan(this.instD, this.band), pb.not(this.terrainWins));
802
+ this.$l.idx = this.$choice(this.useInst, this.indices.at(pb.add(this.start, pb.uint(pb.max(this.bestK, 0)))), pb.uint(0));
803
+ this.$l.ibase = pb.mul(this.idx, SDF_INSTANCE_VEC4S);
804
+ this.$l.mr0 = this.instances.at(this.ibase);
805
+ this.$l.mr1 = this.instances.at(pb.add(this.ibase, 1));
806
+ this.$l.mr2 = this.instances.at(pb.add(this.ibase, 2));
807
+ this.$l.mbmin = this.instances.at(pb.add(this.ibase, 3));
808
+ this.$l.mbmax = this.instances.at(pb.add(this.ibase, 4));
809
+ this.$l.mslot = this.instances.at(pb.add(this.ibase, 5));
810
+ this.$l.matSlot = this.$choice(this.useInst, this.materialSlots.at(pb.mul(this.idx, 2)), pb.vec4(0));
811
+ this.$l.matRes = this.materialSlots.at(pb.add(pb.mul(this.idx, 2), 1));
812
+ this.$l.pc4 = pb.vec4(this.pc, 1);
813
+ this.$l.lp = pb.vec3(pb.dot(this.mr0, this.pc4), pb.dot(this.mr1, this.pc4), pb.dot(this.mr2, this.pc4));
814
+ sampleInstanceMaterial(this, this.lp, this.mbmin, this.mbmax, this.mslot, this.matSlot, this.matRes, this.atlas, this.atlasInvSize.xyz, this.matAlbedoAtlas, this.matEmissiveAtlas, this.matAtlasInvSize.xyz, 'outAlbedo', 'outEmissive');
815
+ this.$if(this.terrainWins, function() {
816
+ // The capture's mip whose texels match the material voxel (two field voxels),
817
+ // so coarse levels read the average over their footprint
818
+ this.$l.tregion = pb.sub(this.terrainRegion.zw, this.terrainRegion.xy);
819
+ this.$l.tuv = pb.div(pb.sub(this.pc.xz, this.terrainRegion.xy), this.tregion);
820
+ this.$l.ttexel = pb.max(pb.div(this.tregion.x, this.terrainAlbedoSize.x), pb.div(this.tregion.y, this.terrainAlbedoSize.y));
821
+ this.$l.tlod = pb.max(pb.log2(pb.div(pb.mul(this.vs, 2), this.ttexel)), 0);
822
+ this.outAlbedo = pb.vec4(pb.textureSampleLevel(this.terrainAlbedo, this.tuv, this.tlod).rgb, 1);
823
+ this.outEmissive = pb.vec3(0);
824
+ });
825
+ this.$l.ct = pb.div(pb.add(pb.uvec3(this.b1.xyz), this.local), pb.uvec3(2));
826
+ pb.textureStore(this.albedoOut, this.ct, this.outAlbedo);
827
+ pb.textureStore(this.emissiveOut, this.ct, pb.vec4(this.outEmissive, 0));
828
+ });
829
+ }
830
+ });
831
+ }
832
+ });
833
+ this._bindGroup = device.createBindGroup(this._program.bindGroupLayouts[0]);
834
+ }
835
+ return this._program;
836
+ }
837
+ /** @internal */ createQueryProgram() {
838
+ const levels = this.levels;
839
+ return buildSDFProgram({
840
+ label: 'GlobalSDFQuery',
841
+ workgroupSize: [
842
+ 64,
843
+ 1,
844
+ 1
845
+ ],
846
+ compute (pb) {
847
+ this.points = pb.vec4[0]().storageBufferReadonly(0);
848
+ this.count = pb.vec4().uniform(0);
849
+ this.result = pb.texStorage2D.r32float().storage(0);
850
+ declareGlobalSDFUniforms(this, 0, levels);
851
+ declareDynamicSDFUniforms(this, 0);
852
+ pb.main(function() {
853
+ this.$l.i = this.$builtins.globalInvocationId.x;
854
+ this.$if(pb.lessThan(this.i, pb.uint(this.count.x)), function() {
855
+ this.$l.d = sampleSceneSDF(this, this.points.at(this.i).xyz, levels);
856
+ pb.textureStore(this.result, pb.uvec2(this.i, 0), pb.vec4(this.d, 0, 0, 0));
857
+ });
858
+ });
859
+ }
860
+ });
861
+ }
862
+ }
863
+
864
+ export { GlobalSDF };
865
+ //# sourceMappingURL=global_sdf.js.map