@zephyr3d/scene 0.9.27 → 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 (502) 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 +54 -0
  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 +279 -55
  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 +171 -1
  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 +104 -178
  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 +25436 -15990
  109. package/dist/index.js +65 -11
  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/lambert.js +4 -1
  118. package/dist/material/lambert.js.map +1 -1
  119. package/dist/material/material.js +66 -4
  120. package/dist/material/material.js.map +1 -1
  121. package/dist/material/meshmaterial.js +118 -45
  122. package/dist/material/meshmaterial.js.map +1 -1
  123. package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
  124. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  125. package/dist/material/mixins/lightmodel/lambert.js +1 -1
  126. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  127. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
  128. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  129. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
  130. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  131. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
  132. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  133. package/dist/material/mixins/lit.js +191 -83
  134. package/dist/material/mixins/lit.js.map +1 -1
  135. package/dist/material/mixins/pbr/common.js +31 -206
  136. package/dist/material/mixins/pbr/common.js.map +1 -1
  137. package/dist/material/mtoon.js +1 -1
  138. package/dist/material/mtoon.js.map +1 -1
  139. package/dist/material/pbrmr.js +3 -0
  140. package/dist/material/pbrmr.js.map +1 -1
  141. package/dist/material/pbrsg.js +3 -0
  142. package/dist/material/pbrsg.js.map +1 -1
  143. package/dist/material/sdf_debug.js +78 -0
  144. package/dist/material/sdf_debug.js.map +1 -0
  145. package/dist/material/shader/helper.js +333 -191
  146. package/dist/material/shader/helper.js.map +1 -1
  147. package/dist/material/shader/terrain_blend.js +204 -43
  148. package/dist/material/shader/terrain_blend.js.map +1 -1
  149. package/dist/material/skin.js +331 -297
  150. package/dist/material/skin.js.map +1 -1
  151. package/dist/material/skinprofile.js +1058 -0
  152. package/dist/material/skinprofile.js.map +1 -0
  153. package/dist/material/sss.js +353 -0
  154. package/dist/material/sss.js.map +1 -0
  155. package/dist/material/sss_kernel.js +265 -0
  156. package/dist/material/sss_kernel.js.map +1 -0
  157. package/dist/material/sssprofile.js +879 -0
  158. package/dist/material/sssprofile.js.map +1 -0
  159. package/dist/material/subsurfaceprofile.js +4 -8
  160. package/dist/material/subsurfaceprofile.js.map +1 -1
  161. package/dist/material/terrain-cm.js +464 -188
  162. package/dist/material/terrain-cm.js.map +1 -1
  163. package/dist/material/unlit.js +3 -0
  164. package/dist/material/unlit.js.map +1 -1
  165. package/dist/material/water.js +1261 -1438
  166. package/dist/material/water.js.map +1 -1
  167. package/dist/node_modules/meshoptimizer/LICENSE +21 -0
  168. package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
  169. package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
  170. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
  171. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
  172. package/dist/physics/character.js +226 -0
  173. package/dist/physics/character.js.map +1 -0
  174. package/dist/physics/collider.js +209 -0
  175. package/dist/physics/collider.js.map +1 -0
  176. package/dist/physics/events.js +79 -0
  177. package/dist/physics/events.js.map +1 -0
  178. package/dist/physics/joint.js +272 -0
  179. package/dist/physics/joint.js.map +1 -0
  180. package/dist/physics/node_physics.js +234 -0
  181. package/dist/physics/node_physics.js.map +1 -0
  182. package/dist/physics/part.js +91 -0
  183. package/dist/physics/part.js.map +1 -0
  184. package/dist/physics/rigid_body.js +379 -0
  185. package/dist/physics/rigid_body.js.map +1 -0
  186. package/dist/physics/vehicle.js +335 -0
  187. package/dist/physics/vehicle.js.map +1 -0
  188. package/dist/physics/world.js +25 -0
  189. package/dist/physics/world.js.map +1 -0
  190. package/dist/posteffect/bloom.js +49 -14
  191. package/dist/posteffect/bloom.js.map +1 -1
  192. package/dist/posteffect/motionblur.js +106 -106
  193. package/dist/posteffect/motionblur.js.map +1 -1
  194. package/dist/posteffect/postsss.js +572 -0
  195. package/dist/posteffect/postsss.js.map +1 -0
  196. package/dist/posteffect/sao.js +2 -6
  197. package/dist/posteffect/sao.js.map +1 -1
  198. package/dist/posteffect/skinsss.js +605 -355
  199. package/dist/posteffect/skinsss.js.map +1 -1
  200. package/dist/posteffect/ssgi.js +91 -162
  201. package/dist/posteffect/ssgi.js.map +1 -1
  202. package/dist/posteffect/ssr.js +3 -11
  203. package/dist/posteffect/ssr.js.map +1 -1
  204. package/dist/posteffect/sss.js +4 -13
  205. package/dist/posteffect/sss.js.map +1 -1
  206. package/dist/posteffect/taa.js +1 -0
  207. package/dist/posteffect/taa.js.map +1 -1
  208. package/dist/render/abuffer_oit.js +3 -2
  209. package/dist/render/abuffer_oit.js.map +1 -1
  210. package/dist/render/clipmap.js +179 -64
  211. package/dist/render/clipmap.js.map +1 -1
  212. package/dist/render/cluster_light.js +958 -224
  213. package/dist/render/cluster_light.js.map +1 -1
  214. package/dist/render/cull_visitor.js +25 -5
  215. package/dist/render/cull_visitor.js.map +1 -1
  216. package/dist/render/debug_views.js +37 -0
  217. package/dist/render/debug_views.js.map +1 -0
  218. package/dist/render/depthpass.js +5 -1
  219. package/dist/render/depthpass.js.map +1 -1
  220. package/dist/render/drawable_mixin.js +53 -6
  221. package/dist/render/drawable_mixin.js.map +1 -1
  222. package/dist/render/dualdepthpeeling_oit.js +5 -5
  223. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  224. package/dist/render/envlight.js +196 -113
  225. package/dist/render/envlight.js.map +1 -1
  226. package/dist/render/fbm_wavegenerator.js +5 -10
  227. package/dist/render/fbm_wavegenerator.js.map +1 -1
  228. package/dist/render/fft_wavegenerator.js +247 -157
  229. package/dist/render/fft_wavegenerator.js.map +1 -1
  230. package/dist/render/gerstner_wavegenerator.js +16 -38
  231. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  232. package/dist/render/gi/probe_volume.js +1334 -0
  233. package/dist/render/gi/probe_volume.js.map +1 -0
  234. package/dist/render/gi/scene_gi.js +282 -0
  235. package/dist/render/gi/scene_gi.js.map +1 -0
  236. package/dist/render/gi/voxel_atlas.js +198 -0
  237. package/dist/render/gi/voxel_atlas.js.map +1 -0
  238. package/dist/render/gi/voxel_lighting.js +652 -0
  239. package/dist/render/gi/voxel_lighting.js.map +1 -0
  240. package/dist/render/gi/voxel_scene.js +642 -0
  241. package/dist/render/gi/voxel_scene.js.map +1 -0
  242. package/dist/render/gi/voxelize_pass.js +223 -0
  243. package/dist/render/gi/voxelize_pass.js.map +1 -0
  244. package/dist/render/globalbindgroup_allocator.js +3 -2
  245. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  246. package/dist/render/gpu_picking.js +9 -36
  247. package/dist/render/gpu_picking.js.map +1 -1
  248. package/dist/render/instance_culling.js +499 -0
  249. package/dist/render/instance_culling.js.map +1 -0
  250. package/dist/render/lightpass.js +19 -8
  251. package/dist/render/lightpass.js.map +1 -1
  252. package/dist/render/lod.js +46 -0
  253. package/dist/render/lod.js.map +1 -0
  254. package/dist/render/primitive.js +301 -9
  255. package/dist/render/primitive.js.map +1 -1
  256. package/dist/render/primitive_readback.js +209 -0
  257. package/dist/render/primitive_readback.js.map +1 -0
  258. package/dist/render/render_queue.js +162 -11
  259. package/dist/render/render_queue.js.map +1 -1
  260. package/dist/render/renderer.js +10 -7
  261. package/dist/render/renderer.js.map +1 -1
  262. package/dist/render/rendergraph/blackboard.js +14 -1
  263. package/dist/render/rendergraph/blackboard.js.map +1 -1
  264. package/dist/render/rendergraph/executor.js +136 -0
  265. package/dist/render/rendergraph/executor.js.map +1 -1
  266. package/dist/render/rendergraph/forward_plus_builder.js +272 -55
  267. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  268. package/dist/render/rendergraph/frame_graph_context.js +41 -0
  269. package/dist/render/rendergraph/frame_graph_context.js.map +1 -0
  270. package/dist/render/rendergraph/scene_render_context.js +31 -2
  271. package/dist/render/rendergraph/scene_render_context.js.map +1 -1
  272. package/dist/render/rendergraph/types.js.map +1 -1
  273. package/dist/render/renderpass.js +28 -5
  274. package/dist/render/renderpass.js.map +1 -1
  275. package/dist/render/sdf/box_allocator.js +98 -0
  276. package/dist/render/sdf/box_allocator.js.map +1 -0
  277. package/dist/render/sdf/global_sdf.js +865 -0
  278. package/dist/render/sdf/global_sdf.js.map +1 -0
  279. package/dist/render/sdf/scene_sdf.js +562 -0
  280. package/dist/render/sdf/scene_sdf.js.map +1 -0
  281. package/dist/render/sdf/sdf_atlas.js +227 -0
  282. package/dist/render/sdf/sdf_atlas.js.map +1 -0
  283. package/dist/render/sdf/sdf_programs.js +42 -0
  284. package/dist/render/sdf/sdf_programs.js.map +1 -0
  285. package/dist/render/sdf/sdf_view.js +199 -0
  286. package/dist/render/sdf/sdf_view.js.map +1 -0
  287. package/dist/render/sdf/waterline.js +610 -0
  288. package/dist/render/sdf/waterline.js.map +1 -0
  289. package/dist/render/shadow_mask_pass.js +142 -25
  290. package/dist/render/shadow_mask_pass.js.map +1 -1
  291. package/dist/render/shore_wave_generator.js +1193 -0
  292. package/dist/render/shore_wave_generator.js.map +1 -0
  293. package/dist/render/sky.js +516 -86
  294. package/dist/render/sky.js.map +1 -1
  295. package/dist/render/transmission_thickness_pass.js +477 -0
  296. package/dist/render/transmission_thickness_pass.js.map +1 -0
  297. package/dist/render/underwater.js +58 -158
  298. package/dist/render/underwater.js.map +1 -1
  299. package/dist/render/virtualtexture/virtual_texture.js +1214 -0
  300. package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
  301. package/dist/render/volumetric_cloud/cloud_common.js +472 -0
  302. package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
  303. package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
  304. package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
  305. package/dist/render/volumetric_cloud/debug_view.js +131 -0
  306. package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
  307. package/dist/render/volumetric_cloud/resources.js +380 -0
  308. package/dist/render/volumetric_cloud/resources.js.map +1 -0
  309. package/dist/render/volumetric_cloud/sky_cache.js +380 -0
  310. package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
  311. package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
  312. package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
  313. package/dist/render/water_caustics.js +134 -319
  314. package/dist/render/water_caustics.js.map +1 -1
  315. package/dist/render/water_foam.js +627 -0
  316. package/dist/render/water_foam.js.map +1 -0
  317. package/dist/render/water_interaction.js +1795 -0
  318. package/dist/render/water_interaction.js.map +1 -0
  319. package/dist/render/weightedblended_oit.js +3 -3
  320. package/dist/render/weightedblended_oit.js.map +1 -1
  321. package/dist/scene/basesprite.js +9 -4
  322. package/dist/scene/basesprite.js.map +1 -1
  323. package/dist/scene/batchgroup.js +108 -12
  324. package/dist/scene/batchgroup.js.map +1 -1
  325. package/dist/scene/environment.js +28 -7
  326. package/dist/scene/environment.js.map +1 -1
  327. package/dist/scene/foliage.js +1020 -0
  328. package/dist/scene/foliage.js.map +1 -0
  329. package/dist/scene/foliage_buckets.js +147 -0
  330. package/dist/scene/foliage_buckets.js.map +1 -0
  331. package/dist/scene/foliage_mask.js +358 -0
  332. package/dist/scene/foliage_mask.js.map +1 -0
  333. package/dist/scene/hair.js +15 -4
  334. package/dist/scene/hair.js.map +1 -1
  335. package/dist/scene/light.js +179 -13
  336. package/dist/scene/light.js.map +1 -1
  337. package/dist/scene/mesh.js +327 -45
  338. package/dist/scene/mesh.js.map +1 -1
  339. package/dist/scene/meshdrawable.js +6 -0
  340. package/dist/scene/meshdrawable.js.map +1 -1
  341. package/dist/scene/msdftext.js +9 -3
  342. package/dist/scene/msdftext.js.map +1 -1
  343. package/dist/scene/msdftextsprite.js +9 -3
  344. package/dist/scene/msdftextsprite.js.map +1 -1
  345. package/dist/scene/particlesys.js +8 -2
  346. package/dist/scene/particlesys.js.map +1 -1
  347. package/dist/scene/physics_settings.js +157 -0
  348. package/dist/scene/physics_settings.js.map +1 -0
  349. package/dist/scene/scene.js +190 -44
  350. package/dist/scene/scene.js.map +1 -1
  351. package/dist/scene/scene_node.js +147 -6
  352. package/dist/scene/scene_node.js.map +1 -1
  353. package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
  354. package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
  355. package/dist/scene/terrain-cm/grass.js +881 -42
  356. package/dist/scene/terrain-cm/grass.js.map +1 -1
  357. package/dist/scene/terrain-cm/grass_gpu.js +929 -0
  358. package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
  359. package/dist/scene/terrain-cm/grassmaterial.js +180 -9
  360. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  361. package/dist/scene/terrain-cm/terrain-cm.js +465 -36
  362. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  363. package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
  364. package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
  365. package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
  366. package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
  367. package/dist/scene/update_queue.js +54 -0
  368. package/dist/scene/update_queue.js.map +1 -0
  369. package/dist/scene/water.js +633 -268
  370. package/dist/scene/water.js.map +1 -1
  371. package/dist/scene/water_surface_sampler.js +324 -0
  372. package/dist/scene/water_surface_sampler.js.map +1 -0
  373. package/dist/scene/wind.js +317 -0
  374. package/dist/scene/wind.js.map +1 -0
  375. package/dist/shaders/atmosphere.js +388 -166
  376. package/dist/shaders/atmosphere.js.map +1 -1
  377. package/dist/shaders/fog.js +100 -32
  378. package/dist/shaders/fog.js.map +1 -1
  379. package/dist/shaders/gi.js +365 -0
  380. package/dist/shaders/gi.js.map +1 -0
  381. package/dist/shaders/ltc_rect.js +202 -0
  382. package/dist/shaders/ltc_rect.js.map +1 -0
  383. package/dist/shaders/misc.js +1 -10
  384. package/dist/shaders/misc.js.map +1 -1
  385. package/dist/shaders/noise.js +5 -1
  386. package/dist/shaders/noise.js.map +1 -1
  387. package/dist/shaders/probes.js +309 -0
  388. package/dist/shaders/probes.js.map +1 -0
  389. package/dist/shaders/sdf.js +537 -0
  390. package/dist/shaders/sdf.js.map +1 -0
  391. package/dist/shaders/shadow.js +104 -70
  392. package/dist/shaders/shadow.js.map +1 -1
  393. package/dist/shaders/skin_brdf.js +243 -0
  394. package/dist/shaders/skin_brdf.js.map +1 -0
  395. package/dist/shaders/ssgi.js +63 -0
  396. package/dist/shaders/ssgi.js.map +1 -0
  397. package/dist/shaders/ssgi_reprojection.js +118 -0
  398. package/dist/shaders/ssgi_reprojection.js.map +1 -0
  399. package/dist/shaders/ssr.js +6 -159
  400. package/dist/shaders/ssr.js.map +1 -1
  401. package/dist/shaders/temporal.js +38 -27
  402. package/dist/shaders/temporal.js.map +1 -1
  403. package/dist/shaders/water.js +32 -82
  404. package/dist/shaders/water.js.map +1 -1
  405. package/dist/shaders/water_medium.js +7 -21
  406. package/dist/shaders/water_medium.js.map +1 -1
  407. package/dist/shadow/dom.js +110 -19
  408. package/dist/shadow/dom.js.map +1 -1
  409. package/dist/shadow/esm.js +3 -3
  410. package/dist/shadow/esm.js.map +1 -1
  411. package/dist/shadow/pcf_opt.js +15 -48
  412. package/dist/shadow/pcf_opt.js.map +1 -1
  413. package/dist/shadow/pcf_pd.js +15 -53
  414. package/dist/shadow/pcf_pd.js.map +1 -1
  415. package/dist/shadow/pcss.js +34 -9
  416. package/dist/shadow/pcss.js.map +1 -1
  417. package/dist/shadow/shadow_impl.js +3 -8
  418. package/dist/shadow/shadow_impl.js.map +1 -1
  419. package/dist/shadow/shadow_region.js +40 -2
  420. package/dist/shadow/shadow_region.js.map +1 -1
  421. package/dist/shadow/shadowmapper.js +93 -53
  422. package/dist/shadow/shadowmapper.js.map +1 -1
  423. package/dist/shadow/ssm.js +18 -72
  424. package/dist/shadow/ssm.js.map +1 -1
  425. package/dist/shadow/vsm.js +3 -3
  426. package/dist/shadow/vsm.js.map +1 -1
  427. package/dist/shapes/shape.js +44 -1
  428. package/dist/shapes/shape.js.map +1 -1
  429. package/dist/utility/buoyancy.js +287 -0
  430. package/dist/utility/buoyancy.js.map +1 -0
  431. package/dist/utility/physical.js +10 -1
  432. package/dist/utility/physical.js.map +1 -1
  433. package/dist/utility/pmrem.js +17 -43
  434. package/dist/utility/pmrem.js.map +1 -1
  435. package/dist/utility/sdf/bake.js +435 -0
  436. package/dist/utility/sdf/bake.js.map +1 -0
  437. package/dist/utility/sdf/bvh.js +576 -0
  438. package/dist/utility/sdf/bvh.js.map +1 -0
  439. package/dist/utility/serialization/manager.js +34 -6
  440. package/dist/utility/serialization/manager.js.map +1 -1
  441. package/dist/utility/serialization/scene/animation.js +46 -5
  442. package/dist/utility/serialization/scene/animation.js.map +1 -1
  443. package/dist/utility/serialization/scene/batch.js +11 -4
  444. package/dist/utility/serialization/scene/batch.js.map +1 -1
  445. package/dist/utility/serialization/scene/camera.js +87 -254
  446. package/dist/utility/serialization/scene/camera.js.map +1 -1
  447. package/dist/utility/serialization/scene/cloth.js +25 -0
  448. package/dist/utility/serialization/scene/cloth.js.map +1 -0
  449. package/dist/utility/serialization/scene/common.js +14 -6
  450. package/dist/utility/serialization/scene/common.js.map +1 -1
  451. package/dist/utility/serialization/scene/foliage.js +821 -0
  452. package/dist/utility/serialization/scene/foliage.js.map +1 -0
  453. package/dist/utility/serialization/scene/hair.js +11 -4
  454. package/dist/utility/serialization/scene/hair.js.map +1 -1
  455. package/dist/utility/serialization/scene/light.js +115 -7
  456. package/dist/utility/serialization/scene/light.js.map +1 -1
  457. package/dist/utility/serialization/scene/material.js +388 -220
  458. package/dist/utility/serialization/scene/material.js.map +1 -1
  459. package/dist/utility/serialization/scene/mesh.js +69 -4
  460. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  461. package/dist/utility/serialization/scene/node.js +140 -5
  462. package/dist/utility/serialization/scene/node.js.map +1 -1
  463. package/dist/utility/serialization/scene/node_physics.js +1318 -0
  464. package/dist/utility/serialization/scene/node_physics.js.map +1 -0
  465. package/dist/utility/serialization/scene/particle.js +11 -4
  466. package/dist/utility/serialization/scene/particle.js.map +1 -1
  467. package/dist/utility/serialization/scene/physics.js +120 -0
  468. package/dist/utility/serialization/scene/physics.js.map +1 -0
  469. package/dist/utility/serialization/scene/primitive.js +124 -10
  470. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  471. package/dist/utility/serialization/scene/scene.js +1687 -51
  472. package/dist/utility/serialization/scene/scene.js.map +1 -1
  473. package/dist/utility/serialization/scene/sprite.js +11 -4
  474. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  475. package/dist/utility/serialization/scene/terrain.js +163 -33
  476. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  477. package/dist/utility/serialization/scene/text.js +11 -4
  478. package/dist/utility/serialization/scene/text.js.map +1 -1
  479. package/dist/utility/serialization/scene/water.js +1543 -264
  480. package/dist/utility/serialization/scene/water.js.map +1 -1
  481. package/dist/utility/serialization/types.js.map +1 -1
  482. package/dist/utility/shprojector.js +10 -22
  483. package/dist/utility/shprojector.js.map +1 -1
  484. package/dist/utility/textures/foam.js +170 -0
  485. package/dist/utility/textures/foam.js.map +1 -0
  486. package/dist/utility/textures/ggxlut.js +17 -43
  487. package/dist/utility/textures/ggxlut.js.map +1 -1
  488. package/dist/utility/textures/ltcdata.js +4 -4
  489. package/dist/utility/textures/ltcdata.js.map +1 -1
  490. package/dist/utility/textures/ltclut.js +16 -6
  491. package/dist/utility/textures/ltclut.js.map +1 -1
  492. package/dist/utility/unsupported.js +24 -0
  493. package/dist/utility/unsupported.js.map +1 -0
  494. package/dist/values.js +13 -1
  495. package/dist/values.js.map +1 -1
  496. package/package.json +25 -12
  497. package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
  498. package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  499. package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  500. package/src/asset/meshoptimizer/LICENSE +21 -0
  501. package/dist/animation/joint_dynamics/convex_collider.js +0 -320
  502. package/dist/animation/joint_dynamics/convex_collider.js.map +0 -1
