@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
@@ -1 +1 @@
1
- {"version":3,"file":"hair.js","sources":["../../src/scene/hair.ts"],"sourcesContent":["/**\r\n * A groom in the scene graph.\r\n *\r\n * @remarks\r\n * Strand hair is drawn from control points held in storage buffers and expanded\r\n * into camera-facing ribbons in the vertex shader, which needs a handful of\r\n * pieces wired together that nothing else in the engine needs: a primitive that\r\n * carries no attributes but still establishes a vertex layout, a draw range\r\n * derived from the material rather than from an index buffer, and a bounding box\r\n * computed on the CPU because the geometry does not exist until the vertex stage\r\n * has run. This node owns that assembly so a groom can be placed, transformed,\r\n * serialised and simulated like anything else.\r\n *\r\n * The material is deliberately not exposed. Strand geometry only exists inside\r\n * {@link HairStrandMaterial} - it reads control points from storage buffers that\r\n * no other material knows about - so letting one be swapped in would produce a\r\n * node that cannot draw. The shading controls are surfaced as properties of the\r\n * node instead, and they are what gets serialised.\r\n *\r\n * The geometry itself lives in a `.zhair` asset, referenced by path. That file\r\n * holds nothing but strands: the node's own settings, shading included, belong\r\n * to the scene rather than to the asset, so one groom can be shared by several\r\n * characters that each style it differently.\r\n *\r\n * WebGPU only, because the expansion reads storage buffers from the vertex\r\n * stage. On other backends the node exists and keeps its data but draws nothing.\r\n */\r\nimport type { Immutable, Nullable, Vector4 } from '@zephyr3d/base';\r\nimport { applyMixins, DRef, Vector3 } from '@zephyr3d/base';\r\nimport type { Scene } from './scene';\r\nimport { GraphNode } from './graph_node';\r\nimport { mixinDrawable } from '../render/drawable_mixin';\r\nimport type { Drawable, DrawContext, PickTarget, RenderQueue } from '../render';\r\nimport { Primitive } from '../render';\r\nimport { HairStrandMaterial } from '../material/hairstrand';\r\nimport type { HairShadingModel } from '../material/hair';\r\nimport { HairStrandData, type HairStrandSource } from '../material/hairstrand_data';\r\nimport {\r\n GPUHairSimulation,\r\n isHairSimulationSupported,\r\n type GPUHairSimulationOptions\r\n} from '../animation/hair/gpu_hair_simulation';\r\nimport type { ParsedZHair } from '../asset/loaders/zhair/zhair_format';\r\nimport { parseZHair } from '../asset/loaders/zhair/zhair_format';\r\nimport { loadZHairStrandSources, mergeHairStrandSources } from '../asset/loaders/zhair/zhair_loader';\r\nimport { BoundingBox } from '../utility/bounding_volume';\r\nimport { QUEUE_OPAQUE } from '../values';\r\nimport { getEngine } from '../app/api';\r\nimport type { BlendMode } from '../material/meshmaterial';\r\n\r\n/**\r\n * Hair scene node.\r\n *\r\n * @remarks\r\n * Point it at a `.zhair` asset with {@link HairNode.setHairAsset}, or hand it\r\n * control points directly with {@link HairNode.setStrands}. Everything the draw\r\n * call needs is derived from them.\r\n *\r\n * @public\r\n */\r\nexport class HairNode extends applyMixins(GraphNode, mixinDrawable) implements Drawable {\r\n /** @internal */\r\n private readonly _pickTarget: PickTarget;\r\n /** @internal */\r\n private readonly _material: DRef<HairStrandMaterial>;\r\n /** @internal */\r\n private readonly _primitive: DRef<Primitive>;\r\n /** @internal */\r\n private readonly _strands: DRef<HairStrandData>;\r\n /** @internal Local-space bounds of the control points, width included. */\r\n private _bounds: Nullable<BoundingBox>;\r\n /** @internal Length of the longest strand, for padding the bounds while simulating. */\r\n private _maxStrandLength: number;\r\n /** @internal */\r\n private _castShadow: boolean;\r\n /** @internal Kept so the simulation can be rebuilt from the authored pose. */\r\n private _source: Nullable<HairStrandSource>;\r\n /** @internal Path of the `.zhair` asset, empty when strands were set directly. */\r\n private _hairAsset: string;\r\n /**\r\n * @internal The opened asset, kept so decimation can be changed without\r\n * reading the file again.\r\n */\r\n private _parsedAsset: Nullable<ParsedZHair>;\r\n /** @internal */\r\n private _strandStride: number;\r\n /** @internal */\r\n private _maxStrands: number;\r\n /** @internal */\r\n private _simulation: Nullable<GPUHairSimulation>;\r\n /** @internal */\r\n private _simulationEnabled: boolean;\r\n /**\r\n * @internal Simulation tuning, owned by the node.\r\n *\r\n * Held here rather than on the simulation because the node outlives it: the\r\n * dials survive the simulation being switched off and on, and can be set\r\n * before any strands have loaded.\r\n */\r\n private readonly _simulationOptions: GPUHairSimulationOptions;\r\n /**\r\n * Creates an empty hair node.\r\n *\r\n * @param scene - Scene the node belongs to.\r\n */\r\n constructor(scene: Scene) {\r\n super(scene);\r\n this._pickTarget = { node: this };\r\n this._material = new DRef(new HairStrandMaterial());\r\n this._primitive = new DRef();\r\n this._strands = new DRef();\r\n this._bounds = null;\r\n this._maxStrandLength = 0;\r\n this._castShadow = true;\r\n this._source = null;\r\n this._hairAsset = '';\r\n this._parsedAsset = null;\r\n this._strandStride = 1;\r\n this._maxStrands = 0;\r\n this._simulation = null;\r\n this._simulationEnabled = false;\r\n this._simulationOptions = {\r\n gravity: new Vector3(0, -9.8, 0),\r\n damping: 0.08,\r\n globalStiffness: 0,\r\n globalRange: 0,\r\n localStiffness: 0.9,\r\n localIterations: 2,\r\n ftlDamping: 0.7,\r\n vspCoeff: 0.8,\r\n vspAccelThreshold: 50,\r\n substeps: 1,\r\n friction: 0.2,\r\n maxSpeedFactor: 4\r\n };\r\n }\r\n\r\n //\r\n // Geometry\r\n //\r\n\r\n /** Path of the `.zhair` asset this node draws, or an empty string. */\r\n get hairAsset() {\r\n return this._hairAsset;\r\n }\r\n /**\r\n * Loads a `.zhair` asset.\r\n *\r\n * @remarks\r\n * The opened file is kept, so changing {@link HairNode.strandStride} or\r\n * {@link HairNode.maxStrands} afterwards re-decimates what is already in\r\n * memory instead of reading it again - and never touches the source archive\r\n * the asset was converted from.\r\n *\r\n * @param path - Asset path, or an empty string to clear the node.\r\n */\r\n async setHairAsset(path: string) {\r\n const assetId = path?.trim() ?? '';\r\n if (assetId === this._hairAsset) {\r\n return;\r\n }\r\n this._hairAsset = assetId;\r\n this._parsedAsset = null;\r\n if (!assetId) {\r\n this.setStrands(null);\r\n return;\r\n }\r\n let parsed: ParsedZHair;\r\n try {\r\n const data = (await getEngine().resourceManager.assetManager.fetchBinaryData(assetId)) as ArrayBuffer;\r\n parsed = parseZHair(data);\r\n } catch (err) {\r\n console.error(`Load hair asset failed: ${assetId}: ${err}`);\r\n this.setStrands(null);\r\n return;\r\n }\r\n // A load that was superseded while it was in flight must not overwrite the\r\n // asset that replaced it.\r\n if (this._hairAsset !== assetId) {\r\n return;\r\n }\r\n this._parsedAsset = parsed;\r\n this._applyAssetDecimation();\r\n }\r\n /**\r\n * Draw one strand in every `strandStride`.\r\n *\r\n * @remarks\r\n * A stride rather than a leading slice, so the kept strands stay spread over\r\n * the whole scalp instead of leaving a bald patch. Only meaningful when the\r\n * strands came from an asset; strands set directly are used as given.\r\n */\r\n get strandStride() {\r\n return this._strandStride;\r\n }\r\n set strandStride(val: number) {\r\n const v = val < 1 ? 1 : val | 0;\r\n if (v !== this._strandStride) {\r\n this._strandStride = v;\r\n this._applyAssetDecimation();\r\n }\r\n }\r\n /**\r\n * Upper bound on the number of strands drawn, or 0 for no bound.\r\n *\r\n * @remarks\r\n * Applied on top of {@link HairNode.strandStride} by widening the stride\r\n * further, so the result stays evenly spread.\r\n */\r\n get maxStrands() {\r\n return this._maxStrands;\r\n }\r\n set maxStrands(val: number) {\r\n const v = val < 0 ? 0 : val | 0;\r\n if (v !== this._maxStrands) {\r\n this._maxStrands = v;\r\n this._applyAssetDecimation();\r\n }\r\n }\r\n /** GPU-resident strand geometry, or null when none has been assigned. */\r\n get strands(): Nullable<HairStrandData> {\r\n return this._strands.get();\r\n }\r\n /** Number of strands currently drawn. */\r\n get strandCount() {\r\n return this._strands.get()?.strandCount ?? 0;\r\n }\r\n /**\r\n * Replaces the strands this node draws.\r\n *\r\n * @remarks\r\n * The direct route, for strands built in code rather than loaded from an\r\n * asset. Uploads the control points, sizes the draw call and recomputes the\r\n * bounding box; the previous strand data is released and any running\r\n * simulation is rebuilt, since its rest pose comes from the strands.\r\n *\r\n * @param source - Control points and topology, or null to clear the node.\r\n */\r\n setStrands(source: Nullable<HairStrandSource>) {\r\n this._disposeSimulation();\r\n this._strands.dispose();\r\n this._bounds = null;\r\n this._maxStrandLength = 0;\r\n this._source = null;\r\n if (!source) {\r\n this.material.strands = null;\r\n this._primitive.dispose();\r\n this.invalidateBoundingVolume();\r\n return;\r\n }\r\n const data = new HairStrandData(source);\r\n this._strands.set(data);\r\n this._source = source;\r\n this.material.strands = data;\r\n const extents = computeStrandExtents(source);\r\n this._bounds = extents.bounds;\r\n this._maxStrandLength = extents.maxStrandLength;\r\n this._ensurePrimitive();\r\n this._syncDrawRange();\r\n this.invalidateBoundingVolume();\r\n if (this._simulationEnabled) {\r\n this._createSimulation();\r\n }\r\n }\r\n\r\n //\r\n // Shading\r\n //\r\n // The material is an implementation detail of this node - see the class\r\n // remarks - so its controls are surfaced here. Only the ones that do something\r\n // on the strand path are: the vertex shader emits no texture coordinates, so\r\n // the material's albedo, shift and occlusion maps have nothing to sample and\r\n // are deliberately left unreachable.\r\n //\r\n\r\n /** Base colour of the strands. */\r\n get albedoColor(): Immutable<Vector4> {\r\n return this.material.albedoColor;\r\n }\r\n set albedoColor(value: Vector4) {\r\n this.material.albedoColor = value;\r\n }\r\n /**\r\n * Which scattering model shades the hair.\r\n *\r\n * @remarks\r\n * `kajiya-kay` is the art-directed double lobe; `marschner` derives the lobes\r\n * from the fibre and is the one that renders back-lighting.\r\n */\r\n get shadingModel(): HairShadingModel {\r\n return this.material.shadingModel;\r\n }\r\n set shadingModel(value: HairShadingModel) {\r\n this.material.shadingModel = value;\r\n }\r\n /** Colour of the primary, near-white specular lobe. */\r\n get specular1Color(): Immutable<Vector3> {\r\n return this.material.specular1Color;\r\n }\r\n set specular1Color(value: Vector3) {\r\n this.material.specular1Color = value;\r\n }\r\n /** Exponent of the primary specular lobe. */\r\n get specular1Power() {\r\n return this.material.specular1Power;\r\n }\r\n set specular1Power(value: number) {\r\n this.material.specular1Power = value;\r\n }\r\n /** Shift of the primary specular lobe along the strand. */\r\n get specular1Shift() {\r\n return this.material.specular1Shift;\r\n }\r\n set specular1Shift(value: number) {\r\n this.material.specular1Shift = value;\r\n }\r\n /** Colour of the secondary, tinted specular lobe. */\r\n get specular2Color(): Immutable<Vector3> {\r\n return this.material.specular2Color;\r\n }\r\n set specular2Color(value: Vector3) {\r\n this.material.specular2Color = value;\r\n }\r\n /** Exponent of the secondary specular lobe. */\r\n get specular2Power() {\r\n return this.material.specular2Power;\r\n }\r\n set specular2Power(value: number) {\r\n this.material.specular2Power = value;\r\n }\r\n /** Shift of the secondary specular lobe along the strand. */\r\n get specular2Shift() {\r\n return this.material.specular2Shift;\r\n }\r\n set specular2Shift(value: number) {\r\n this.material.specular2Shift = value;\r\n }\r\n /** How far diffuse lighting wraps around the fibre. */\r\n get diffuseWrap() {\r\n return this.material.diffuseWrap;\r\n }\r\n set diffuseWrap(value: number) {\r\n this.material.diffuseWrap = value;\r\n }\r\n /** Colour of light transmitted through the fibre. */\r\n get transmissionColor(): Immutable<Vector3> {\r\n return this.material.transmissionColor;\r\n }\r\n set transmissionColor(value: Vector3) {\r\n this.material.transmissionColor = value;\r\n }\r\n /** Strength of back-lit transmission. */\r\n get transmissionIntensity() {\r\n return this.material.transmissionIntensity;\r\n }\r\n set transmissionIntensity(value: number) {\r\n this.material.transmissionIntensity = value;\r\n }\r\n /** Falloff exponent of back-lit transmission. */\r\n get transmissionPower() {\r\n return this.material.transmissionPower;\r\n }\r\n set transmissionPower(value: number) {\r\n this.material.transmissionPower = value;\r\n }\r\n /** Colour of multiple scattering within the groom. */\r\n get scatterColor(): Immutable<Vector3> {\r\n return this.material.scatterColor;\r\n }\r\n set scatterColor(value: Vector3) {\r\n this.material.scatterColor = value;\r\n }\r\n /** Strength of multiple scattering. */\r\n get scatterIntensity() {\r\n return this.material.scatterIntensity;\r\n }\r\n set scatterIntensity(value: number) {\r\n this.material.scatterIntensity = value;\r\n }\r\n /** Share of scattering taken as local rather than global. */\r\n get scatterLocal() {\r\n return this.material.scatterLocal;\r\n }\r\n set scatterLocal(value: number) {\r\n this.material.scatterLocal = value;\r\n }\r\n /** How far scattered light wraps around the fibre. */\r\n get scatterWrap() {\r\n return this.material.scatterWrap;\r\n }\r\n set scatterWrap(value: number) {\r\n this.material.scatterWrap = value;\r\n }\r\n /** Longitudinal shift of the Marschner lobes. */\r\n get marschnerShift() {\r\n return this.material.marschnerShift;\r\n }\r\n set marschnerShift(value: number) {\r\n this.material.marschnerShift = value;\r\n }\r\n /** Longitudinal roughness of the Marschner lobes. */\r\n get marschnerRoughness() {\r\n return this.material.marschnerRoughness;\r\n }\r\n set marschnerRoughness(value: number) {\r\n this.material.marschnerRoughness = value;\r\n }\r\n /** Index of refraction of the fibre. */\r\n get marschnerIOR() {\r\n return this.material.marschnerIOR;\r\n }\r\n set marschnerIOR(value: number) {\r\n this.material.marschnerIOR = value;\r\n }\r\n /**\r\n * How strongly pigmented the fibre is.\r\n *\r\n * @remarks\r\n * A multiplier on the distance transmitted light travels before leaving, so\r\n * raising it deepens the colour of the TT and TRT paths without touching the\r\n * white surface reflection.\r\n */\r\n get marschnerAbsorption() {\r\n return this.material.marschnerAbsorption;\r\n }\r\n set marschnerAbsorption(value: number) {\r\n this.material.marschnerAbsorption = value;\r\n }\r\n /** Relative weight of the R, TT and TRT lobes. */\r\n get marschnerLobes(): Immutable<Vector3> {\r\n return this.material.marschnerLobes;\r\n }\r\n set marschnerLobes(value: Immutable<Vector3>) {\r\n this.material.marschnerLobes = value;\r\n }\r\n\r\n //\r\n // Strand expansion\r\n //\r\n\r\n /**\r\n * Ribbon segments generated per strand.\r\n *\r\n * @remarks\r\n * The main quality dial, and independent of how many control points a strand\r\n * has: the shader resamples the curve, so the same 30-point strand can be\r\n * drawn with 12 segments close up and 3 far away.\r\n */\r\n get segmentsPerStrand() {\r\n return this.material.segmentsPerStrand;\r\n }\r\n set segmentsPerStrand(val: number) {\r\n if (val !== this.material.segmentsPerStrand) {\r\n this.material.segmentsPerStrand = val;\r\n this._syncDrawRange();\r\n }\r\n }\r\n /** Multiplier on the width stored in the strand data. */\r\n get strandWidthScale() {\r\n return this.material.strandWidthScale;\r\n }\r\n set strandWidthScale(value: number) {\r\n this.material.strandWidthScale = value;\r\n }\r\n /** Lower bound on ribbon width, in world units. */\r\n get minStrandWidth() {\r\n return this.material.minStrandWidth;\r\n }\r\n set minStrandWidth(value: number) {\r\n this.material.minStrandWidth = value;\r\n }\r\n /**\r\n * Lower bound on ribbon width, in pixels.\r\n *\r\n * @remarks\r\n * Real hair is thinner than a pixel and rasterises to a flickering dotted\r\n * line. Widening it to about a pixel and paying the extra coverage back in\r\n * alpha keeps distant hair fading rather than breaking up.\r\n */\r\n get minPixelWidth() {\r\n return this.material.minPixelWidth;\r\n }\r\n set minPixelWidth(value: number) {\r\n this.material.minPixelWidth = value;\r\n }\r\n /**\r\n * How far the shading normal bends across the ribbon, in [0, 1].\r\n *\r\n * @remarks\r\n * At 0 a strand shades as a flat tape, at 1 as a cylinder.\r\n */\r\n get strandRoundness() {\r\n return this.material.strandRoundness;\r\n }\r\n set strandRoundness(value: number) {\r\n this.material.strandRoundness = value;\r\n }\r\n /**\r\n * Strength of the root-depth ambient occlusion, in [0, 1].\r\n *\r\n * @remarks\r\n * Environment irradiance is not attenuated by anything - a shadow map answers\r\n * for one light, not for the sky - so without this every strand of a groom\r\n * receives the full sky and a dense hairstyle is lit as brightly in its\r\n * interior as on its surface, which reads as a flat silhouette with no volume.\r\n * Most obvious on pale hair, where there is little pigment to darken the\r\n * interior instead. Set to 0 to disable.\r\n *\r\n * Ambient light only: direct light is already attenuated by the shadow map,\r\n * and under DOM by a graded transmittance, so it is left alone.\r\n */\r\n get rootOcclusion() {\r\n return this.material.rootOcclusion;\r\n }\r\n set rootOcclusion(value: number) {\r\n this.material.rootOcclusion = value;\r\n }\r\n /**\r\n * How far along a strand the root occlusion reaches, in (0, 1].\r\n *\r\n * @remarks\r\n * Short for a groom whose strands leave the scalp quickly, longer for one\r\n * where they lie against each other most of their length.\r\n */\r\n get rootOcclusionRange() {\r\n return this.material.rootOcclusionRange;\r\n }\r\n set rootOcclusionRange(value: number) {\r\n this.material.rootOcclusionRange = value;\r\n }\r\n /** Drop strands with distance, redistributing their coverage to the survivors. */\r\n get strandLOD() {\r\n return this.material.strandLOD;\r\n }\r\n set strandLOD(value: boolean) {\r\n this.material.strandLOD = value;\r\n }\r\n /** Floor on the fraction of strands distance decimation keeps. */\r\n get minStrandLODRatio() {\r\n return this.material.minStrandLODRatio;\r\n }\r\n set minStrandLODRatio(value: number) {\r\n this.material.minStrandLODRatio = value;\r\n }\r\n\r\n //\r\n // Transparency and shadows\r\n //\r\n\r\n /**\r\n * How the strands blend with what is behind them.\r\n *\r\n * @remarks\r\n * The pixel-width floor pays its widening back in alpha, which only means\r\n * anything once blending is on; left at `none` every strand covers its pixel\r\n * fully and a dense groom reads as a solid shell.\r\n */\r\n get blendMode(): BlendMode {\r\n return this.material.blendMode;\r\n }\r\n set blendMode(value: BlendMode) {\r\n this.material.blendMode = value;\r\n }\r\n /** Alpha below which a fragment is discarded. */\r\n get alphaCutoff() {\r\n return this.material.alphaCutoff;\r\n }\r\n set alphaCutoff(value: number) {\r\n this.material.alphaCutoff = value;\r\n }\r\n /** Dither the alpha test, so a temporal filter resolves the soft edge. */\r\n get alphaDither() {\r\n return this.material.alphaDither;\r\n }\r\n set alphaDither(value: boolean) {\r\n this.material.alphaDither = value;\r\n }\r\n /**\r\n * Whether a blended groom contributes motion vectors.\r\n *\r\n * @remarks\r\n * Only meaningful with {@link HairNode.blendMode} set to something other than\r\n * `none`. Blended geometry is otherwise absent from the prepass, so a groom\r\n * carries the velocity of whatever is behind it and a temporal filter smears\r\n * it as it swings - the same ghosting the dithered path avoids by being\r\n * opaque. On by default, because a groom is dense enough to own its pixels\r\n * and there is no reason to want the ghosting.\r\n */\r\n get transparentMotionVector() {\r\n return this.material.transparentMotionVector;\r\n }\r\n set transparentMotionVector(value: boolean) {\r\n this.material.transparentMotionVector = value;\r\n }\r\n /** Whether the groom is drawn into shadow maps. */\r\n get castShadow() {\r\n return this._castShadow;\r\n }\r\n set castShadow(val: boolean) {\r\n this._castShadow = !!val;\r\n }\r\n /** Let the shadow caster pass respect strand alpha, for softer self-shadowing. */\r\n get transparentShadowCaster() {\r\n return this.material.transparentShadowCaster;\r\n }\r\n set transparentShadowCaster(value: boolean) {\r\n this.material.transparentShadowCaster = value;\r\n }\r\n /** Alpha below which a fragment casts no shadow. */\r\n get shadowAlphaCutoff() {\r\n return this.material.shadowAlphaCutoff;\r\n }\r\n set shadowAlphaCutoff(value: number) {\r\n this.material.shadowAlphaCutoff = value;\r\n }\r\n\r\n //\r\n // Simulation\r\n //\r\n\r\n /**\r\n * Whether strand dynamics run on this groom.\r\n *\r\n * @remarks\r\n * WebGPU only. Switching it on with no strands loaded is allowed - the\r\n * simulation is built when the strands arrive.\r\n */\r\n get simulationEnabled() {\r\n return this._simulationEnabled;\r\n }\r\n set simulationEnabled(val: boolean) {\r\n const enabled = !!val;\r\n if (enabled === this._simulationEnabled) {\r\n return;\r\n }\r\n this._simulationEnabled = enabled;\r\n if (enabled) {\r\n this._createSimulation();\r\n } else {\r\n this._disposeSimulation();\r\n // Without this the groom keeps whatever pose it was last simulated into,\r\n // which reads as a bug rather than as hair that stopped moving.\r\n if (this._source) {\r\n this._strands.get()?.dispose();\r\n this._strands.set(new HairStrandData(this._source));\r\n this.material.strands = this._strands.get();\r\n }\r\n // Back to the tight rest bounds.\r\n this.invalidateBoundingVolume();\r\n }\r\n }\r\n /**\r\n * The running strand dynamics, or null when they are off or unsupported.\r\n *\r\n * @remarks\r\n * Exposed so a caller can reach past the node's own dials - to set colliders,\r\n * for one, which have no serialised form here.\r\n */\r\n get simulation(): Nullable<GPUHairSimulation> {\r\n return this._simulation;\r\n }\r\n /** World-space gravity acting on the strands. */\r\n get gravity(): Immutable<Vector3> {\r\n return this._simulationOptions.gravity!;\r\n }\r\n set gravity(value: Vector3) {\r\n this._simulationOptions.gravity = new Vector3(value);\r\n if (this._simulation) {\r\n this._simulation.gravity = this._simulationOptions.gravity;\r\n }\r\n }\r\n /**\r\n * How strongly strands keep the shape they were authored with, in [0, 1].\r\n *\r\n * @remarks\r\n * The dial to reach for first. It holds the angle each segment makes with the\r\n * one before it, measured against that segment's current direction rather\r\n * than a fixed pose, so a groom keeps its curl while remaining free to hang\r\n * and swing. It is also what damps a groom: without bending resistance a\r\n * strand carries a disturbance up and down its length for seconds.\r\n */\r\n get localStiffness() {\r\n return this._simulationOptions.localStiffness!;\r\n }\r\n set localStiffness(value: number) {\r\n this._simulationOptions.localStiffness = value;\r\n if (this._simulation) {\r\n this._simulation.localStiffness = value;\r\n }\r\n }\r\n /** Local shape constraint passes per substep. */\r\n get localIterations() {\r\n return this._simulationOptions.localIterations!;\r\n }\r\n set localIterations(value: number) {\r\n this._simulationOptions.localIterations = value;\r\n if (this._simulation) {\r\n this._simulation.localIterations = value;\r\n }\r\n }\r\n /**\r\n * How strongly strands are pulled back to their authored pose, in [0, 1].\r\n *\r\n * @remarks\r\n * Off by default. It anchors a point to a fixed place rather than to its\r\n * neighbours, which is a spring, and a groom held by springs reads as one.\r\n * {@link HairNode.localStiffness} is what holds styling without that; this is\r\n * for grooms that genuinely should not move far from how they were authored,\r\n * and wants {@link HairNode.globalRange} set as well.\r\n */\r\n get globalStiffness() {\r\n return this._simulationOptions.globalStiffness!;\r\n }\r\n set globalStiffness(value: number) {\r\n this._simulationOptions.globalStiffness = value;\r\n if (this._simulation) {\r\n this._simulation.globalStiffness = value;\r\n }\r\n }\r\n /** Fraction of a strand, from the root, the pose pull acts on, in [0, 1]. */\r\n get globalRange() {\r\n return this._simulationOptions.globalRange!;\r\n }\r\n set globalRange(value: number) {\r\n this._simulationOptions.globalRange = value;\r\n if (this._simulation) {\r\n this._simulation.globalRange = value;\r\n }\r\n }\r\n /**\r\n * Share of a point's length correction fed back into its parent, in [0, 1].\r\n *\r\n * @remarks\r\n * What keeps the solver from gaining energy as the node moves. Lowering it\r\n * makes a groom livelier and, past a point, makes it whip; 0 removes the\r\n * feedback entirely.\r\n */\r\n get ftlDamping() {\r\n return this._simulationOptions.ftlDamping!;\r\n }\r\n set ftlDamping(value: number) {\r\n this._simulationOptions.ftlDamping = value;\r\n if (this._simulation) {\r\n this._simulation.ftlDamping = value;\r\n }\r\n }\r\n /** Velocity lost each step, in [0, 1]. */\r\n get damping() {\r\n return this._simulationOptions.damping!;\r\n }\r\n set damping(value: number) {\r\n this._simulationOptions.damping = value;\r\n if (this._simulation) {\r\n this._simulation.damping = value;\r\n }\r\n }\r\n /** Tangential motion removed at a contact, in [0, 1]. */\r\n get friction() {\r\n return this._simulationOptions.friction!;\r\n }\r\n set friction(value: number) {\r\n this._simulationOptions.friction = value;\r\n if (this._simulation) {\r\n this._simulation.friction = value;\r\n }\r\n }\r\n /**\r\n * How much of the node's motion strands are carried along by, in [0, 1].\r\n *\r\n * @remarks\r\n * 0 leaves a groom exactly where it was in the world and lets the pinned\r\n * roots drag it along, which is what produces lag; 1 carries it rigidly with\r\n * the node, so it never lags. Lower values give a livelier, floppier groom.\r\n */\r\n get vspCoeff() {\r\n return this._simulationOptions.vspCoeff!;\r\n }\r\n set vspCoeff(value: number) {\r\n this._simulationOptions.vspCoeff = value;\r\n if (this._simulation) {\r\n this._simulation.vspCoeff = value;\r\n }\r\n }\r\n /** Node acceleration above which strands are carried rigidly, in units/s². */\r\n get vspAccelThreshold() {\r\n return this._simulationOptions.vspAccelThreshold!;\r\n }\r\n set vspAccelThreshold(value: number) {\r\n this._simulationOptions.vspAccelThreshold = value;\r\n if (this._simulation) {\r\n this._simulation.vspAccelThreshold = value;\r\n }\r\n }\r\n /**\r\n * Per-substep ceiling on how far a point may travel, in segment lengths.\r\n *\r\n * @remarks\r\n * A backstop against a single bad substep, not a dial to shape motion with:\r\n * at the default it never engages during ordinary movement.\r\n */\r\n get maxSpeedFactor() {\r\n return this._simulationOptions.maxSpeedFactor!;\r\n }\r\n set maxSpeedFactor(value: number) {\r\n this._simulationOptions.maxSpeedFactor = value;\r\n if (this._simulation) {\r\n this._simulation.maxSpeedFactor = value;\r\n }\r\n }\r\n /** Integration substeps per fixed step. */\r\n get substeps() {\r\n return this._simulationOptions.substeps!;\r\n }\r\n set substeps(value: number) {\r\n this._simulationOptions.substeps = value;\r\n if (this._simulation) {\r\n this._simulation.substeps = value;\r\n }\r\n }\r\n\r\n //\r\n // Scene node and drawable\r\n //\r\n\r\n /**\r\n * {@inheritDoc SceneNode.update}\r\n *\r\n * @remarks\r\n * Drives the strand solver. The node re-queues itself every frame, because a\r\n * simulation only stands still when it is switched off.\r\n */\r\n update(frameId: number, elapsedInSeconds: number, deltaInSeconds: number) {\r\n super.update(frameId, elapsedInSeconds, deltaInSeconds);\r\n const simulation = this._simulation;\r\n if (simulation?.enabled) {\r\n simulation.update(deltaInSeconds, this.worldMatrix);\r\n this.scene?.queueUpdateNode(this);\r\n }\r\n }\r\n /**\r\n * {@inheritDoc SceneNode.computeBoundingVolume}\r\n *\r\n * @remarks\r\n * Derived on the CPU from the control points, because the ribbons are built in\r\n * the vertex shader and the engine never sees them. The box is in local space;\r\n * the base class applies the world matrix.\r\n *\r\n * While a simulation runs, the strands swing out of their authored pose, so\r\n * the rest bounds are padded by the longest strand: the solver keeps every\r\n * point within its strand's length of the pinned root, which bounds any pose\r\n * it can produce.\r\n */\r\n computeBoundingVolume(): Nullable<BoundingBox> {\r\n if (!this._bounds) {\r\n return null;\r\n }\r\n const box = new BoundingBox(this._bounds);\r\n if (this._simulation) {\r\n const pad = this._maxStrandLength;\r\n box.minPoint.setXYZ(box.minPoint.x - pad, box.minPoint.y - pad, box.minPoint.z - pad);\r\n box.maxPoint.setXYZ(box.maxPoint.x + pad, box.maxPoint.y + pad, box.maxPoint.z + pad);\r\n }\r\n return box;\r\n }\r\n /** {@inheritDoc SceneNode.isHair} */\r\n isHair(): this is HairNode {\r\n return true;\r\n }\r\n /** {@inheritDoc Drawable.getPickTarget} */\r\n getPickTarget() {\r\n return this._pickTarget;\r\n }\r\n /** {@inheritDoc Drawable.getMaterial} */\r\n getMaterial() {\r\n return this._material.get();\r\n }\r\n /** {@inheritDoc Drawable.getPrimitive} */\r\n getPrimitive() {\r\n return this._primitive.get();\r\n }\r\n /** {@inheritDoc Drawable.getMorphData} */\r\n getMorphData() {\r\n return null;\r\n }\r\n /** {@inheritDoc Drawable.getMorphInfo} */\r\n getMorphInfo() {\r\n return null;\r\n }\r\n /** {@inheritDoc Drawable.getSkinInfluenceData} */\r\n getSkinInfluenceData() {\r\n return null;\r\n }\r\n /** {@inheritDoc Drawable.getQueueType} */\r\n getQueueType() {\r\n return this._material.get()?.getQueueType() ?? QUEUE_OPAQUE;\r\n }\r\n /** {@inheritDoc Drawable.isUnlit} */\r\n isUnlit() {\r\n return !this._material.get()?.supportLighting();\r\n }\r\n /** {@inheritDoc Drawable.needSceneColor} */\r\n needSceneColor() {\r\n return this._material.get()?.needSceneColor() ?? false;\r\n }\r\n /** {@inheritDoc Drawable.needSceneDepth} */\r\n needSceneDepth() {\r\n return this._material.get()?.needSceneDepth() ?? false;\r\n }\r\n /** {@inheritDoc Drawable.draw} */\r\n draw(ctx: DrawContext, renderQueue: Nullable<RenderQueue>) {\r\n const material = this._material.get();\r\n const primitive = this._primitive.get();\r\n // An empty groom is a legitimate state - a node exists before its asset has\r\n // finished loading - and the draw range is zero until strands arrive.\r\n if (material && primitive && material.vertexCount > 0) {\r\n this.bind(ctx, renderQueue);\r\n material.draw(primitive, ctx);\r\n }\r\n }\r\n protected onDispose() {\r\n super.onDispose();\r\n this._disposeSimulation();\r\n this._primitive.dispose();\r\n this._strands.dispose();\r\n this._material.dispose();\r\n this._parsedAsset = null;\r\n this._source = null;\r\n this._bounds = null;\r\n }\r\n\r\n //\r\n // Internals\r\n //\r\n\r\n /**\r\n * The material, which is an implementation detail of this node.\r\n * @internal\r\n */\r\n private get material() {\r\n return this._material.get()!;\r\n }\r\n /**\r\n * Re-decimates the opened asset into the strands actually drawn.\r\n * @internal\r\n */\r\n private _applyAssetDecimation() {\r\n const parsed = this._parsedAsset;\r\n if (!parsed) {\r\n return;\r\n }\r\n // A `.zhair` holds one set per source curve object, and an XGen export\r\n // routinely carries several spline descriptions for a single hairstyle;\r\n // merging lets the whole head draw in one call.\r\n this.setStrands(\r\n mergeHairStrandSources(\r\n loadZHairStrandSources(parsed, {\r\n strandStride: this._strandStride,\r\n maxStrands: this._maxStrands\r\n })\r\n )\r\n );\r\n }\r\n /**\r\n * Starts strand dynamics over the current strands.\r\n * @internal\r\n */\r\n private _createSimulation() {\r\n this._disposeSimulation();\r\n const data = this._strands.get();\r\n if (!data || !this._source || !isHairSimulationSupported()) {\r\n return;\r\n }\r\n const simulation = new GPUHairSimulation(data, this._source, this._simulationOptions);\r\n if (simulation.enabled) {\r\n this._simulation = simulation;\r\n // Motion vectors have to follow the control points once something is\r\n // moving them, or a groom swinging under a still head reports no motion\r\n // and a temporal filter smears it.\r\n this.material.prevPoints = simulation.framePointBuffer;\r\n // The bounds widen while a simulation runs - see computeBoundingVolume.\r\n this.invalidateBoundingVolume();\r\n this.scene?.queueUpdateNode(this);\r\n } else {\r\n console.warn(simulation.disabledReason ?? 'GPU hair simulation could not start');\r\n simulation.dispose();\r\n }\r\n }\r\n /** @internal */\r\n private _disposeSimulation() {\r\n this.material.prevPoints = null;\r\n this._simulation?.dispose();\r\n this._simulation = null;\r\n }\r\n /**\r\n * Builds the primitive the draw call is issued against.\r\n *\r\n * @remarks\r\n * No vertex attribute is ever read - the vertex shader derives everything from\r\n * the vertex index and the storage buffers - but a draw still needs a vertex\r\n * layout to exist, and one float is the smallest buffer that establishes one.\r\n * @internal\r\n */\r\n private _ensurePrimitive() {\r\n if (this._primitive.get()) {\r\n return;\r\n }\r\n const primitive = new Primitive();\r\n primitive.createAndSetVertexBuffer('position_f32x3', new Float32Array(3));\r\n primitive.primitiveType = 'triangle-list';\r\n this._primitive.set(primitive);\r\n }\r\n /**\r\n * Points the draw range at however many vertices the material now needs.\r\n *\r\n * @remarks\r\n * Drawing is non-indexed, six vertices per ribbon segment, so the count moves\r\n * with both the strand count and {@link HairNode.segmentsPerStrand}.\r\n * @internal\r\n */\r\n private _syncDrawRange() {\r\n const primitive = this._primitive.get();\r\n if (primitive) {\r\n primitive.indexCount = this.material.vertexCount;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Local-space bounds of a strand source, padded by the widest strand, along\r\n * with the length of the longest strand.\r\n *\r\n * @remarks\r\n * The ribbon is built around the control point, so half a width would suffice;\r\n * a full width is used because {@link HairNode.minPixelWidth} widens sub-pixel\r\n * strands beyond their authored size and the box has to survive that.\r\n *\r\n * The strand length is measured here because the same pass already touches\r\n * every point; it pads the bounds while a simulation runs.\r\n * @internal\r\n */\r\nfunction computeStrandExtents(source: HairStrandSource): {\r\n bounds: BoundingBox;\r\n maxStrandLength: number;\r\n} {\r\n const scale = source.scale ?? 1;\r\n const positions = source.positions;\r\n const box = new BoundingBox();\r\n box.beginExtend();\r\n let maxStrandLength = 0;\r\n let first = 0;\r\n for (let s = 0; s < source.pointCounts.length; s++) {\r\n const count = source.pointCounts[s];\r\n let length = 0;\r\n for (let i = 0; i < count; i++) {\r\n const p = (first + i) * 3;\r\n const x = positions[p] * scale;\r\n const y = positions[p + 1] * scale;\r\n const z = positions[p + 2] * scale;\r\n box.extend3(x, y, z);\r\n if (i > 0) {\r\n length += Math.hypot(\r\n x - positions[p - 3] * scale,\r\n y - positions[p - 2] * scale,\r\n z - positions[p - 1] * scale\r\n );\r\n }\r\n }\r\n if (length > maxStrandLength) {\r\n maxStrandLength = length;\r\n }\r\n first += count;\r\n }\r\n let widest = source.defaultWidth ?? 0.0001;\r\n if (source.widths) {\r\n for (let i = 0; i < source.widths.length; i++) {\r\n if (source.widths[i] > widest) {\r\n widest = source.widths[i];\r\n }\r\n }\r\n }\r\n const pad = widest * scale * (source.widthScale ?? 1);\r\n box.minPoint.setXYZ(box.minPoint.x - pad, box.minPoint.y - pad, box.minPoint.z - pad);\r\n box.maxPoint.setXYZ(box.maxPoint.x + pad, box.maxPoint.y + pad, box.maxPoint.z + pad);\r\n return { bounds: box, maxStrandLength };\r\n}\r\n"],"names":["HairNode","applyMixins","GraphNode","mixinDrawable","scene","_pickTarget","node","_material","DRef","HairStrandMaterial","_primitive","_strands","_bounds","_maxStrandLength","_castShadow","_source","_hairAsset","_parsedAsset","_strandStride","_maxStrands","_simulation","_simulationEnabled","_simulationOptions","gravity","Vector3","damping","globalStiffness","globalRange","localStiffness","localIterations","ftlDamping","vspCoeff","vspAccelThreshold","substeps","friction","maxSpeedFactor","hairAsset","setHairAsset","path","assetId","trim","setStrands","parsed","data","getEngine","resourceManager","assetManager","fetchBinaryData","parseZHair","err","console","error","_applyAssetDecimation","strandStride","val","v","maxStrands","strands","get","strandCount","source","_disposeSimulation","dispose","material","invalidateBoundingVolume","HairStrandData","set","extents","computeStrandExtents","bounds","maxStrandLength","_ensurePrimitive","_syncDrawRange","_createSimulation","albedoColor","value","shadingModel","specular1Color","specular1Power","specular1Shift","specular2Color","specular2Power","specular2Shift","diffuseWrap","transmissionColor","transmissionIntensity","transmissionPower","scatterColor","scatterIntensity","scatterLocal","scatterWrap","marschnerShift","marschnerRoughness","marschnerIOR","marschnerAbsorption","marschnerLobes","segmentsPerStrand","strandWidthScale","minStrandWidth","minPixelWidth","strandRoundness","rootOcclusion","rootOcclusionRange","strandLOD","minStrandLODRatio","blendMode","alphaCutoff","alphaDither","transparentMotionVector","castShadow","transparentShadowCaster","shadowAlphaCutoff","simulationEnabled","enabled","simulation","update","frameId","elapsedInSeconds","deltaInSeconds","worldMatrix","queueUpdateNode","computeBoundingVolume","box","BoundingBox","pad","minPoint","setXYZ","x","y","z","maxPoint","isHair","getPickTarget","getMaterial","getPrimitive","getMorphData","getMorphInfo","getSkinInfluenceData","getQueueType","QUEUE_OPAQUE","isUnlit","supportLighting","needSceneColor","needSceneDepth","draw","ctx","renderQueue","primitive","vertexCount","bind","onDispose","mergeHairStrandSources","loadZHairStrandSources","isHairSimulationSupported","GPUHairSimulation","prevPoints","framePointBuffer","warn","disabledReason","Primitive","createAndSetVertexBuffer","Float32Array","primitiveType","indexCount","scale","positions","beginExtend","first","s","pointCounts","length","count","i","p","extend3","Math","hypot","widest","defaultWidth","widths","widthScale"],"mappings":"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"}
1
+ {"version":3,"file":"hair.js","sources":["../../src/scene/hair.ts"],"sourcesContent":["/**\r\n * A groom in the scene graph.\r\n *\r\n * @remarks\r\n * Strand hair is drawn from control points held in storage buffers and expanded\r\n * into camera-facing ribbons in the vertex shader, which needs a handful of\r\n * pieces wired together that nothing else in the engine needs: a primitive that\r\n * carries no attributes but still establishes a vertex layout, a draw range\r\n * derived from the material rather than from an index buffer, and a bounding box\r\n * computed on the CPU because the geometry does not exist until the vertex stage\r\n * has run. This node owns that assembly so a groom can be placed, transformed,\r\n * serialised and simulated like anything else.\r\n *\r\n * The material is deliberately not exposed. Strand geometry only exists inside\r\n * {@link HairStrandMaterial} - it reads control points from storage buffers that\r\n * no other material knows about - so letting one be swapped in would produce a\r\n * node that cannot draw. The shading controls are surfaced as properties of the\r\n * node instead, and they are what gets serialised.\r\n *\r\n * The geometry itself lives in a `.zhair` asset, referenced by path. That file\r\n * holds nothing but strands: the node's own settings, shading included, belong\r\n * to the scene rather than to the asset, so one groom can be shared by several\r\n * characters that each style it differently.\r\n *\r\n * WebGPU only, because the expansion reads storage buffers from the vertex\r\n * stage. On other backends the node exists and keeps its data but draws nothing.\r\n */\r\nimport type { Immutable, Nullable, Vector4 } from '@zephyr3d/base';\r\nimport { applyMixins, DRef, Vector3 } from '@zephyr3d/base';\r\nimport type { Scene } from './scene';\r\nimport { GraphNode } from './graph_node';\r\nimport { mixinDrawable } from '../render/drawable_mixin';\r\nimport type { Drawable, DrawContext, PickTarget, RenderQueue } from '../render';\r\nimport { Primitive } from '../render';\r\nimport { HairStrandMaterial } from '../material/hairstrand';\r\nimport type { HairShadingModel } from '../material/hair';\r\nimport { HairStrandData, type HairStrandSource } from '../material/hairstrand_data';\r\nimport {\r\n GPUHairSimulation,\r\n isHairSimulationSupported,\r\n type GPUHairSimulationOptions\r\n} from '../animation/hair/gpu_hair_simulation';\r\nimport type { ParsedZHair } from '../asset/loaders/zhair/zhair_format';\r\nimport { parseZHair } from '../asset/loaders/zhair/zhair_format';\r\nimport { loadZHairStrandSources, mergeHairStrandSources } from '../asset/loaders/zhair/zhair_loader';\r\nimport { BoundingBox } from '../utility/bounding_volume';\r\nimport { QUEUE_OPAQUE } from '../values';\r\nimport { getEngine } from '../app/api';\r\nimport type { BlendMode } from '../material/meshmaterial';\r\nimport { warnUnsupported } from '../utility/unsupported';\r\n\r\n/**\r\n * Hair scene node.\r\n *\r\n * @remarks\r\n * Point it at a `.zhair` asset with {@link HairNode.setHairAsset}, or hand it\r\n * control points directly with {@link HairNode.setStrands}. Everything the draw\r\n * call needs is derived from them.\r\n *\r\n * @public\r\n */\r\nexport class HairNode extends applyMixins(GraphNode, mixinDrawable) implements Drawable {\r\n /** @internal */\r\n private readonly _pickTarget: PickTarget;\r\n /** @internal */\r\n private readonly _material: DRef<HairStrandMaterial>;\r\n /** @internal */\r\n private readonly _primitive: DRef<Primitive>;\r\n /** @internal */\r\n private readonly _strands: DRef<HairStrandData>;\r\n /** @internal Local-space bounds of the control points, width included. */\r\n private _bounds: Nullable<BoundingBox>;\r\n /** @internal Length of the longest strand, for padding the bounds while simulating. */\r\n private _maxStrandLength: number;\r\n /** @internal */\r\n private _castShadow: boolean;\r\n /** @internal Kept so the simulation can be rebuilt from the authored pose. */\r\n private _source: Nullable<HairStrandSource>;\r\n /** @internal Path of the `.zhair` asset, empty when strands were set directly. */\r\n private _hairAsset: string;\r\n /**\r\n * @internal The opened asset, kept so decimation can be changed without\r\n * reading the file again.\r\n */\r\n private _parsedAsset: Nullable<ParsedZHair>;\r\n /** @internal */\r\n private _strandStride: number;\r\n /** @internal */\r\n private _maxStrands: number;\r\n /** @internal */\r\n private _simulation: Nullable<GPUHairSimulation>;\r\n /** @internal */\r\n private _simulationEnabled: boolean;\r\n /**\r\n * @internal Simulation tuning, owned by the node.\r\n *\r\n * Held here rather than on the simulation because the node outlives it: the\r\n * dials survive the simulation being switched off and on, and can be set\r\n * before any strands have loaded.\r\n */\r\n private readonly _simulationOptions: GPUHairSimulationOptions;\r\n /**\r\n * Creates an empty hair node.\r\n *\r\n * @param scene - Scene the node belongs to.\r\n */\r\n constructor(scene: Scene) {\r\n super(scene);\r\n this._pickTarget = { node: this };\r\n this._material = new DRef(new HairStrandMaterial());\r\n this._primitive = new DRef();\r\n this._strands = new DRef();\r\n this._bounds = null;\r\n this._maxStrandLength = 0;\r\n this._castShadow = true;\r\n this._source = null;\r\n this._hairAsset = '';\r\n this._parsedAsset = null;\r\n this._strandStride = 1;\r\n this._maxStrands = 0;\r\n this._simulation = null;\r\n this._simulationEnabled = false;\r\n this._simulationOptions = {\r\n gravity: new Vector3(0, -9.8, 0),\r\n damping: 0.08,\r\n globalStiffness: 0,\r\n globalRange: 0,\r\n localStiffness: 0.9,\r\n localIterations: 2,\r\n ftlDamping: 0.7,\r\n vspCoeff: 0.8,\r\n vspAccelThreshold: 50,\r\n substeps: 1,\r\n friction: 0.2,\r\n maxSpeedFactor: 4\r\n };\r\n }\r\n\r\n //\r\n // Geometry\r\n //\r\n\r\n /** Path of the `.zhair` asset this node draws, or an empty string. */\r\n get hairAsset() {\r\n return this._hairAsset;\r\n }\r\n /**\r\n * Loads a `.zhair` asset.\r\n *\r\n * @remarks\r\n * The opened file is kept, so changing {@link HairNode.strandStride} or\r\n * {@link HairNode.maxStrands} afterwards re-decimates what is already in\r\n * memory instead of reading it again - and never touches the source archive\r\n * the asset was converted from.\r\n *\r\n * @param path - Asset path, or an empty string to clear the node.\r\n */\r\n async setHairAsset(path: string) {\r\n const assetId = path?.trim() ?? '';\r\n if (assetId === this._hairAsset) {\r\n return;\r\n }\r\n this._hairAsset = assetId;\r\n this._parsedAsset = null;\r\n if (!assetId) {\r\n this.setStrands(null);\r\n return;\r\n }\r\n let parsed: ParsedZHair;\r\n try {\r\n const data = (await getEngine().resourceManager.assetManager.fetchBinaryData(assetId)) as ArrayBuffer;\r\n parsed = parseZHair(data);\r\n } catch (err) {\r\n console.error(`Load hair asset failed: ${assetId}: ${err}`);\r\n this.setStrands(null);\r\n return;\r\n }\r\n // A load that was superseded while it was in flight must not overwrite the\r\n // asset that replaced it.\r\n if (this._hairAsset !== assetId) {\r\n return;\r\n }\r\n this._parsedAsset = parsed;\r\n this._applyAssetDecimation();\r\n }\r\n /**\r\n * Draw one strand in every `strandStride`.\r\n *\r\n * @remarks\r\n * A stride rather than a leading slice, so the kept strands stay spread over\r\n * the whole scalp instead of leaving a bald patch. Only meaningful when the\r\n * strands came from an asset; strands set directly are used as given.\r\n */\r\n get strandStride() {\r\n return this._strandStride;\r\n }\r\n set strandStride(val: number) {\r\n const v = val < 1 ? 1 : val | 0;\r\n if (v !== this._strandStride) {\r\n this._strandStride = v;\r\n this._applyAssetDecimation();\r\n }\r\n }\r\n /**\r\n * Upper bound on the number of strands drawn, or 0 for no bound.\r\n *\r\n * @remarks\r\n * Applied on top of {@link HairNode.strandStride} by widening the stride\r\n * further, so the result stays evenly spread.\r\n */\r\n get maxStrands() {\r\n return this._maxStrands;\r\n }\r\n set maxStrands(val: number) {\r\n const v = val < 0 ? 0 : val | 0;\r\n if (v !== this._maxStrands) {\r\n this._maxStrands = v;\r\n this._applyAssetDecimation();\r\n }\r\n }\r\n /** GPU-resident strand geometry, or null when none has been assigned. */\r\n get strands(): Nullable<HairStrandData> {\r\n return this._strands.get();\r\n }\r\n /** Number of strands currently drawn. */\r\n get strandCount() {\r\n return this._strands.get()?.strandCount ?? 0;\r\n }\r\n /**\r\n * Replaces the strands this node draws.\r\n *\r\n * @remarks\r\n * The direct route, for strands built in code rather than loaded from an\r\n * asset. Uploads the control points, sizes the draw call and recomputes the\r\n * bounding box; the previous strand data is released and any running\r\n * simulation is rebuilt, since its rest pose comes from the strands.\r\n *\r\n * @param source - Control points and topology, or null to clear the node.\r\n */\r\n setStrands(source: Nullable<HairStrandSource>) {\r\n this._disposeSimulation();\r\n this._strands.dispose();\r\n this._bounds = null;\r\n this._maxStrandLength = 0;\r\n this._source = null;\r\n if (!source) {\r\n this.material.strands = null;\r\n this._primitive.dispose();\r\n this.invalidateBoundingVolume();\r\n return;\r\n }\r\n const data = new HairStrandData(source);\r\n this._strands.set(data);\r\n this._source = source;\r\n this.material.strands = data;\r\n const extents = computeStrandExtents(source);\r\n this._bounds = extents.bounds;\r\n this._maxStrandLength = extents.maxStrandLength;\r\n this._ensurePrimitive();\r\n this._syncDrawRange();\r\n this.invalidateBoundingVolume();\r\n if (this._simulationEnabled) {\r\n this._createSimulation();\r\n }\r\n }\r\n\r\n //\r\n // Shading\r\n //\r\n // The material is an implementation detail of this node - see the class\r\n // remarks - so its controls are surfaced here. Only the ones that do something\r\n // on the strand path are: the vertex shader emits no texture coordinates, so\r\n // the material's albedo, shift and occlusion maps have nothing to sample and\r\n // are deliberately left unreachable.\r\n //\r\n\r\n /** Base colour of the strands. */\r\n get albedoColor(): Immutable<Vector4> {\r\n return this.material.albedoColor;\r\n }\r\n set albedoColor(value: Vector4) {\r\n this.material.albedoColor = value;\r\n }\r\n /**\r\n * Which scattering model shades the hair.\r\n *\r\n * @remarks\r\n * `kajiya-kay` is the art-directed double lobe; `marschner` derives the lobes\r\n * from the fibre and is the one that renders back-lighting.\r\n */\r\n get shadingModel(): HairShadingModel {\r\n return this.material.shadingModel;\r\n }\r\n set shadingModel(value: HairShadingModel) {\r\n this.material.shadingModel = value;\r\n }\r\n /** Colour of the primary, near-white specular lobe. */\r\n get specular1Color(): Immutable<Vector3> {\r\n return this.material.specular1Color;\r\n }\r\n set specular1Color(value: Vector3) {\r\n this.material.specular1Color = value;\r\n }\r\n /** Exponent of the primary specular lobe. */\r\n get specular1Power() {\r\n return this.material.specular1Power;\r\n }\r\n set specular1Power(value: number) {\r\n this.material.specular1Power = value;\r\n }\r\n /** Shift of the primary specular lobe along the strand. */\r\n get specular1Shift() {\r\n return this.material.specular1Shift;\r\n }\r\n set specular1Shift(value: number) {\r\n this.material.specular1Shift = value;\r\n }\r\n /** Colour of the secondary, tinted specular lobe. */\r\n get specular2Color(): Immutable<Vector3> {\r\n return this.material.specular2Color;\r\n }\r\n set specular2Color(value: Vector3) {\r\n this.material.specular2Color = value;\r\n }\r\n /** Exponent of the secondary specular lobe. */\r\n get specular2Power() {\r\n return this.material.specular2Power;\r\n }\r\n set specular2Power(value: number) {\r\n this.material.specular2Power = value;\r\n }\r\n /** Shift of the secondary specular lobe along the strand. */\r\n get specular2Shift() {\r\n return this.material.specular2Shift;\r\n }\r\n set specular2Shift(value: number) {\r\n this.material.specular2Shift = value;\r\n }\r\n /** How far diffuse lighting wraps around the fibre. */\r\n get diffuseWrap() {\r\n return this.material.diffuseWrap;\r\n }\r\n set diffuseWrap(value: number) {\r\n this.material.diffuseWrap = value;\r\n }\r\n /** Colour of light transmitted through the fibre. */\r\n get transmissionColor(): Immutable<Vector3> {\r\n return this.material.transmissionColor;\r\n }\r\n set transmissionColor(value: Vector3) {\r\n this.material.transmissionColor = value;\r\n }\r\n /** Strength of back-lit transmission. */\r\n get transmissionIntensity() {\r\n return this.material.transmissionIntensity;\r\n }\r\n set transmissionIntensity(value: number) {\r\n this.material.transmissionIntensity = value;\r\n }\r\n /** Falloff exponent of back-lit transmission. */\r\n get transmissionPower() {\r\n return this.material.transmissionPower;\r\n }\r\n set transmissionPower(value: number) {\r\n this.material.transmissionPower = value;\r\n }\r\n /** Colour of multiple scattering within the groom. */\r\n get scatterColor(): Immutable<Vector3> {\r\n return this.material.scatterColor;\r\n }\r\n set scatterColor(value: Vector3) {\r\n this.material.scatterColor = value;\r\n }\r\n /** Strength of multiple scattering. */\r\n get scatterIntensity() {\r\n return this.material.scatterIntensity;\r\n }\r\n set scatterIntensity(value: number) {\r\n this.material.scatterIntensity = value;\r\n }\r\n /** Share of scattering taken as local rather than global. */\r\n get scatterLocal() {\r\n return this.material.scatterLocal;\r\n }\r\n set scatterLocal(value: number) {\r\n this.material.scatterLocal = value;\r\n }\r\n /** How far scattered light wraps around the fibre. */\r\n get scatterWrap() {\r\n return this.material.scatterWrap;\r\n }\r\n set scatterWrap(value: number) {\r\n this.material.scatterWrap = value;\r\n }\r\n /** Longitudinal shift of the Marschner lobes. */\r\n get marschnerShift() {\r\n return this.material.marschnerShift;\r\n }\r\n set marschnerShift(value: number) {\r\n this.material.marschnerShift = value;\r\n }\r\n /** Longitudinal roughness of the Marschner lobes. */\r\n get marschnerRoughness() {\r\n return this.material.marschnerRoughness;\r\n }\r\n set marschnerRoughness(value: number) {\r\n this.material.marschnerRoughness = value;\r\n }\r\n /** Index of refraction of the fibre. */\r\n get marschnerIOR() {\r\n return this.material.marschnerIOR;\r\n }\r\n set marschnerIOR(value: number) {\r\n this.material.marschnerIOR = value;\r\n }\r\n /**\r\n * How strongly pigmented the fibre is.\r\n *\r\n * @remarks\r\n * A multiplier on the distance transmitted light travels before leaving, so\r\n * raising it deepens the colour of the TT and TRT paths without touching the\r\n * white surface reflection.\r\n */\r\n get marschnerAbsorption() {\r\n return this.material.marschnerAbsorption;\r\n }\r\n set marschnerAbsorption(value: number) {\r\n this.material.marschnerAbsorption = value;\r\n }\r\n /** Relative weight of the R, TT and TRT lobes. */\r\n get marschnerLobes(): Immutable<Vector3> {\r\n return this.material.marschnerLobes;\r\n }\r\n set marschnerLobes(value: Immutable<Vector3>) {\r\n this.material.marschnerLobes = value;\r\n }\r\n\r\n //\r\n // Strand expansion\r\n //\r\n\r\n /**\r\n * Ribbon segments generated per strand.\r\n *\r\n * @remarks\r\n * The main quality dial, and independent of how many control points a strand\r\n * has: the shader resamples the curve, so the same 30-point strand can be\r\n * drawn with 12 segments close up and 3 far away.\r\n */\r\n get segmentsPerStrand() {\r\n return this.material.segmentsPerStrand;\r\n }\r\n set segmentsPerStrand(val: number) {\r\n if (val !== this.material.segmentsPerStrand) {\r\n this.material.segmentsPerStrand = val;\r\n this._syncDrawRange();\r\n }\r\n }\r\n /** Multiplier on the width stored in the strand data. */\r\n get strandWidthScale() {\r\n return this.material.strandWidthScale;\r\n }\r\n set strandWidthScale(value: number) {\r\n this.material.strandWidthScale = value;\r\n }\r\n /** Lower bound on ribbon width, in world units. */\r\n get minStrandWidth() {\r\n return this.material.minStrandWidth;\r\n }\r\n set minStrandWidth(value: number) {\r\n this.material.minStrandWidth = value;\r\n }\r\n /**\r\n * Lower bound on ribbon width, in pixels.\r\n *\r\n * @remarks\r\n * Real hair is thinner than a pixel and rasterises to a flickering dotted\r\n * line. Widening it to about a pixel and paying the extra coverage back in\r\n * alpha keeps distant hair fading rather than breaking up.\r\n */\r\n get minPixelWidth() {\r\n return this.material.minPixelWidth;\r\n }\r\n set minPixelWidth(value: number) {\r\n this.material.minPixelWidth = value;\r\n }\r\n /**\r\n * How far the shading normal bends across the ribbon, in [0, 1].\r\n *\r\n * @remarks\r\n * At 0 a strand shades as a flat tape, at 1 as a cylinder.\r\n */\r\n get strandRoundness() {\r\n return this.material.strandRoundness;\r\n }\r\n set strandRoundness(value: number) {\r\n this.material.strandRoundness = value;\r\n }\r\n /**\r\n * Strength of the root-depth ambient occlusion, in [0, 1].\r\n *\r\n * @remarks\r\n * Environment irradiance is not attenuated by anything - a shadow map answers\r\n * for one light, not for the sky - so without this every strand of a groom\r\n * receives the full sky and a dense hairstyle is lit as brightly in its\r\n * interior as on its surface, which reads as a flat silhouette with no volume.\r\n * Most obvious on pale hair, where there is little pigment to darken the\r\n * interior instead. Set to 0 to disable.\r\n *\r\n * Ambient light only: direct light is already attenuated by the shadow map,\r\n * and under DOM by a graded transmittance, so it is left alone.\r\n */\r\n get rootOcclusion() {\r\n return this.material.rootOcclusion;\r\n }\r\n set rootOcclusion(value: number) {\r\n this.material.rootOcclusion = value;\r\n }\r\n /**\r\n * How far along a strand the root occlusion reaches, in (0, 1].\r\n *\r\n * @remarks\r\n * Short for a groom whose strands leave the scalp quickly, longer for one\r\n * where they lie against each other most of their length.\r\n */\r\n get rootOcclusionRange() {\r\n return this.material.rootOcclusionRange;\r\n }\r\n set rootOcclusionRange(value: number) {\r\n this.material.rootOcclusionRange = value;\r\n }\r\n /** Drop strands with distance, redistributing their coverage to the survivors. */\r\n get strandLOD() {\r\n return this.material.strandLOD;\r\n }\r\n set strandLOD(value: boolean) {\r\n this.material.strandLOD = value;\r\n }\r\n /** Floor on the fraction of strands distance decimation keeps. */\r\n get minStrandLODRatio() {\r\n return this.material.minStrandLODRatio;\r\n }\r\n set minStrandLODRatio(value: number) {\r\n this.material.minStrandLODRatio = value;\r\n }\r\n\r\n //\r\n // Transparency and shadows\r\n //\r\n\r\n /**\r\n * How the strands blend with what is behind them.\r\n *\r\n * @remarks\r\n * The pixel-width floor pays its widening back in alpha, which only means\r\n * anything once blending is on; left at `none` every strand covers its pixel\r\n * fully and a dense groom reads as a solid shell.\r\n */\r\n get blendMode(): BlendMode {\r\n return this.material.blendMode;\r\n }\r\n set blendMode(value: BlendMode) {\r\n this.material.blendMode = value;\r\n }\r\n /** Alpha below which a fragment is discarded. */\r\n get alphaCutoff() {\r\n return this.material.alphaCutoff;\r\n }\r\n set alphaCutoff(value: number) {\r\n this.material.alphaCutoff = value;\r\n }\r\n /** Dither the alpha test, so a temporal filter resolves the soft edge. */\r\n get alphaDither() {\r\n return this.material.alphaDither;\r\n }\r\n set alphaDither(value: boolean) {\r\n this.material.alphaDither = value;\r\n }\r\n /**\r\n * Whether a blended groom contributes motion vectors.\r\n *\r\n * @remarks\r\n * Only meaningful with {@link HairNode.blendMode} set to something other than\r\n * `none`. Blended geometry is otherwise absent from the prepass, so a groom\r\n * carries the velocity of whatever is behind it and a temporal filter smears\r\n * it as it swings - the same ghosting the dithered path avoids by being\r\n * opaque. On by default, because a groom is dense enough to own its pixels\r\n * and there is no reason to want the ghosting.\r\n */\r\n get transparentMotionVector() {\r\n return this.material.transparentMotionVector;\r\n }\r\n set transparentMotionVector(value: boolean) {\r\n this.material.transparentMotionVector = value;\r\n }\r\n /** Whether the groom is drawn into shadow maps. */\r\n get castShadow() {\r\n return this._castShadow;\r\n }\r\n set castShadow(val: boolean) {\r\n this._castShadow = !!val;\r\n }\r\n /** Let the shadow caster pass respect strand alpha, for softer self-shadowing. */\r\n get transparentShadowCaster() {\r\n return this.material.transparentShadowCaster;\r\n }\r\n set transparentShadowCaster(value: boolean) {\r\n this.material.transparentShadowCaster = value;\r\n }\r\n /** Alpha below which a fragment casts no shadow. */\r\n get shadowAlphaCutoff() {\r\n return this.material.shadowAlphaCutoff;\r\n }\r\n set shadowAlphaCutoff(value: number) {\r\n this.material.shadowAlphaCutoff = value;\r\n }\r\n\r\n //\r\n // Simulation\r\n //\r\n\r\n /**\r\n * Whether strand dynamics run on this groom.\r\n *\r\n * @remarks\r\n * WebGPU only. Switching it on with no strands loaded is allowed - the\r\n * simulation is built when the strands arrive.\r\n */\r\n get simulationEnabled() {\r\n return this._simulationEnabled;\r\n }\r\n set simulationEnabled(val: boolean) {\r\n const enabled = !!val;\r\n if (enabled === this._simulationEnabled) {\r\n return;\r\n }\r\n this._simulationEnabled = enabled;\r\n if (enabled) {\r\n this._createSimulation();\r\n } else {\r\n this._disposeSimulation();\r\n // Without this the groom keeps whatever pose it was last simulated into,\r\n // which reads as a bug rather than as hair that stopped moving.\r\n if (this._source) {\r\n this._strands.get()?.dispose();\r\n this._strands.set(new HairStrandData(this._source));\r\n this.material.strands = this._strands.get();\r\n }\r\n // Back to the tight rest bounds.\r\n this.invalidateBoundingVolume();\r\n }\r\n }\r\n /**\r\n * The running strand dynamics, or null when they are off or unsupported.\r\n *\r\n * @remarks\r\n * Exposed so a caller can reach past the node's own dials - to set colliders,\r\n * for one, which have no serialised form here.\r\n */\r\n get simulation(): Nullable<GPUHairSimulation> {\r\n return this._simulation;\r\n }\r\n /** World-space gravity acting on the strands. */\r\n get gravity(): Immutable<Vector3> {\r\n return this._simulationOptions.gravity!;\r\n }\r\n set gravity(value: Vector3) {\r\n this._simulationOptions.gravity = new Vector3(value);\r\n if (this._simulation) {\r\n this._simulation.gravity = this._simulationOptions.gravity;\r\n }\r\n }\r\n /**\r\n * How strongly strands keep the shape they were authored with, in [0, 1].\r\n *\r\n * @remarks\r\n * The dial to reach for first. It holds the angle each segment makes with the\r\n * one before it, measured against that segment's current direction rather\r\n * than a fixed pose, so a groom keeps its curl while remaining free to hang\r\n * and swing. It is also what damps a groom: without bending resistance a\r\n * strand carries a disturbance up and down its length for seconds.\r\n */\r\n get localStiffness() {\r\n return this._simulationOptions.localStiffness!;\r\n }\r\n set localStiffness(value: number) {\r\n this._simulationOptions.localStiffness = value;\r\n if (this._simulation) {\r\n this._simulation.localStiffness = value;\r\n }\r\n }\r\n /** Local shape constraint passes per substep. */\r\n get localIterations() {\r\n return this._simulationOptions.localIterations!;\r\n }\r\n set localIterations(value: number) {\r\n this._simulationOptions.localIterations = value;\r\n if (this._simulation) {\r\n this._simulation.localIterations = value;\r\n }\r\n }\r\n /**\r\n * How strongly strands are pulled back to their authored pose, in [0, 1].\r\n *\r\n * @remarks\r\n * Off by default. It anchors a point to a fixed place rather than to its\r\n * neighbours, which is a spring, and a groom held by springs reads as one.\r\n * {@link HairNode.localStiffness} is what holds styling without that; this is\r\n * for grooms that genuinely should not move far from how they were authored,\r\n * and wants {@link HairNode.globalRange} set as well.\r\n */\r\n get globalStiffness() {\r\n return this._simulationOptions.globalStiffness!;\r\n }\r\n set globalStiffness(value: number) {\r\n this._simulationOptions.globalStiffness = value;\r\n if (this._simulation) {\r\n this._simulation.globalStiffness = value;\r\n }\r\n }\r\n /** Fraction of a strand, from the root, the pose pull acts on, in [0, 1]. */\r\n get globalRange() {\r\n return this._simulationOptions.globalRange!;\r\n }\r\n set globalRange(value: number) {\r\n this._simulationOptions.globalRange = value;\r\n if (this._simulation) {\r\n this._simulation.globalRange = value;\r\n }\r\n }\r\n /**\r\n * Share of a point's length correction fed back into its parent, in [0, 1].\r\n *\r\n * @remarks\r\n * What keeps the solver from gaining energy as the node moves. Lowering it\r\n * makes a groom livelier and, past a point, makes it whip; 0 removes the\r\n * feedback entirely.\r\n */\r\n get ftlDamping() {\r\n return this._simulationOptions.ftlDamping!;\r\n }\r\n set ftlDamping(value: number) {\r\n this._simulationOptions.ftlDamping = value;\r\n if (this._simulation) {\r\n this._simulation.ftlDamping = value;\r\n }\r\n }\r\n /** Velocity lost each step, in [0, 1]. */\r\n get damping() {\r\n return this._simulationOptions.damping!;\r\n }\r\n set damping(value: number) {\r\n this._simulationOptions.damping = value;\r\n if (this._simulation) {\r\n this._simulation.damping = value;\r\n }\r\n }\r\n /** Tangential motion removed at a contact, in [0, 1]. */\r\n get friction() {\r\n return this._simulationOptions.friction!;\r\n }\r\n set friction(value: number) {\r\n this._simulationOptions.friction = value;\r\n if (this._simulation) {\r\n this._simulation.friction = value;\r\n }\r\n }\r\n /**\r\n * How much of the node's motion strands are carried along by, in [0, 1].\r\n *\r\n * @remarks\r\n * 0 leaves a groom exactly where it was in the world and lets the pinned\r\n * roots drag it along, which is what produces lag; 1 carries it rigidly with\r\n * the node, so it never lags. Lower values give a livelier, floppier groom.\r\n */\r\n get vspCoeff() {\r\n return this._simulationOptions.vspCoeff!;\r\n }\r\n set vspCoeff(value: number) {\r\n this._simulationOptions.vspCoeff = value;\r\n if (this._simulation) {\r\n this._simulation.vspCoeff = value;\r\n }\r\n }\r\n /** Node acceleration above which strands are carried rigidly, in units/s². */\r\n get vspAccelThreshold() {\r\n return this._simulationOptions.vspAccelThreshold!;\r\n }\r\n set vspAccelThreshold(value: number) {\r\n this._simulationOptions.vspAccelThreshold = value;\r\n if (this._simulation) {\r\n this._simulation.vspAccelThreshold = value;\r\n }\r\n }\r\n /**\r\n * Per-substep ceiling on how far a point may travel, in segment lengths.\r\n *\r\n * @remarks\r\n * A backstop against a single bad substep, not a dial to shape motion with:\r\n * at the default it never engages during ordinary movement.\r\n */\r\n get maxSpeedFactor() {\r\n return this._simulationOptions.maxSpeedFactor!;\r\n }\r\n set maxSpeedFactor(value: number) {\r\n this._simulationOptions.maxSpeedFactor = value;\r\n if (this._simulation) {\r\n this._simulation.maxSpeedFactor = value;\r\n }\r\n }\r\n /** Integration substeps per fixed step. */\r\n get substeps() {\r\n return this._simulationOptions.substeps!;\r\n }\r\n set substeps(value: number) {\r\n this._simulationOptions.substeps = value;\r\n if (this._simulation) {\r\n this._simulation.substeps = value;\r\n }\r\n }\r\n\r\n //\r\n // Scene node and drawable\r\n //\r\n\r\n /**\r\n * {@inheritDoc SceneNode.update}\r\n *\r\n * @remarks\r\n * Drives the strand solver. The node re-queues itself every frame, because a\r\n * simulation only stands still when it is switched off.\r\n */\r\n update(frameId: number, elapsedInSeconds: number, deltaInSeconds: number) {\r\n super.update(frameId, elapsedInSeconds, deltaInSeconds);\r\n const simulation = this._simulation;\r\n if (simulation?.enabled) {\r\n simulation.update(deltaInSeconds, this.worldMatrix);\r\n this.scene?.queueUpdateNode(this);\r\n }\r\n }\r\n /**\r\n * {@inheritDoc SceneNode.computeBoundingVolume}\r\n *\r\n * @remarks\r\n * Derived on the CPU from the control points, because the ribbons are built in\r\n * the vertex shader and the engine never sees them. The box is in local space;\r\n * the base class applies the world matrix.\r\n *\r\n * While a simulation runs, the strands swing out of their authored pose, so\r\n * the rest bounds are padded by the longest strand: the solver keeps every\r\n * point within its strand's length of the pinned root, which bounds any pose\r\n * it can produce.\r\n */\r\n computeBoundingVolume(): Nullable<BoundingBox> {\r\n if (!this._bounds) {\r\n return null;\r\n }\r\n const box = new BoundingBox(this._bounds);\r\n if (this._simulation) {\r\n const pad = this._maxStrandLength;\r\n box.minPoint.setXYZ(box.minPoint.x - pad, box.minPoint.y - pad, box.minPoint.z - pad);\r\n box.maxPoint.setXYZ(box.maxPoint.x + pad, box.maxPoint.y + pad, box.maxPoint.z + pad);\r\n }\r\n return box;\r\n }\r\n /** {@inheritDoc SceneNode.isHair} */\r\n isHair(): this is HairNode {\r\n return true;\r\n }\r\n /** {@inheritDoc Drawable.getPickTarget} */\r\n getPickTarget() {\r\n return this._pickTarget;\r\n }\r\n /** {@inheritDoc Drawable.getMaterial} */\r\n getMaterial() {\r\n return this._material.get();\r\n }\r\n /** {@inheritDoc Drawable.getPrimitive} */\r\n getPrimitive() {\r\n return this._primitive.get();\r\n }\r\n /** {@inheritDoc Drawable.getMorphData} */\r\n getMorphData() {\r\n return null;\r\n }\r\n /** {@inheritDoc Drawable.getMorphInfo} */\r\n getMorphInfo() {\r\n return null;\r\n }\r\n /** {@inheritDoc Drawable.getSkinInfluenceData} */\r\n getSkinInfluenceData() {\r\n return null;\r\n }\r\n /** {@inheritDoc Drawable.getQueueType} */\r\n getQueueType() {\r\n return this._material.get()?.getQueueType() ?? QUEUE_OPAQUE;\r\n }\r\n /** {@inheritDoc Drawable.isUnlit} */\r\n isUnlit() {\r\n return !this._material.get()?.supportLighting();\r\n }\r\n /** {@inheritDoc Drawable.needSceneColor} */\r\n needSceneColor() {\r\n return this._material.get()?.needSceneColor() ?? false;\r\n }\r\n /** {@inheritDoc Drawable.needSceneDepth} */\r\n needSceneDepth() {\r\n return this._material.get()?.needSceneDepth() ?? false;\r\n }\r\n /** {@inheritDoc Drawable.draw} */\r\n draw(ctx: DrawContext, renderQueue: Nullable<RenderQueue>) {\r\n const material = this._material.get();\r\n const primitive = this._primitive.get();\r\n // An empty groom is a legitimate state - a node exists before its asset has\r\n // finished loading - and the draw range is zero until strands arrive.\r\n if (material && primitive && material.vertexCount > 0) {\r\n this.bind(ctx, renderQueue);\r\n material.draw(primitive, ctx);\r\n }\r\n }\r\n protected onDispose() {\r\n super.onDispose();\r\n this._disposeSimulation();\r\n this._primitive.dispose();\r\n this._strands.dispose();\r\n this._material.dispose();\r\n this._parsedAsset = null;\r\n this._source = null;\r\n this._bounds = null;\r\n }\r\n\r\n //\r\n // Internals\r\n //\r\n\r\n /**\r\n * The material, which is an implementation detail of this node.\r\n * @internal\r\n */\r\n private get material() {\r\n return this._material.get()!;\r\n }\r\n /**\r\n * Re-decimates the opened asset into the strands actually drawn.\r\n * @internal\r\n */\r\n private _applyAssetDecimation() {\r\n const parsed = this._parsedAsset;\r\n if (!parsed) {\r\n return;\r\n }\r\n // A `.zhair` holds one set per source curve object, and an XGen export\r\n // routinely carries several spline descriptions for a single hairstyle;\r\n // merging lets the whole head draw in one call.\r\n this.setStrands(\r\n mergeHairStrandSources(\r\n loadZHairStrandSources(parsed, {\r\n strandStride: this._strandStride,\r\n maxStrands: this._maxStrands\r\n })\r\n )\r\n );\r\n }\r\n /**\r\n * Starts strand dynamics over the current strands.\r\n * @internal\r\n */\r\n private _createSimulation() {\r\n this._disposeSimulation();\r\n const data = this._strands.get();\r\n if (!data || !this._source) {\r\n return;\r\n }\r\n if (!isHairSimulationSupported()) {\r\n warnUnsupported(\r\n 'Hair strand dynamics (Hair.simulationEnabled)',\r\n 'WebGPU',\r\n 'strands stay in their rest pose'\r\n );\r\n return;\r\n }\r\n const simulation = new GPUHairSimulation(data, this._source, this._simulationOptions);\r\n if (simulation.enabled) {\r\n this._simulation = simulation;\r\n // Motion vectors have to follow the control points once something is\r\n // moving them, or a groom swinging under a still head reports no motion\r\n // and a temporal filter smears it.\r\n this.material.prevPoints = simulation.framePointBuffer;\r\n // The bounds widen while a simulation runs - see computeBoundingVolume.\r\n this.invalidateBoundingVolume();\r\n this.scene?.queueUpdateNode(this);\r\n } else {\r\n console.warn(simulation.disabledReason ?? 'GPU hair simulation could not start');\r\n simulation.dispose();\r\n }\r\n }\r\n /** @internal */\r\n private _disposeSimulation() {\r\n this.material.prevPoints = null;\r\n this._simulation?.dispose();\r\n this._simulation = null;\r\n }\r\n /**\r\n * Builds the primitive the draw call is issued against.\r\n *\r\n * @remarks\r\n * No vertex attribute is ever read - the vertex shader derives everything from\r\n * the vertex index and the storage buffers - but a draw still needs a vertex\r\n * layout to exist, and one float is the smallest buffer that establishes one.\r\n * @internal\r\n */\r\n private _ensurePrimitive() {\r\n if (this._primitive.get()) {\r\n return;\r\n }\r\n const primitive = new Primitive();\r\n primitive.createAndSetVertexBuffer('position_f32x3', new Float32Array(3));\r\n primitive.primitiveType = 'triangle-list';\r\n this._primitive.set(primitive);\r\n }\r\n /**\r\n * Points the draw range at however many vertices the material now needs.\r\n *\r\n * @remarks\r\n * Drawing is non-indexed, six vertices per ribbon segment, so the count moves\r\n * with both the strand count and {@link HairNode.segmentsPerStrand}.\r\n * @internal\r\n */\r\n private _syncDrawRange() {\r\n const primitive = this._primitive.get();\r\n if (primitive) {\r\n primitive.indexCount = this.material.vertexCount;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Local-space bounds of a strand source, padded by the widest strand, along\r\n * with the length of the longest strand.\r\n *\r\n * @remarks\r\n * The ribbon is built around the control point, so half a width would suffice;\r\n * a full width is used because {@link HairNode.minPixelWidth} widens sub-pixel\r\n * strands beyond their authored size and the box has to survive that.\r\n *\r\n * The strand length is measured here because the same pass already touches\r\n * every point; it pads the bounds while a simulation runs.\r\n * @internal\r\n */\r\nfunction computeStrandExtents(source: HairStrandSource): {\r\n bounds: BoundingBox;\r\n maxStrandLength: number;\r\n} {\r\n const scale = source.scale ?? 1;\r\n const positions = source.positions;\r\n const box = new BoundingBox();\r\n box.beginExtend();\r\n let maxStrandLength = 0;\r\n let first = 0;\r\n for (let s = 0; s < source.pointCounts.length; s++) {\r\n const count = source.pointCounts[s];\r\n let length = 0;\r\n for (let i = 0; i < count; i++) {\r\n const p = (first + i) * 3;\r\n const x = positions[p] * scale;\r\n const y = positions[p + 1] * scale;\r\n const z = positions[p + 2] * scale;\r\n box.extend3(x, y, z);\r\n if (i > 0) {\r\n length += Math.hypot(\r\n x - positions[p - 3] * scale,\r\n y - positions[p - 2] * scale,\r\n z - positions[p - 1] * scale\r\n );\r\n }\r\n }\r\n if (length > maxStrandLength) {\r\n maxStrandLength = length;\r\n }\r\n first += count;\r\n }\r\n let widest = source.defaultWidth ?? 0.0001;\r\n if (source.widths) {\r\n for (let i = 0; i < source.widths.length; i++) {\r\n if (source.widths[i] > widest) {\r\n widest = source.widths[i];\r\n }\r\n }\r\n }\r\n const pad = widest * scale * (source.widthScale ?? 1);\r\n box.minPoint.setXYZ(box.minPoint.x - pad, box.minPoint.y - pad, box.minPoint.z - pad);\r\n box.maxPoint.setXYZ(box.maxPoint.x + pad, box.maxPoint.y + pad, box.maxPoint.z + pad);\r\n return { bounds: box, maxStrandLength };\r\n}\r\n"],"names":["HairNode","applyMixins","GraphNode","mixinDrawable","scene","_pickTarget","node","_material","DRef","HairStrandMaterial","_primitive","_strands","_bounds","_maxStrandLength","_castShadow","_source","_hairAsset","_parsedAsset","_strandStride","_maxStrands","_simulation","_simulationEnabled","_simulationOptions","gravity","Vector3","damping","globalStiffness","globalRange","localStiffness","localIterations","ftlDamping","vspCoeff","vspAccelThreshold","substeps","friction","maxSpeedFactor","hairAsset","setHairAsset","path","assetId","trim","setStrands","parsed","data","getEngine","resourceManager","assetManager","fetchBinaryData","parseZHair","err","console","error","_applyAssetDecimation","strandStride","val","v","maxStrands","strands","get","strandCount","source","_disposeSimulation","dispose","material","invalidateBoundingVolume","HairStrandData","set","extents","computeStrandExtents","bounds","maxStrandLength","_ensurePrimitive","_syncDrawRange","_createSimulation","albedoColor","value","shadingModel","specular1Color","specular1Power","specular1Shift","specular2Color","specular2Power","specular2Shift","diffuseWrap","transmissionColor","transmissionIntensity","transmissionPower","scatterColor","scatterIntensity","scatterLocal","scatterWrap","marschnerShift","marschnerRoughness","marschnerIOR","marschnerAbsorption","marschnerLobes","segmentsPerStrand","strandWidthScale","minStrandWidth","minPixelWidth","strandRoundness","rootOcclusion","rootOcclusionRange","strandLOD","minStrandLODRatio","blendMode","alphaCutoff","alphaDither","transparentMotionVector","castShadow","transparentShadowCaster","shadowAlphaCutoff","simulationEnabled","enabled","simulation","update","frameId","elapsedInSeconds","deltaInSeconds","worldMatrix","queueUpdateNode","computeBoundingVolume","box","BoundingBox","pad","minPoint","setXYZ","x","y","z","maxPoint","isHair","getPickTarget","getMaterial","getPrimitive","getMorphData","getMorphInfo","getSkinInfluenceData","getQueueType","QUEUE_OPAQUE","isUnlit","supportLighting","needSceneColor","needSceneDepth","draw","ctx","renderQueue","primitive","vertexCount","bind","onDispose","mergeHairStrandSources","loadZHairStrandSources","isHairSimulationSupported","warnUnsupported","GPUHairSimulation","prevPoints","framePointBuffer","warn","disabledReason","Primitive","createAndSetVertexBuffer","Float32Array","primitiveType","indexCount","scale","positions","beginExtend","first","s","pointCounts","length","count","i","p","extend3","Math","hypot","widest","defaultWidth","widths","widthScale"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Vector4, DWeakRef, Vector3 } from '@zephyr3d/base';
1
+ import { Matrix4x4, Vector4, DWeakRef, Vector3 } from '@zephyr3d/base';
2
2
  import { GraphNode } from './graph_node.js';
3
3
  import { BoundingBox } from '../utility/bounding_volume.js';
4
4
  import { ShadowMapper } from '../shadow/shadowmapper.js';
@@ -157,6 +157,10 @@ export abstract class AmbientLight extends BaseLight {
157
157
  */ class PunctualLight extends BaseLight {
158
158
  /** @internal */ _color;
159
159
  /** @internal */ _castShadow;
160
+ /** @internal */ _transmission;
161
+ /** @internal */ _contactShadowLength;
162
+ /** @internal */ _contactShadowLengthInWS;
163
+ /** @internal */ _contactShadowCastingIntensity;
160
164
  /** @internal */ _shadowMapper;
161
165
  /**
162
166
  * Creates an instance of punctual light
@@ -166,6 +170,25 @@ export abstract class AmbientLight extends BaseLight {
166
170
  super(scene, type);
167
171
  this.ensurePunctualState();
168
172
  }
173
+ /**
174
+ * {@inheritDoc SceneNode.computeWorldBoundingVolume}
175
+ *
176
+ * @remarks
177
+ * The local bounds are built from the range in world units, the same value
178
+ * the shader uses, so they are placed with the world rotation and position
179
+ * only. Scaling a light changes neither its reach nor its cone.
180
+ *
181
+ * @override
182
+ */ computeWorldBoundingVolume(localBV) {
183
+ if (!localBV) {
184
+ return null;
185
+ }
186
+ const m = new Matrix4x4(this.worldMatrix);
187
+ for(let i = 0; i < 3; i++){
188
+ m.setRow(i, m.getRow(i).inplaceNormalize());
189
+ }
190
+ return localBV.transform(m);
191
+ }
169
192
  /** @internal */ ensurePunctualState() {
170
193
  if (!this._color) {
171
194
  this._color = Vector4.one();
@@ -173,6 +196,18 @@ export abstract class AmbientLight extends BaseLight {
173
196
  if (this._castShadow == null) {
174
197
  this._castShadow = false;
175
198
  }
199
+ if (this._transmission == null) {
200
+ this._transmission = false;
201
+ }
202
+ if (this._contactShadowLength == null) {
203
+ this._contactShadowLength = 0;
204
+ }
205
+ if (this._contactShadowLengthInWS == null) {
206
+ this._contactShadowLengthInWS = false;
207
+ }
208
+ if (this._contactShadowCastingIntensity == null) {
209
+ this._contactShadowCastingIntensity = 1;
210
+ }
176
211
  if (!this._shadowMapper) {
177
212
  this._shadowMapper = new ShadowMapper(this);
178
213
  }
@@ -220,6 +255,64 @@ export abstract class AmbientLight extends BaseLight {
220
255
  return this._shadowMapper;
221
256
  }
222
257
  /**
258
+ * Whether this light contributes back-lit subsurface transmission. Off by default.
259
+ *
260
+ * @public
261
+ */ get transmission() {
262
+ this.ensurePunctualState();
263
+ return this._transmission;
264
+ }
265
+ set transmission(b) {
266
+ this.setTransmission(b);
267
+ }
268
+ /**
269
+ * Sets whether this light contributes back-lit subsurface transmission.
270
+ * @param b - true to enable transmission for this light
271
+ * @returns self
272
+ */ setTransmission(b) {
273
+ this.ensurePunctualState();
274
+ this._transmission = !!b;
275
+ return this;
276
+ }
277
+ /**
278
+ * Length of the screen-space ray traced towards the light for sharp contact
279
+ * shadows. Zero disables them.
280
+ *
281
+ * @public
282
+ */ get contactShadowLength() {
283
+ this.ensurePunctualState();
284
+ return this._contactShadowLength;
285
+ }
286
+ set contactShadowLength(val) {
287
+ this.ensurePunctualState();
288
+ this._contactShadowLength = Math.max(0, Number(val) || 0);
289
+ }
290
+ /**
291
+ * Whether {@link PunctualLight.contactShadowLength} is in world units rather
292
+ * than a fraction of the screen height.
293
+ *
294
+ * @public
295
+ */ get contactShadowLengthInWS() {
296
+ this.ensurePunctualState();
297
+ return this._contactShadowLengthInWS;
298
+ }
299
+ set contactShadowLengthInWS(val) {
300
+ this.ensurePunctualState();
301
+ this._contactShadowLengthInWS = !!val;
302
+ }
303
+ /**
304
+ * Darkness of contact shadows, 0 = none, 1 = fully shadowed.
305
+ *
306
+ * @public
307
+ */ get contactShadowCastingIntensity() {
308
+ this.ensurePunctualState();
309
+ return this._contactShadowCastingIntensity;
310
+ }
311
+ set contactShadowCastingIntensity(val) {
312
+ this.ensurePunctualState();
313
+ this._contactShadowCastingIntensity = Math.min(1, Math.max(0, Number(val) || 0));
314
+ }
315
+ /**
223
316
  * {@inheritDoc BaseLight.isPunctualLight}
224
317
  * @override
225
318
  */ isPunctualLight() {
@@ -363,9 +456,8 @@ export abstract class AmbientLight extends BaseLight {
363
456
  if (intensity !== this._luminousIntensity) {
364
457
  this._luminousIntensity = intensity;
365
458
  this.invalidateUniforms();
366
- if (this._range <= 0) {
367
- this.invalidateBoundingVolume();
368
- }
459
+ // computeUniforms caps a set range by the derived one too, so the bounds follow either way.
460
+ this.invalidateBoundingVolume();
369
461
  }
370
462
  }
371
463
  /**
@@ -393,6 +485,18 @@ export abstract class AmbientLight extends BaseLight {
393
485
  return this;
394
486
  }
395
487
  /**
488
+ * {@inheritDoc BaseLight.setIntensity}
489
+ * @override
490
+ */ setIntensity(val) {
491
+ const changed = val !== this._intensity;
492
+ super.setIntensity(val);
493
+ // The range is capped by the one the intensity gives, and with it the culling bounds.
494
+ if (changed) {
495
+ this.invalidateBoundingVolume();
496
+ }
497
+ return this;
498
+ }
499
+ /**
396
500
  * Sets the diffuse scale of the light
397
501
  * @param val - The value to set
398
502
  * @returns self
@@ -401,6 +505,7 @@ export abstract class AmbientLight extends BaseLight {
401
505
  if (this._diffuseScale !== val) {
402
506
  this._diffuseScale = val;
403
507
  this.invalidateUniforms();
508
+ this.invalidateBoundingVolume();
404
509
  }
405
510
  return this;
406
511
  }
@@ -413,6 +518,7 @@ export abstract class AmbientLight extends BaseLight {
413
518
  if (this._specularScale !== val) {
414
519
  this._specularScale = val;
415
520
  this.invalidateUniforms();
521
+ this.invalidateBoundingVolume();
416
522
  }
417
523
  return this;
418
524
  }
@@ -449,7 +555,8 @@ export abstract class AmbientLight extends BaseLight {
449
555
  // diffuse/specular scales still multiply the shaded result, so the auto-derived influence
450
556
  // radius has to account for them. SpotLight has no equivalent scales, hence no such factor.
451
557
  const physicalScale = Math.max(this._diffuseScale, this._specularScale);
452
- const range = this.range <= 0 ? physical ? Math.sqrt(Math.max(0, this.luminousIntensity * physicalScale) / PHYSICAL_LIGHT_CUTOFF_LUX) / metersPerUnit : 32 * Math.sqrt(Math.max(0.0001, this.intensity * physicalScale)) : this.range;
558
+ const calculatedRange = physical ? Math.sqrt(Math.max(0, this.luminousIntensity * physicalScale) / PHYSICAL_LIGHT_CUTOFF_LUX) / metersPerUnit : 32 * Math.sqrt(Math.max(0.0001, this.intensity * physicalScale));
559
+ const range = this.range <= 0 ? calculatedRange : Math.min(this.range, calculatedRange);
453
560
  const resolvedIntensity = physical ? this.luminousIntensity / (metersPerUnit * metersPerUnit) : this.intensity;
454
561
  this._positionRange = new Vector4(a.x, a.y, a.z, range);
455
562
  this._directionCutoff = new Vector4(b.x, b.y, b.z, -1);
@@ -498,6 +605,18 @@ export abstract class AmbientLight extends BaseLight {
498
605
  }
499
606
  return this;
500
607
  }
608
+ /**
609
+ * {@inheritDoc BaseLight.setIntensity}
610
+ * @override
611
+ */ setIntensity(val) {
612
+ const changed = val !== this._intensity;
613
+ super.setIntensity(val);
614
+ // The automatic range follows the intensity, and with it the culling bounds.
615
+ if (changed && this._range <= 0) {
616
+ this.invalidateBoundingVolume();
617
+ }
618
+ return this;
619
+ }
501
620
  /** The cutoff of the light */ get cutoff() {
502
621
  return this._cutoff;
503
622
  }
@@ -595,12 +714,14 @@ export abstract class AmbientLight extends BaseLight {
595
714
  // In legacy mode `_cutoff` already stores the cosine of the cone half-angle (see the
596
715
  // constructor default and computeUniforms), so it must be used as-is. Physical mode keeps the
597
716
  // half-angle in radians instead, hence the conversion there.
598
- // The lower clamp keeps the radius finite for half-angles at or beyond 90 degrees.
599
- const cosCutoff = Math.min(1, Math.max(0.0001, this.scene?.lightingMode === 'physical' ? Math.cos(this._outerConeAngle) : this._cutoff));
717
+ // Both setters keep the half-angle within 90 degrees, so the cosine is never negative.
718
+ const cosCutoff = Math.min(1, Math.max(0, this.scene?.lightingMode === 'physical' ? Math.cos(this._outerConeAngle) : this._cutoff));
600
719
  const range = this.positionAndRange.w;
601
- const r = range / cosCutoff * Math.sqrt(1 - cosCutoff * cosCutoff);
602
- bbox.minPoint = new Vector3(-r, -r, 0);
603
- bbox.maxPoint = new Vector3(r, r, range);
720
+ // The lit region is the cone intersected with the range sphere. The light shines down its
721
+ // local -Z (computeUniforms negates the Z row), so that is the side the bounds extend into.
722
+ const r = range * Math.sqrt(1 - cosCutoff * cosCutoff);
723
+ bbox.minPoint = new Vector3(-r, -r, -range);
724
+ bbox.maxPoint = new Vector3(r, r, 0);
604
725
  return bbox;
605
726
  }
606
727
  /** @internal */ computeUniforms() {
@@ -622,6 +743,13 @@ export abstract class AmbientLight extends BaseLight {
622
743
  }
623
744
  /**
624
745
  * Rectangular area light
746
+ *
747
+ * @remarks
748
+ * Shadows render a cube map from the light's centre. Only the `pcss` shadow
749
+ * mode softens them by the light's size - the penumbra follows the panel's
750
+ * area and the blocker and receiver distances, hardening towards contact. The
751
+ * other modes filter with a fixed kernel that ignores the light's size.
752
+ *
625
753
  * @public
626
754
  */ class RectLight extends PunctualLight {
627
755
  /** @internal */ _range;
@@ -639,7 +767,15 @@ export abstract class AmbientLight extends BaseLight {
639
767
  this._luminance = 100;
640
768
  this.invalidateBoundingVolume();
641
769
  }
642
- /** The range of the light */ get range() {
770
+ /**
771
+ * The range of the light, in world units.
772
+ *
773
+ * @remarks
774
+ * 0 derives it from the light's output, as for point lights: the distance at
775
+ * which the rect, seen as a point of intensity `luminance * area`, falls to
776
+ * the same illuminance cutoff. It is never shorter than the rect's diagonal,
777
+ * so a large dim panel still lights its own surroundings.
778
+ */ get range() {
643
779
  return this._range;
644
780
  }
645
781
  set range(val) {
@@ -691,6 +827,9 @@ export abstract class AmbientLight extends BaseLight {
691
827
  if (luminance !== this._luminance) {
692
828
  this._luminance = luminance;
693
829
  this.invalidateUniforms();
830
+ if (this._range <= 0) {
831
+ this.invalidateBoundingVolume();
832
+ }
694
833
  }
695
834
  }
696
835
  /**
@@ -723,16 +862,43 @@ export abstract class AmbientLight extends BaseLight {
723
862
  return this;
724
863
  }
725
864
  /**
865
+ * {@inheritDoc BaseLight.setIntensity}
866
+ * @override
867
+ */ setIntensity(val) {
868
+ const changed = val !== this._intensity;
869
+ super.setIntensity(val);
870
+ // The automatic range follows the intensity, and with it the culling bounds.
871
+ if (changed && this._range <= 0) {
872
+ this.invalidateBoundingVolume();
873
+ }
874
+ return this;
875
+ }
876
+ /**
726
877
  * {@inheritDoc BaseLight.isRectLight}
727
878
  * @override
728
879
  */ isRectLight() {
729
880
  return true;
730
881
  }
731
882
  /**
883
+ * The range actually applied: {@link range}, or the automatic one when that
884
+ * is 0.
885
+ * @internal
886
+ */ resolveRange() {
887
+ if (this._range > 0) {
888
+ return this._range;
889
+ }
890
+ const physical = this.scene?.lightingMode === 'physical';
891
+ const metersPerUnit = this.scene?.metersPerUnit ?? 1;
892
+ // Far from the rect its irradiance is luminance * area / d^2 - an isotropic
893
+ // point of intensity luminance * area - so the point light's cutoff applies.
894
+ const range = physical ? Math.sqrt(Math.max(0, this._luminance * this._width * this._height * metersPerUnit * metersPerUnit) / PHYSICAL_LIGHT_CUTOFF_LUX) / metersPerUnit : 32 * Math.sqrt(Math.max(0.0001, this.intensity * this._width * this._height));
895
+ return Math.max(range, Math.hypot(this._width, this._height));
896
+ }
897
+ /**
732
898
  * Rect light supports shadow in current pipeline
733
899
  */ /** @internal */ computeBoundingVolume() {
734
900
  const bbox = new BoundingBox();
735
- const r = this._range;
901
+ const r = this.positionAndRange.w;
736
902
  bbox.minPoint = new Vector3(-r, -r, -r);
737
903
  bbox.maxPoint = new Vector3(r, r, r);
738
904
  return bbox;
@@ -741,7 +907,7 @@ export abstract class AmbientLight extends BaseLight {
741
907
  const pos = this.worldMatrix.getRow(3);
742
908
  const axisX = this.worldMatrix.getRow(0).inplaceNormalize().scaleBy(this._width * 0.5);
743
909
  const axisY = this.worldMatrix.getRow(1).inplaceNormalize().scaleBy(this._height * 0.5);
744
- this._positionRange = new Vector4(pos.x, pos.y, pos.z, this.range);
910
+ this._positionRange = new Vector4(pos.x, pos.y, pos.z, this.resolveRange());
745
911
  this._directionCutoff = new Vector4(axisX.x, axisX.y, axisX.z, 0);
746
912
  const physical = this.scene?.lightingMode === 'physical';
747
913
  this._diffuseIntensity = makeColorIntensity(this.color, physical ? this.luminance : this.intensity);