@@ -1,9 +1,10 @@
1
- import { applyMixins, DRef, randomUUID, Vector3, Vector4 } from '@zephyr3d/base';
1
+ import { applyMixins, DRef, randomUUID, Vector3, half2float, Vector4, nextPowerOf2 } from '@zephyr3d/base';
2
2
  import { GraphNode } from '../graph_node.js';
3
3
  import { mixinDrawable } from '../../render/drawable_mixin.js';
4
- import { MAX_TERRAIN_MIPMAP_LEVELS, QUEUE_OPAQUE, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_SHADOWMAP } from '../../values.js';
5
- import '../../render/render_queue.js';
4
+ import { MAX_TERRAIN_MIPMAP_LEVELS, QUEUE_OPAQUE, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR } from '../../values.js';
6
5
  import { getDevice } from '../../app/api.js';
6
+ import { warnUnsupported } from '../../utility/unsupported.js';
7
+ import '../../render/render_queue.js';
7
8
  import '../../render/primitive.js';
8
9
  import '@zephyr3d/device';
9
10
  import { CopyBlitter } from '../../blitter/copy.js';
@@ -11,11 +12,12 @@ import '../../material/shader/helper.js';
11
12
  import '../../material/lambert.js';
12
13
  import '../../material/blinn.js';
13
14
  import '../../material/unlit.js';
15
+ import '../../material/meshmaterial.js';
14
16
  import '../../material/particle.js';
15
17
  import '../../material/subsurfaceprofile.js';
16
- import '../../material/skin.js';
18
+ import '../../material/sssprofile.js';
19
+ import '../../material/sss.js';
17
20
  import '../../material/eye.js';
18
- import '../../material/meshmaterial.js';
19
21
  import '../../material/hair.js';
20
22
  import '../../material/hairstrand.js';
21
23
  import '../../material/grassmaterial.js';
@@ -33,18 +35,49 @@ import '../../render/lightpass.js';
33
35
  import '../../render/sky.js';
34
36
  import { Clipmap } from '../../render/clipmap.js';
35
37
  import '../../render/envlight.js';
38
+ import '../../render/lod.js';
36
39
  import '../../render/dualdepthpeeling_oit.js';
37
40
  import '../../shaders/atmosphere.js';
41
+ import '../../render/water_interaction.js';
38
42
  import '../../render/rendergraph/scene_render_context.js';
43
+ import '../../render/sdf/scene_sdf.js';
44
+ import '../../render/sdf/global_sdf.js';
45
+ import '../../render/gi/voxelize_pass.js';
39
46
  import { BoundingBox } from '../../utility/bounding_volume.js';
40
47
  import { RenderMipmap } from '../../utility/rendermipmap.js';
41
48
  import { GrassRenderer } from './grass.js';
49
+ import { TerrainVirtualTexture } from './terrain_rvt.js';
50
+ import { TerrainGIAlbedo } from './terrain_gi_albedo.js';
42
51
 
43
52
  class HeightMinMaxBlitter extends CopyBlitter {
44
53
  filter(scope, type, srcTex, srcUV, srcLayer, sampleType) {
45
54
  return this.readTexel(scope, type, srcTex, srcUV, srcLayer, sampleType).xxxx;
46
55
  }
47
56
  }
57
+ /**
58
+ * Copies the height map into the top-left of a power-of-two target, repeating the last row and
59
+ * column into the padding. The min/max reduction halves each level with a 2x2 footprint, which
60
+ * drops the last row or column of an odd sized level; padding with edge copies avoids that
61
+ * without affecting either extreme.
62
+ */ class HeightPadBlitter extends CopyBlitter {
63
+ /** (dest width, dest height, source width, source height) */ sizes = new Vector4();
64
+ setup(scope, _type) {
65
+ const pb = scope.$builder;
66
+ if (pb.shaderKind === 'fragment') {
67
+ scope.padSizes = pb.vec4().uniform(0);
68
+ }
69
+ }
70
+ setUniforms(bindGroup, _sourceTex) {
71
+ bindGroup.setValue('padSizes', this.sizes);
72
+ }
73
+ filter(scope, type, srcTex, srcUV, srcLayer, sampleType) {
74
+ const pb = scope.$builder;
75
+ const sizes = scope.padSizes;
76
+ const texel = pb.min(pb.floor(pb.mul(srcUV, sizes.xy)), pb.sub(sizes.zw, pb.vec2(1)));
77
+ const uv = pb.div(pb.add(texel, pb.vec2(0.5)), sizes.zw);
78
+ return this.readTexel(scope, type, srcTex, uv, srcLayer, sampleType).xxxx;
79
+ }
80
+ }
48
81
  class HeightBoundingGenerator extends RenderMipmap {
49
82
  renderPixel(scope, leftTop, rightTop, leftBottom, rightBottom) {
50
83
  const pb = scope.$builder;
@@ -70,11 +103,22 @@ class HeightBoundingGenerator extends RenderMipmap {
70
103
  */ class ClipmapTerrain extends applyMixins(GraphNode, mixinDrawable) {
71
104
  static _heightBoundingGenerator = new HeightBoundingGenerator();
72
105
  static _copyBlitter = new HeightMinMaxBlitter();
106
+ static _padBlitter = new HeightPadBlitter();
73
107
  static _tmpBuffer = new Float32Array(MAX_TERRAIN_MIPMAP_LEVELS * 2 * 4);
74
108
  _pickTarget;
75
109
  _clipmap;
110
+ _shadowClipmap;
76
111
  _renderData;
112
+ _shadowRenderData;
113
+ _shadowGatherContext;
77
114
  _gridScale;
115
+ /**
116
+ * World position of clipmap grid point (0, 0): the centre of the first height map texel.
117
+ * Texel i holds the height at the centre of cell i (the convention the height sampling,
118
+ * brushes and grass placement all share), so this is what puts the finest level's vertices
119
+ * on the samples instead of halfway between them.
120
+ */ _gridOriginX;
121
+ _gridOriginZ;
78
122
  _material;
79
123
  _grassRenderer;
80
124
  _castShadow;
@@ -85,7 +129,13 @@ class HeightBoundingGenerator extends RenderMipmap {
85
129
  _grassAssetId;
86
130
  _minHeight;
87
131
  _maxHeight;
132
+ /** Bumped by every height range update, so a read back overtaken by a newer one is dropped */ _heightRangeSerial;
133
+ _heightData;
134
+ _heightReadBack;
135
+ _heightVersion;
88
136
  _tmpTexture;
137
+ _virtualTexture;
138
+ _giAlbedo;
89
139
  /**
90
140
  * Creates a new clipmap terrain instance.
91
141
  *
@@ -103,9 +153,16 @@ class HeightBoundingGenerator extends RenderMipmap {
103
153
  this._clipmap = new Clipmap(clipMapTileSize, [
104
154
  'tex1_f32'
105
155
  ], MAX_TERRAIN_MIPMAP_LEVELS);
156
+ this._shadowClipmap = new Clipmap(clipMapTileSize, [
157
+ 'tex1_f32'
158
+ ], MAX_TERRAIN_MIPMAP_LEVELS);
106
159
  this._renderData = null;
160
+ this._shadowRenderData = null;
161
+ this._shadowGatherContext = null;
107
162
  this._grassRenderer = new DRef(new GrassRenderer(this));
108
163
  this._gridScale = 1;
164
+ this._gridOriginX = 0;
165
+ this._gridOriginZ = 0;
109
166
  this._castShadow = true;
110
167
  this._sizeX = sizeX;
111
168
  this._sizeZ = sizeZ;
@@ -114,9 +171,16 @@ class HeightBoundingGenerator extends RenderMipmap {
114
171
  this._splatMapAssetId = `assets/.embedded.dir/${this.persistentId}-splatmap.bin`;
115
172
  this._minHeight = 0;
116
173
  this._maxHeight = 0;
174
+ this._heightRangeSerial = 0;
175
+ this._heightData = null;
176
+ this._heightReadBack = null;
177
+ this._heightVersion = 0;
117
178
  this._material = new DRef(new ClipmapTerrainMaterial(this.createHeightMapTexture(this._sizeX, this._sizeZ)));
118
179
  this._tmpTexture = new DRef();
180
+ this._virtualTexture = null;
181
+ this._giAlbedo = null;
119
182
  this.updateRegion();
183
+ this.updateHeightPyramid();
120
184
  scene.queuePerCameraUpdateNode(this);
121
185
  }
122
186
  /**
@@ -209,6 +273,83 @@ class HeightBoundingGenerator extends RenderMipmap {
209
273
  this.resizeHeightMap(this._sizeX, this._sizeZ);
210
274
  }
211
275
  }
276
+ /**
277
+ * Whether the detail layers are blended into a runtime virtual texture instead of per pixel.
278
+ * Pages are blended once, as the camera needs them, so shading costs the same whatever the
279
+ * number of layers. WebGPU only, ignored elsewhere; has no effect without detail layers.
280
+ *
281
+ * Code writing into the splat map or the height map directly must call
282
+ * {@link ClipmapTerrain.invalidateRuntimeVirtualTexture} for the area it changed.
283
+ */ get runtimeVirtualTexture() {
284
+ return !!this._virtualTexture;
285
+ }
286
+ set runtimeVirtualTexture(val) {
287
+ if (!!val !== this.runtimeVirtualTexture) {
288
+ this.setRuntimeVirtualTexture(val ? {} : null);
289
+ }
290
+ }
291
+ /**
292
+ * Enables the runtime virtual texture with the given options, or disables it with null.
293
+ * See {@link ClipmapTerrain.runtimeVirtualTexture}.
294
+ */ setRuntimeVirtualTexture(options) {
295
+ if (options && getDevice().type !== 'webgpu') {
296
+ warnUnsupported('Terrain runtime virtual texture (ClipmapTerrain.runtimeVirtualTexture)', 'WebGPU', 'the terrain is shaded directly every frame');
297
+ return;
298
+ }
299
+ if (options && !this.scene) {
300
+ return;
301
+ }
302
+ if (this._virtualTexture && options) {
303
+ this._virtualTexture.setOptions(options);
304
+ return;
305
+ }
306
+ if (this._virtualTexture) {
307
+ this.scene?.removeVirtualTextureClient(this._virtualTexture);
308
+ this._virtualTexture.dispose();
309
+ this._virtualTexture = null;
310
+ if (this.material) {
311
+ this.material.virtualTexture = null;
312
+ }
313
+ }
314
+ if (options && this.material) {
315
+ this._virtualTexture = new TerrainVirtualTexture(this, options);
316
+ this.material.virtualTexture = this._virtualTexture.virtualTexture;
317
+ this.scene.addVirtualTextureClient(this._virtualTexture);
318
+ }
319
+ }
320
+ /**
321
+ * Refills the runtime virtual texture over a world XZ rectangle, or everywhere without
322
+ * arguments, and recaptures the albedo global illumination reads there. Needed after writing
323
+ * into the splat map or the height map directly (brushes); replacing textures or changing
324
+ * layer parameters through the material is picked up automatically.
325
+ */ invalidateRuntimeVirtualTexture(x0, z0, x1, z1) {
326
+ this._giAlbedo?.invalidate(x0, z0, x1, z1);
327
+ if (!this._virtualTexture) {
328
+ return;
329
+ }
330
+ if (x0 === undefined || z0 === undefined || x1 === undefined || z1 === undefined) {
331
+ this._virtualTexture.invalidate();
332
+ } else {
333
+ this._virtualTexture.invalidateWorldRegion(x0, z0, x1, z1);
334
+ }
335
+ }
336
+ /**
337
+ * Top-down capture of the material albedo read by global illumination, created on first use.
338
+ * WebGPU only.
339
+ * @internal
340
+ */ get giAlbedo() {
341
+ if (!this._giAlbedo) {
342
+ this._giAlbedo = new TerrainGIAlbedo(this);
343
+ }
344
+ return this._giAlbedo;
345
+ }
346
+ /**
347
+ * The virtual texture behind {@link ClipmapTerrain.runtimeVirtualTexture}, null when disabled.
348
+ * For inspection: {@link VirtualTexture.stats}, {@link VirtualTexture.renderDebugTexture}.
349
+ * It is recreated when the terrain aspect or the options change.
350
+ */ get runtimeVirtualTextureData() {
351
+ return this._virtualTexture?.virtualTexture ?? null;
352
+ }
212
353
  /** Wether the mesh node casts shadows */ get castShadow() {
213
354
  return this._castShadow;
214
355
  }
@@ -239,8 +380,162 @@ class HeightBoundingGenerator extends RenderMipmap {
239
380
  set heightMap(val) {
240
381
  if (this.material && val) {
241
382
  this.material.heightMap = val;
383
+ this._heightData = null;
242
384
  this.updateRegion();
385
+ this.updateHeightPyramid();
386
+ this._heightVersion++;
387
+ this.readBackHeightData();
388
+ }
389
+ }
390
+ /**
391
+ * Goes up whenever the heights change: a new height map, or edits to the
392
+ * current one once {@link ClipmapTerrain.updateBoundingBox} is called after
393
+ * them. Lets CPU-side users such as physics notice they are out of date.
394
+ */ get heightVersion() {
395
+ return this._heightVersion;
396
+ }
397
+ /**
398
+ * CPU copy of the heights, half floats row by row (height map texel values, before the terrain
399
+ * scale and translation); null while unknown.
400
+ *
401
+ * @remarks
402
+ * Lets CPU-side users such as physics and prop placement read heights without a GPU read back.
403
+ * Known at once when loaded or set with {@link ClipmapTerrain.setHeights}; after other edits
404
+ * it is null until a read back started by {@link ClipmapTerrain.updateBoundingBox} lands, which
405
+ * {@link ClipmapTerrain.whenHeightDataReady} waits for.
406
+ */ get heightData() {
407
+ return this._heightData;
408
+ }
409
+ /**
410
+ * Records the CPU copy of the heights just uploaded to the height map.
411
+ * @internal
412
+ */ setHeightData(data, width, height) {
413
+ this._heightData = {
414
+ data,
415
+ width,
416
+ height
417
+ };
418
+ this._heightVersion++;
419
+ }
420
+ /**
421
+ * Resolves once {@link ClipmapTerrain.heightData} is known, reading the heights back from the
422
+ * GPU if needed. Resolves to null when the height map format cannot be read back.
423
+ */ whenHeightDataReady() {
424
+ if (this._heightData) {
425
+ return Promise.resolve(this._heightData);
426
+ }
427
+ return this.readBackHeightData() ?? Promise.resolve(null);
428
+ }
429
+ /**
430
+ * World height of the terrain surface at a world position, bilinearly interpolated, or null
431
+ * outside the terrain or while {@link ClipmapTerrain.heightData} is unknown.
432
+ */ getHeightAt(x, z) {
433
+ const data = this._heightData;
434
+ const region = this.worldRegion;
435
+ if (!data || region.z <= region.x || region.w <= region.y) {
436
+ return null;
437
+ }
438
+ const u = (x - region.x) / (region.z - region.x);
439
+ const v = (z - region.y) / (region.w - region.y);
440
+ if (u < 0 || u > 1 || v < 0 || v > 1) {
441
+ return null;
442
+ }
443
+ return this.sampleHeightTexel(data, u, v) * this.scale.y + this.worldMatrix.m13;
444
+ }
445
+ /**
446
+ * Unit surface normal of the terrain at a world position, from central differences one height
447
+ * map texel apart, or null where {@link ClipmapTerrain.getHeightAt} is.
448
+ */ getNormalAt(x, z, out) {
449
+ const data = this._heightData;
450
+ const region = this.worldRegion;
451
+ if (!data || region.z <= region.x || region.w <= region.y) {
452
+ return null;
453
+ }
454
+ const rw = region.z - region.x;
455
+ const rh = region.w - region.y;
456
+ const u = (x - region.x) / rw;
457
+ const v = (z - region.y) / rh;
458
+ if (u < 0 || u > 1 || v < 0 || v > 1) {
459
+ return null;
460
+ }
461
+ const du = 1 / data.width;
462
+ const dv = 1 / data.height;
463
+ const sy = this.scale.y;
464
+ const dx = (this.sampleHeightTexel(data, u + du, v) - this.sampleHeightTexel(data, u - du, v)) * sy;
465
+ const dz = (this.sampleHeightTexel(data, u, v + dv) - this.sampleHeightTexel(data, u, v - dv)) * sy;
466
+ out = out ?? new Vector3();
467
+ out.setXYZ(-dx / (2 * du * rw), 1, -dz / (2 * dv * rh));
468
+ return out.inplaceNormalize();
469
+ }
470
+ sampleHeightTexel(data, u, v) {
471
+ // Texel i holds the height at the centre of cell i, at u = (i + 0.5) / width
472
+ const w = data.width;
473
+ const h = data.height;
474
+ const fx = Math.min(Math.max(u * w - 0.5, 0), w - 1);
475
+ const fz = Math.min(Math.max(v * h - 0.5, 0), h - 1);
476
+ const x0 = Math.floor(fx);
477
+ const z0 = Math.floor(fz);
478
+ const x1 = Math.min(x0 + 1, w - 1);
479
+ const z1 = Math.min(z0 + 1, h - 1);
480
+ const tx = fx - x0;
481
+ const tz = fz - z0;
482
+ const d = data.data;
483
+ const h00 = half2float(d[z0 * w + x0]);
484
+ const h10 = half2float(d[z0 * w + x1]);
485
+ const h01 = half2float(d[z1 * w + x0]);
486
+ const h11 = half2float(d[z1 * w + x1]);
487
+ const a = h00 + (h10 - h00) * tx;
488
+ const b = h01 + (h11 - h01) * tx;
489
+ return a + (b - a) * tz;
490
+ }
491
+ /**
492
+ * Reads the half float heights back from the GPU into {@link ClipmapTerrain.heightData}.
493
+ * A read back overtaken by another height change is dropped.
494
+ */ readBackHeightData() {
495
+ const map = this.heightMap;
496
+ if (!map || map.format !== 'r16f' && map.format !== 'rgba16f') {
497
+ return null;
243
498
  }
499
+ if (this._heightReadBack && this._heightReadBack.version === this._heightVersion) {
500
+ return this._heightReadBack.promise;
501
+ }
502
+ const version = this._heightVersion;
503
+ const w = map.width;
504
+ const h = map.height;
505
+ const channels = map.format === 'r16f' ? 1 : 4;
506
+ const buffer = new Uint16Array(w * h * channels);
507
+ const promise = map.readPixels(0, 0, w, h, 0, 0, buffer).then(()=>{
508
+ if (version !== this._heightVersion || this.heightMap !== map) {
509
+ return this._heightData;
510
+ }
511
+ let data = buffer;
512
+ if (channels !== 1) {
513
+ // The height is in the red channel
514
+ data = new Uint16Array(w * h);
515
+ for(let i = 0; i < data.length; i++){
516
+ data[i] = buffer[i * channels];
517
+ }
518
+ }
519
+ // Same heights as on the GPU: the version stays, physics need not rebuild
520
+ this._heightData = {
521
+ data,
522
+ width: w,
523
+ height: h
524
+ };
525
+ return this._heightData;
526
+ }).catch((err)=>{
527
+ console.error(`Terrain height read back failed: ${err}`);
528
+ return null;
529
+ }).finally(()=>{
530
+ if (this._heightReadBack?.promise === promise) {
531
+ this._heightReadBack = null;
532
+ }
533
+ });
534
+ this._heightReadBack = {
535
+ version,
536
+ promise
537
+ };
538
+ return promise;
244
539
  }
245
540
  /** The splat map texture */ get splatMap() {
246
541
  return this.material?.getSplatMap() ?? null;
@@ -253,6 +548,7 @@ class HeightBoundingGenerator extends RenderMipmap {
253
548
  }
254
549
  set wireframe(val) {
255
550
  this._clipmap.wireframe = val;
551
+ this._shadowClipmap.wireframe = val;
256
552
  }
257
553
  /**
258
554
  * {@inheritDoc Drawable.getPickTarget }
@@ -363,37 +659,125 @@ class HeightBoundingGenerator extends RenderMipmap {
363
659
  * terrain.updateBoundingBox();
364
660
  * ```
365
661
  */ updateBoundingBox() {
662
+ // Called after the heights changed on the GPU: a CPU copy is stale now.
663
+ this._heightData = null;
664
+ this._heightVersion++;
665
+ this.readBackHeightData();
666
+ const tmp = this.updateHeightPyramid();
667
+ if (!tmp) {
668
+ return;
669
+ }
670
+ const data = new Float32Array(4);
671
+ const serial = ++this._heightRangeSerial;
672
+ tmp.readPixels(0, 0, 1, 1, 0, tmp.mipLevelCount - 1, data).then(()=>{
673
+ // Loading sets the range synchronously right after a resize has started a read back of
674
+ // the empty height map; that stale result must not land on top of it.
675
+ if (serial !== this._heightRangeSerial) {
676
+ return;
677
+ }
678
+ // The reduction stores (max, min) in (r, g)
679
+ this._maxHeight = data[0];
680
+ this._minHeight = data[1];
681
+ this.invalidateWorldBoundingVolume(false);
682
+ }).catch((_err)=>{
683
+ console.error('Read pixels failed');
684
+ });
685
+ }
686
+ /**
687
+ * Replaces every height of an r16f height map with heights from the CPU.
688
+ *
689
+ * Unlike writing into {@link ClipmapTerrain.heightMap} and calling
690
+ * {@link ClipmapTerrain.updateBoundingBox}, the bounds are right at once and the heights stay
691
+ * available as {@link ClipmapTerrain.heightData}, so physics colliders need no GPU read back.
692
+ *
693
+ * @param data - Half float heights, one per height map texel, row by row
694
+ */ setHeights(data) {
366
695
  const heightMap = this.heightMap;
367
- if (!heightMap) {
696
+ if (!heightMap || heightMap.format !== 'r16f' || data.length !== heightMap.width * heightMap.height) {
697
+ console.error('setHeights() needs one half float per texel of an r16f height map');
368
698
  return;
369
699
  }
700
+ heightMap.update(data, 0, 0, heightMap.width, heightMap.height);
701
+ this.updateHeightPyramid();
702
+ this.setHeightRangeFromHalfData(data);
703
+ this.setHeightData(data, heightMap.width, heightMap.height);
704
+ this.invalidateRuntimeVirtualTexture();
705
+ }
706
+ /**
707
+ * The min/max height pyramid of the height map: (max, min) in (r, g), mip 0 padded to a power
708
+ * of two by repeating the last row and column, so texel j of mip m covers height map texels
709
+ * [j * 2^m, (j + 1) * 2^m). Null until built.
710
+ * @internal
711
+ */ get heightPyramid() {
712
+ return this._tmpTexture.get() ?? null;
713
+ }
714
+ /**
715
+ * Tile resolution of the clipmap, in grid cells
716
+ * @internal
717
+ */ get clipmapTileResolution() {
718
+ return this._clipmap.tileResolution;
719
+ }
720
+ /**
721
+ * Rebuilds the min/max height pyramid from the current height map.
722
+ * Called when the height map is replaced and by {@link ClipmapTerrain.updateBoundingBox}; code
723
+ * that writes into the height map directly must call one of them afterwards.
724
+ * @internal
725
+ */ updateHeightPyramid() {
726
+ const heightMap = this.heightMap;
727
+ if (!heightMap) {
728
+ return null;
729
+ }
370
730
  const device = getDevice();
731
+ const width = nextPowerOf2(heightMap.width);
732
+ const height = nextPowerOf2(heightMap.height);
371
733
  let tmp = this._tmpTexture.get();
372
- if (tmp && (tmp.width !== heightMap.width || tmp.height !== heightMap.height)) {
734
+ if (tmp && (tmp.width !== width || tmp.height !== height)) {
373
735
  this._tmpTexture.dispose();
374
736
  }
375
737
  if (!this._tmpTexture.get()) {
376
- tmp = getDevice().createTexture2D(device.type === 'webgl' ? 'rgba32f' : 'rg32f', heightMap.width, heightMap.height);
738
+ tmp = getDevice().createTexture2D('rg32f', width, height);
377
739
  tmp.name = 'TerrainBoundingBoxTexture';
378
740
  this._tmpTexture.set(tmp);
379
741
  }
380
742
  const tmpFB = device.createFrameBuffer([
381
743
  tmp
382
744
  ], null);
383
- ClipmapTerrain._copyBlitter.blit(heightMap, tmpFB, fetchSampler('clamp_nearest_nomip'));
745
+ ClipmapTerrain._padBlitter.sizes.setXYZW(width, height, heightMap.width, heightMap.height);
746
+ ClipmapTerrain._padBlitter.blit(heightMap, tmpFB, fetchSampler('clamp_nearest_nomip'));
384
747
  tmpFB.dispose();
385
748
  ClipmapTerrain._heightBoundingGenerator.render(tmp);
386
- const data = new Float32Array(4);
387
- tmp.readPixels(0, 0, 1, 1, 0, tmp.mipLevelCount - 1, data).then(()=>{
388
- this._minHeight = data[0];
389
- this._maxHeight = data[1];
749
+ return tmp;
750
+ }
751
+ /**
752
+ * Sets the height range from height map data already on the CPU, e.g. when loading.
753
+ *
754
+ * {@link ClipmapTerrain.updateBoundingBox} reads the range back from the GPU, which lands a
755
+ * few frames later; until then the bounds would stay flat and cull the terrain wrongly.
756
+ *
757
+ * @param data - Half float heights, one per texel
758
+ * @param stride - Number of half floats per texel, the height being the first
759
+ * @internal
760
+ */ setHeightRangeFromHalfData(data, stride = 1) {
761
+ let minHeight = Number.POSITIVE_INFINITY;
762
+ let maxHeight = Number.NEGATIVE_INFINITY;
763
+ for(let i = 0; i < data.length; i += stride){
764
+ const h = half2float(data[i]);
765
+ if (h < minHeight) {
766
+ minHeight = h;
767
+ }
768
+ if (h > maxHeight) {
769
+ maxHeight = h;
770
+ }
771
+ }
772
+ if (minHeight <= maxHeight) {
773
+ this._heightRangeSerial++;
774
+ this._minHeight = minHeight;
775
+ this._maxHeight = maxHeight;
390
776
  this.invalidateWorldBoundingVolume(false);
391
- }).catch((_err)=>{
392
- console.error('Read pixels failed');
393
- });
777
+ }
394
778
  }
395
779
  /** @internal */ createHeightMapTexture(width, height) {
396
- return getDevice().createTexture2D(getDevice().type === 'webgl' ? 'rgba16f' : 'r16f', width, height);
780
+ return getDevice().createTexture2D('r16f', width, height);
397
781
  }
398
782
  /** @internal */ _onTransformChanged(invalidateLocal) {
399
783
  super._onTransformChanged(invalidateLocal);
@@ -404,29 +788,51 @@ class HeightBoundingGenerator extends RenderMipmap {
404
788
  if (!mat) {
405
789
  return;
406
790
  }
407
- const that = this;
408
- const bv = this.getWorldBoundingVolume().toAABB();
791
+ const bv = this.getWorldBoundingVolume()?.toAABB() ?? null;
792
+ const originY = this.worldMatrix.m13;
793
+ const calcAABB = (_userData, minX, maxX, minZ, maxZ, outAABB)=>{
794
+ outAABB.minPoint.setXYZ(minX, bv ? bv.minPoint.y : originY - 9999, minZ);
795
+ outAABB.maxPoint.setXYZ(maxX, bv ? bv.maxPoint.y : originY + 9999, maxZ);
796
+ };
797
+ // Passes rendered from this camera only need the tiles inside its frustum. Shadow passes
798
+ // render from the light and need casters outside of it too, so they get an unculled set
799
+ // built around the same camera (same levels, so the level data below serves both).
409
800
  this._renderData = this._clipmap.gather({
410
801
  camera: camera,
411
802
  minMaxWorldPos: mat.region,
412
803
  gridScale: this._gridScale,
804
+ gridOriginX: this._gridOriginX,
805
+ gridOriginZ: this._gridOriginZ,
413
806
  userData: this,
414
- frustumCulling: !this.castShadow,
415
- calcAABB (userData, minX, maxX, minZ, maxZ, outAABB) {
416
- const p = that.worldMatrix.transformPointAffine(Vector3.zero());
417
- outAABB.minPoint.setXYZ(minX, bv ? bv.minPoint.y : p.y - 9999, minZ);
418
- outAABB.maxPoint.setXYZ(maxX, bv ? bv.maxPoint.y : p.y + 9999, maxZ);
419
- }
807
+ frustumCulling: true,
808
+ calcAABB
420
809
  });
810
+ // Each shadow map camera (cascade, cube face) culls this set again in draw()
811
+ this._shadowGatherContext = this.castShadow ? {
812
+ camera: camera,
813
+ minMaxWorldPos: mat.region,
814
+ gridScale: this._gridScale,
815
+ gridOriginX: this._gridOriginX,
816
+ gridOriginZ: this._gridOriginZ,
817
+ userData: this,
818
+ frustumCulling: false,
819
+ calcAABB
820
+ } : null;
821
+ this._shadowRenderData = this._shadowGatherContext ? this._shadowClipmap.gather(this._shadowGatherContext) : null;
421
822
  let maxMipLevel = 0;
422
- for (const info of this._renderData){
423
- const buffer = info.primitive.getVertexBuffer('texCoord1');
424
- buffer.bufferSubData(0, info.mipLevels, 0, info.numInstances);
425
- if (info.maxMiplevel > maxMipLevel) {
426
- maxMipLevel = info.maxMiplevel;
823
+ for (const renderData of [
824
+ this._renderData,
825
+ this._shadowRenderData
826
+ ]){
827
+ for (const info of renderData ?? []){
828
+ const buffer = info.primitive.getVertexBuffer('texCoord1');
829
+ buffer.bufferSubData(0, info.mipLevels, 0, info.numInstances);
830
+ if (info.maxMiplevel > maxMipLevel) {
831
+ maxMipLevel = info.maxMiplevel;
832
+ }
427
833
  }
428
834
  }
429
- const levelAABB = this._clipmap.calcLevelAABB(camera, mat.region, this._gridScale);
835
+ const levelAABB = this._clipmap.calcLevelAABB(camera, mat.region, this._gridScale, this._gridOriginX, this._gridOriginZ);
430
836
  const cameraPos = camera.getWorldPosition();
431
837
  const tmpBuffer = ClipmapTerrain._tmpBuffer;
432
838
  for(let i = 0; i <= maxMipLevel; i++){
@@ -449,6 +855,8 @@ class HeightBoundingGenerator extends RenderMipmap {
449
855
  tmpBuffer[i * 8 + 7] = 1 / (currentAABB.maxPoint.z - tmpBuffer[i * 8 + 3]);
450
856
  }
451
857
  mat.setLevelData(tmpBuffer, 8 * (maxMipLevel + 1));
858
+ // Grass placed on the GPU is generated around this camera, outside of any render pass
859
+ this.grassRenderer.updatePerCamera(camera);
452
860
  this.scene?.queuePerCameraUpdateNode(this);
453
861
  }
454
862
  /**
@@ -460,7 +868,11 @@ class HeightBoundingGenerator extends RenderMipmap {
460
868
  const z = Math.abs(this.scale.z);
461
869
  const px = this.position.x + (this.parent?.worldMatrix.m03 ?? 0);
462
870
  const pz = this.position.z + (this.parent?.worldMatrix.m23 ?? 0);
463
- this._gridScale = Math.max(x * this._sizeX / this.material.heightMap.width, z * this._sizeZ / this.material.heightMap.height);
871
+ const cellX = x * this._sizeX / this.material.heightMap.width;
872
+ const cellZ = z * this._sizeZ / this.material.heightMap.height;
873
+ this._gridScale = Math.max(cellX, cellZ);
874
+ this._gridOriginX = px + cellX * 0.5;
875
+ this._gridOriginZ = pz + cellZ * 0.5;
464
876
  this.material.update(new Vector4(px, pz, px + x * this._sizeX, pz + z * this._sizeZ), this.scale);
465
877
  this.grassRenderer.updateMaterial();
466
878
  }
@@ -469,12 +881,24 @@ class HeightBoundingGenerator extends RenderMipmap {
469
881
  const mat = this._material?.get();
470
882
  if (mat) {
471
883
  this.bind(ctx, renderQueue);
472
- mat.setClipmapGridInfo(this._gridScale, this.worldMatrix.m03, this.worldMatrix.m23);
884
+ // The shader places vertices at grid * gridScale - offset, then adds the world translation
885
+ mat.setClipmapGridInfo(this._gridScale, this.worldMatrix.m03 - this._gridOriginX, this.worldMatrix.m23 - this._gridOriginZ);
473
886
  mat.apply(ctx);
474
- for (const info of this._renderData){
887
+ const isShadowPass = ctx.renderPass.type === RENDER_PASS_TYPE_SHADOWMAP;
888
+ let renderData = isShadowPass ? this._shadowRenderData : this._renderData;
889
+ const cullCamera = renderQueue?.cullCamera;
890
+ if (isShadowPass && renderData && cullCamera && this._shadowGatherContext) {
891
+ this._shadowGatherContext.camera = cullCamera;
892
+ renderData = this._shadowClipmap.cullInstances(renderData, this._shadowGatherContext);
893
+ for (const info of renderData){
894
+ info.primitive.getVertexBuffer('texCoord1').bufferSubData(0, info.mipLevels, 0, info.numInstances);
895
+ }
896
+ }
897
+ for (const info of renderData ?? []){
475
898
  mat.draw(info.primitive, ctx, info.numInstances);
476
899
  }
477
- if (ctx.renderPass.type !== RENDER_PASS_TYPE_OBJECT_COLOR && ctx.renderPass.type !== RENDER_PASS_TYPE_SHADOWMAP) {
900
+ // In shadow map passes the grass renderer draws the blades that cast shadows only
901
+ if (ctx.renderPass.type !== RENDER_PASS_TYPE_OBJECT_COLOR) {
478
902
  this.grassRenderer.draw(ctx);
479
903
  }
480
904
  }
@@ -487,7 +911,7 @@ class HeightBoundingGenerator extends RenderMipmap {
487
911
  sizeX = Math.min(Math.max(sizeX, 1), maxTextureSize) >> 0;
488
912
  sizeZ = Math.min(Math.max(sizeZ, 1), maxTextureSize) >> 0;
489
913
  if (sizeX !== oldHeightMap.width || sizeZ !== oldHeightMap.height) {
490
- const newHeightMap = device.createTexture2D(device.type === 'webgl' ? 'rgba16f' : 'r16f', sizeX, sizeZ);
914
+ const newHeightMap = device.createTexture2D('r16f', sizeX, sizeZ);
491
915
  const fb = device.createFrameBuffer([
492
916
  newHeightMap
493
917
  ], null);
@@ -554,9 +978,14 @@ class HeightBoundingGenerator extends RenderMipmap {
554
978
  * Should be called when the terrain is no longer needed.
555
979
  */ onDispose() {
556
980
  super.onDispose();
981
+ this.setRuntimeVirtualTexture(null);
982
+ this._giAlbedo?.dispose();
983
+ this._giAlbedo = null;
557
984
  this._clipmap?.dispose();
985
+ this._shadowClipmap?.dispose();
558
986
  this._material?.dispose();
559
987
  this._grassRenderer?.dispose();
988
+ this._tmpTexture.dispose();
560
989
  }
561
990
  }
562
991