@zephyr3d/scene 0.9.26 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/README.md +60 -27
  2. package/dist/animation/animation_bank.js +634 -0
  3. package/dist/animation/animation_bank.js.map +1 -0
  4. package/dist/animation/animation_bank_gpu.js +256 -0
  5. package/dist/animation/animation_bank_gpu.js.map +1 -0
  6. package/dist/animation/animationset.js +146 -30
  7. package/dist/animation/animationset.js.map +1 -1
  8. package/dist/animation/cloth/gpu_cloth_component.js +406 -0
  9. package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
  10. package/dist/animation/cloth/gpu_cloth_system.js +332 -202
  11. package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
  12. package/dist/animation/gpu_animation_driver.js +289 -0
  13. package/dist/animation/gpu_animation_driver.js.map +1 -0
  14. package/dist/animation/joint_dynamics/collision.js +112 -1
  15. package/dist/animation/joint_dynamics/collision.js.map +1 -1
  16. package/dist/animation/joint_dynamics/controller.js +6 -0
  17. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  18. package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
  19. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  20. package/dist/animation/joint_dynamics/solver.js +50 -12
  21. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  22. package/dist/animation/joint_dynamics/types.js.map +1 -1
  23. package/dist/animation/skeleton.js +300 -56
  24. package/dist/animation/skeleton.js.map +1 -1
  25. package/dist/animation/skin_palette_atlas.js +252 -0
  26. package/dist/animation/skin_palette_atlas.js.map +1 -0
  27. package/dist/animation/spring/multi_chain_spring_system.js +298 -38
  28. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  29. package/dist/animation/spring/spring_chain.js +2 -0
  30. package/dist/animation/spring/spring_chain.js.map +1 -1
  31. package/dist/animation/spring/spring_collider.js +159 -21
  32. package/dist/animation/spring/spring_collider.js.map +1 -1
  33. package/dist/animation/spring/spring_particle.js +1 -0
  34. package/dist/animation/spring/spring_particle.js.map +1 -1
  35. package/dist/animation/spring/spring_solver.js +173 -0
  36. package/dist/animation/spring/spring_solver.js.map +1 -0
  37. package/dist/animation/spring/spring_system.js +249 -24
  38. package/dist/animation/spring/spring_system.js.map +1 -1
  39. package/dist/animation/spring_modifier.js +32 -1
  40. package/dist/animation/spring_modifier.js.map +1 -1
  41. package/dist/app/app.js +26 -0
  42. package/dist/app/app.js.map +1 -1
  43. package/dist/app/engine.js +86 -15
  44. package/dist/app/engine.js.map +1 -1
  45. package/dist/app/gamepad.js +191 -0
  46. package/dist/app/gamepad.js.map +1 -0
  47. package/dist/app/inputactions.js +271 -0
  48. package/dist/app/inputactions.js.map +1 -0
  49. package/dist/app/inputmgr.js +331 -22
  50. package/dist/app/inputmgr.js.map +1 -1
  51. package/dist/app/loading.js +138 -0
  52. package/dist/app/loading.js.map +1 -0
  53. package/dist/app/pipelinecache.js +209 -0
  54. package/dist/app/pipelinecache.js.map +1 -0
  55. package/dist/app/runtimescript.js +25 -0
  56. package/dist/app/runtimescript.js.map +1 -1
  57. package/dist/app/scriptingsystem.js +54 -0
  58. package/dist/app/scriptingsystem.js.map +1 -1
  59. package/dist/app/scriptregistry.js +7 -2
  60. package/dist/app/scriptregistry.js.map +1 -1
  61. package/dist/asset/asset_manifest.js +59 -0
  62. package/dist/asset/asset_manifest.js.map +1 -0
  63. package/dist/asset/asset_preload.js +258 -0
  64. package/dist/asset/asset_preload.js.map +1 -0
  65. package/dist/asset/assetmanager.js +117 -65
  66. package/dist/asset/assetmanager.js.map +1 -1
  67. package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
  68. package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  69. package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  70. package/dist/asset/loaders/ktx2/basis_location.js +17 -0
  71. package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
  72. package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
  73. package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
  74. package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
  75. package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
  76. package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
  77. package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
  78. package/dist/asset/mesh_lod_builder.js +104 -0
  79. package/dist/asset/mesh_lod_builder.js.map +1 -0
  80. package/dist/asset/mesh_settings.js +111 -0
  81. package/dist/asset/mesh_settings.js.map +1 -0
  82. package/dist/asset/meshopt_decoder.js +18 -0
  83. package/dist/asset/meshopt_decoder.js.map +1 -0
  84. package/dist/asset/meshopt_simplifier.js +26 -0
  85. package/dist/asset/meshopt_simplifier.js.map +1 -0
  86. package/dist/asset/model.js +177 -3
  87. package/dist/asset/model.js.map +1 -1
  88. package/dist/asset/texture_settings.js +231 -0
  89. package/dist/asset/texture_settings.js.map +1 -0
  90. package/dist/asset/zmsh_binary.js +131 -0
  91. package/dist/asset/zmsh_binary.js.map +1 -0
  92. package/dist/asset/zsdf_binary.js +111 -0
  93. package/dist/asset/zsdf_binary.js.map +1 -0
  94. package/dist/blitter/bilateralblur.js +1 -2
  95. package/dist/blitter/bilateralblur.js.map +1 -1
  96. package/dist/blitter/gaussianblur.js +1 -6
  97. package/dist/blitter/gaussianblur.js.map +1 -1
  98. package/dist/camera/base.js +31 -10
  99. package/dist/camera/base.js.map +1 -1
  100. package/dist/camera/camera.js +178 -200
  101. package/dist/camera/camera.js.map +1 -1
  102. package/dist/camera/follow.js +342 -0
  103. package/dist/camera/follow.js.map +1 -0
  104. package/dist/camera/fps.js +9 -7
  105. package/dist/camera/fps.js.map +1 -1
  106. package/dist/camera/orbit.js +46 -19
  107. package/dist/camera/orbit.js.map +1 -1
  108. package/dist/index.d.ts +30475 -19865
  109. package/dist/index.js +67 -12
  110. package/dist/index.js.map +1 -1
  111. package/dist/material/blinn.js +3 -0
  112. package/dist/material/blinn.js.map +1 -1
  113. package/dist/material/eye.js +1 -1
  114. package/dist/material/eye.js.map +1 -1
  115. package/dist/material/hair.js +2 -3
  116. package/dist/material/hair.js.map +1 -1
  117. package/dist/material/hairstrand.js +235 -235
  118. package/dist/material/hairstrand.js.map +1 -1
  119. package/dist/material/lambert.js +4 -1
  120. package/dist/material/lambert.js.map +1 -1
  121. package/dist/material/material.js +362 -300
  122. package/dist/material/material.js.map +1 -1
  123. package/dist/material/meshmaterial.js +118 -45
  124. package/dist/material/meshmaterial.js.map +1 -1
  125. package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
  126. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  127. package/dist/material/mixins/lightmodel/lambert.js +1 -1
  128. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  129. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
  130. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  131. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
  132. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  133. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
  134. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  135. package/dist/material/mixins/lit.js +223 -83
  136. package/dist/material/mixins/lit.js.map +1 -1
  137. package/dist/material/mixins/pbr/common.js +31 -206
  138. package/dist/material/mixins/pbr/common.js.map +1 -1
  139. package/dist/material/mtoon.js +1 -1
  140. package/dist/material/mtoon.js.map +1 -1
  141. package/dist/material/pbrmr.js +3 -0
  142. package/dist/material/pbrmr.js.map +1 -1
  143. package/dist/material/pbrsg.js +3 -0
  144. package/dist/material/pbrsg.js.map +1 -1
  145. package/dist/material/sdf_debug.js +78 -0
  146. package/dist/material/sdf_debug.js.map +1 -0
  147. package/dist/material/shader/helper.js +733 -190
  148. package/dist/material/shader/helper.js.map +1 -1
  149. package/dist/material/shader/terrain_blend.js +204 -43
  150. package/dist/material/shader/terrain_blend.js.map +1 -1
  151. package/dist/material/skin.js +331 -297
  152. package/dist/material/skin.js.map +1 -1
  153. package/dist/material/skinprofile.js +1058 -0
  154. package/dist/material/skinprofile.js.map +1 -0
  155. package/dist/material/sss.js +353 -0
  156. package/dist/material/sss.js.map +1 -0
  157. package/dist/material/sss_kernel.js +265 -0
  158. package/dist/material/sss_kernel.js.map +1 -0
  159. package/dist/material/sssprofile.js +879 -0
  160. package/dist/material/sssprofile.js.map +1 -0
  161. package/dist/material/subsurfaceprofile.js +4 -8
  162. package/dist/material/subsurfaceprofile.js.map +1 -1
  163. package/dist/material/terrain-cm.js +464 -188
  164. package/dist/material/terrain-cm.js.map +1 -1
  165. package/dist/material/unlit.js +3 -0
  166. package/dist/material/unlit.js.map +1 -1
  167. package/dist/material/water.js +2009 -201
  168. package/dist/material/water.js.map +1 -1
  169. package/dist/node_modules/meshoptimizer/LICENSE +21 -0
  170. package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
  171. package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
  172. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
  173. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
  174. package/dist/physics/character.js +226 -0
  175. package/dist/physics/character.js.map +1 -0
  176. package/dist/physics/collider.js +209 -0
  177. package/dist/physics/collider.js.map +1 -0
  178. package/dist/physics/events.js +79 -0
  179. package/dist/physics/events.js.map +1 -0
  180. package/dist/physics/joint.js +272 -0
  181. package/dist/physics/joint.js.map +1 -0
  182. package/dist/physics/node_physics.js +234 -0
  183. package/dist/physics/node_physics.js.map +1 -0
  184. package/dist/physics/part.js +91 -0
  185. package/dist/physics/part.js.map +1 -0
  186. package/dist/physics/rigid_body.js +379 -0
  187. package/dist/physics/rigid_body.js.map +1 -0
  188. package/dist/physics/vehicle.js +335 -0
  189. package/dist/physics/vehicle.js.map +1 -0
  190. package/dist/physics/world.js +25 -0
  191. package/dist/physics/world.js.map +1 -0
  192. package/dist/posteffect/bloom.js +49 -14
  193. package/dist/posteffect/bloom.js.map +1 -1
  194. package/dist/posteffect/motionblur.js +668 -63
  195. package/dist/posteffect/motionblur.js.map +1 -1
  196. package/dist/posteffect/posteffect.js +12 -1
  197. package/dist/posteffect/posteffect.js.map +1 -1
  198. package/dist/posteffect/postsss.js +572 -0
  199. package/dist/posteffect/postsss.js.map +1 -0
  200. package/dist/posteffect/sao.js +48 -50
  201. package/dist/posteffect/sao.js.map +1 -1
  202. package/dist/posteffect/skinsss.js +605 -355
  203. package/dist/posteffect/skinsss.js.map +1 -1
  204. package/dist/posteffect/ssgi.js +91 -162
  205. package/dist/posteffect/ssgi.js.map +1 -1
  206. package/dist/posteffect/ssr.js +3 -11
  207. package/dist/posteffect/ssr.js.map +1 -1
  208. package/dist/posteffect/sss.js +34 -15
  209. package/dist/posteffect/sss.js.map +1 -1
  210. package/dist/posteffect/taa.js +1 -0
  211. package/dist/posteffect/taa.js.map +1 -1
  212. package/dist/render/abuffer_oit.js +40 -39
  213. package/dist/render/abuffer_oit.js.map +1 -1
  214. package/dist/render/clipmap.js +328 -62
  215. package/dist/render/clipmap.js.map +1 -1
  216. package/dist/render/cluster_light.js +958 -224
  217. package/dist/render/cluster_light.js.map +1 -1
  218. package/dist/render/cull_visitor.js +26 -5
  219. package/dist/render/cull_visitor.js.map +1 -1
  220. package/dist/render/debug_views.js +37 -0
  221. package/dist/render/debug_views.js.map +1 -0
  222. package/dist/render/depthpass.js +5 -1
  223. package/dist/render/depthpass.js.map +1 -1
  224. package/dist/render/drawable_mixin.js +53 -6
  225. package/dist/render/drawable_mixin.js.map +1 -1
  226. package/dist/render/dualdepthpeeling_oit.js +53 -53
  227. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  228. package/dist/render/envlight.js +196 -113
  229. package/dist/render/envlight.js.map +1 -1
  230. package/dist/render/fbm_wavegenerator.js +8 -13
  231. package/dist/render/fbm_wavegenerator.js.map +1 -1
  232. package/dist/render/fft_wavegenerator.js +268 -136
  233. package/dist/render/fft_wavegenerator.js.map +1 -1
  234. package/dist/render/gerstner_wavegenerator.js +113 -28
  235. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  236. package/dist/render/gi/probe_volume.js +1334 -0
  237. package/dist/render/gi/probe_volume.js.map +1 -0
  238. package/dist/render/gi/scene_gi.js +282 -0
  239. package/dist/render/gi/scene_gi.js.map +1 -0
  240. package/dist/render/gi/voxel_atlas.js +198 -0
  241. package/dist/render/gi/voxel_atlas.js.map +1 -0
  242. package/dist/render/gi/voxel_lighting.js +652 -0
  243. package/dist/render/gi/voxel_lighting.js.map +1 -0
  244. package/dist/render/gi/voxel_scene.js +642 -0
  245. package/dist/render/gi/voxel_scene.js.map +1 -0
  246. package/dist/render/gi/voxelize_pass.js +223 -0
  247. package/dist/render/gi/voxelize_pass.js.map +1 -0
  248. package/dist/render/globalbindgroup_allocator.js +3 -2
  249. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  250. package/dist/render/gpu_picking.js +9 -36
  251. package/dist/render/gpu_picking.js.map +1 -1
  252. package/dist/render/hzb.js +80 -22
  253. package/dist/render/hzb.js.map +1 -1
  254. package/dist/render/instance_culling.js +499 -0
  255. package/dist/render/instance_culling.js.map +1 -0
  256. package/dist/render/lightpass.js +23 -9
  257. package/dist/render/lightpass.js.map +1 -1
  258. package/dist/render/lod.js +46 -0
  259. package/dist/render/lod.js.map +1 -0
  260. package/dist/render/oit.js +11 -11
  261. package/dist/render/oit.js.map +1 -1
  262. package/dist/render/primitive.js +301 -9
  263. package/dist/render/primitive.js.map +1 -1
  264. package/dist/render/primitive_readback.js +209 -0
  265. package/dist/render/primitive_readback.js.map +1 -0
  266. package/dist/render/render_queue.js +198 -11
  267. package/dist/render/render_queue.js.map +1 -1
  268. package/dist/render/renderer.js +14 -7
  269. package/dist/render/renderer.js.map +1 -1
  270. package/dist/render/rendergraph/blackboard.js +15 -1
  271. package/dist/render/rendergraph/blackboard.js.map +1 -1
  272. package/dist/render/rendergraph/executor.js +136 -0
  273. package/dist/render/rendergraph/executor.js.map +1 -1
  274. package/dist/render/rendergraph/forward_plus_builder.js +585 -59
  275. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  276. package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
  277. package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
  278. package/dist/render/rendergraph/history_resources.js +8 -1
  279. package/dist/render/rendergraph/history_resources.js.map +1 -1
  280. package/dist/render/rendergraph/scene_render_context.js +31 -2
  281. package/dist/render/rendergraph/scene_render_context.js.map +1 -1
  282. package/dist/render/rendergraph/types.js.map +1 -1
  283. package/dist/render/renderpass.js +28 -5
  284. package/dist/render/renderpass.js.map +1 -1
  285. package/dist/render/sdf/box_allocator.js +98 -0
  286. package/dist/render/sdf/box_allocator.js.map +1 -0
  287. package/dist/render/sdf/global_sdf.js +865 -0
  288. package/dist/render/sdf/global_sdf.js.map +1 -0
  289. package/dist/render/sdf/scene_sdf.js +562 -0
  290. package/dist/render/sdf/scene_sdf.js.map +1 -0
  291. package/dist/render/sdf/sdf_atlas.js +227 -0
  292. package/dist/render/sdf/sdf_atlas.js.map +1 -0
  293. package/dist/render/sdf/sdf_programs.js +42 -0
  294. package/dist/render/sdf/sdf_programs.js.map +1 -0
  295. package/dist/render/sdf/sdf_view.js +199 -0
  296. package/dist/render/sdf/sdf_view.js.map +1 -0
  297. package/dist/render/sdf/waterline.js +610 -0
  298. package/dist/render/sdf/waterline.js.map +1 -0
  299. package/dist/render/shadow_mask_pass.js +142 -25
  300. package/dist/render/shadow_mask_pass.js.map +1 -1
  301. package/dist/render/shore_wave_generator.js +1193 -0
  302. package/dist/render/shore_wave_generator.js.map +1 -0
  303. package/dist/render/sky.js +516 -86
  304. package/dist/render/sky.js.map +1 -1
  305. package/dist/render/transmission_thickness_pass.js +477 -0
  306. package/dist/render/transmission_thickness_pass.js.map +1 -0
  307. package/dist/render/underwater.js +524 -0
  308. package/dist/render/underwater.js.map +1 -0
  309. package/dist/render/virtualtexture/virtual_texture.js +1214 -0
  310. package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
  311. package/dist/render/volumetric_cloud/cloud_common.js +472 -0
  312. package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
  313. package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
  314. package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
  315. package/dist/render/volumetric_cloud/debug_view.js +131 -0
  316. package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
  317. package/dist/render/volumetric_cloud/resources.js +380 -0
  318. package/dist/render/volumetric_cloud/resources.js.map +1 -0
  319. package/dist/render/volumetric_cloud/sky_cache.js +380 -0
  320. package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
  321. package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
  322. package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
  323. package/dist/render/water_caustics.js +1195 -0
  324. package/dist/render/water_caustics.js.map +1 -0
  325. package/dist/render/water_foam.js +627 -0
  326. package/dist/render/water_foam.js.map +1 -0
  327. package/dist/render/water_interaction.js +1795 -0
  328. package/dist/render/water_interaction.js.map +1 -0
  329. package/dist/render/weightedblended_oit.js +36 -36
  330. package/dist/render/weightedblended_oit.js.map +1 -1
  331. package/dist/scene/basesprite.js +9 -4
  332. package/dist/scene/basesprite.js.map +1 -1
  333. package/dist/scene/batchgroup.js +108 -12
  334. package/dist/scene/batchgroup.js.map +1 -1
  335. package/dist/scene/environment.js +28 -7
  336. package/dist/scene/environment.js.map +1 -1
  337. package/dist/scene/foliage.js +1020 -0
  338. package/dist/scene/foliage.js.map +1 -0
  339. package/dist/scene/foliage_buckets.js +147 -0
  340. package/dist/scene/foliage_buckets.js.map +1 -0
  341. package/dist/scene/foliage_mask.js +358 -0
  342. package/dist/scene/foliage_mask.js.map +1 -0
  343. package/dist/scene/hair.js +15 -4
  344. package/dist/scene/hair.js.map +1 -1
  345. package/dist/scene/light.js +179 -13
  346. package/dist/scene/light.js.map +1 -1
  347. package/dist/scene/mesh.js +327 -45
  348. package/dist/scene/mesh.js.map +1 -1
  349. package/dist/scene/meshdrawable.js +6 -0
  350. package/dist/scene/meshdrawable.js.map +1 -1
  351. package/dist/scene/msdftext.js +9 -3
  352. package/dist/scene/msdftext.js.map +1 -1
  353. package/dist/scene/msdftextsprite.js +9 -3
  354. package/dist/scene/msdftextsprite.js.map +1 -1
  355. package/dist/scene/particlesys.js +8 -2
  356. package/dist/scene/particlesys.js.map +1 -1
  357. package/dist/scene/physics_settings.js +157 -0
  358. package/dist/scene/physics_settings.js.map +1 -0
  359. package/dist/scene/scene.js +190 -44
  360. package/dist/scene/scene.js.map +1 -1
  361. package/dist/scene/scene_node.js +147 -6
  362. package/dist/scene/scene_node.js.map +1 -1
  363. package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
  364. package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
  365. package/dist/scene/terrain-cm/grass.js +881 -42
  366. package/dist/scene/terrain-cm/grass.js.map +1 -1
  367. package/dist/scene/terrain-cm/grass_gpu.js +929 -0
  368. package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
  369. package/dist/scene/terrain-cm/grassmaterial.js +180 -9
  370. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  371. package/dist/scene/terrain-cm/terrain-cm.js +465 -36
  372. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  373. package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
  374. package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
  375. package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
  376. package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
  377. package/dist/scene/update_queue.js +54 -0
  378. package/dist/scene/update_queue.js.map +1 -0
  379. package/dist/scene/water.js +997 -96
  380. package/dist/scene/water.js.map +1 -1
  381. package/dist/scene/water_surface_sampler.js +324 -0
  382. package/dist/scene/water_surface_sampler.js.map +1 -0
  383. package/dist/scene/wind.js +317 -0
  384. package/dist/scene/wind.js.map +1 -0
  385. package/dist/shaders/atmosphere.js +388 -166
  386. package/dist/shaders/atmosphere.js.map +1 -1
  387. package/dist/shaders/fog.js +100 -32
  388. package/dist/shaders/fog.js.map +1 -1
  389. package/dist/shaders/gi.js +365 -0
  390. package/dist/shaders/gi.js.map +1 -0
  391. package/dist/shaders/ltc_rect.js +202 -0
  392. package/dist/shaders/ltc_rect.js.map +1 -0
  393. package/dist/shaders/misc.js +1 -10
  394. package/dist/shaders/misc.js.map +1 -1
  395. package/dist/shaders/noise.js +5 -1
  396. package/dist/shaders/noise.js.map +1 -1
  397. package/dist/shaders/probes.js +309 -0
  398. package/dist/shaders/probes.js.map +1 -0
  399. package/dist/shaders/sdf.js +537 -0
  400. package/dist/shaders/sdf.js.map +1 -0
  401. package/dist/shaders/shadow.js +104 -70
  402. package/dist/shaders/shadow.js.map +1 -1
  403. package/dist/shaders/skin_brdf.js +243 -0
  404. package/dist/shaders/skin_brdf.js.map +1 -0
  405. package/dist/shaders/ssr.js +6 -159
  406. package/dist/shaders/ssr.js.map +1 -1
  407. package/dist/shaders/temporal.js +38 -27
  408. package/dist/shaders/temporal.js.map +1 -1
  409. package/dist/shaders/water.js +40 -87
  410. package/dist/shaders/water.js.map +1 -1
  411. package/dist/shaders/water_medium.js +42 -0
  412. package/dist/shaders/water_medium.js.map +1 -0
  413. package/dist/shadow/dom.js +110 -19
  414. package/dist/shadow/dom.js.map +1 -1
  415. package/dist/shadow/esm.js +3 -3
  416. package/dist/shadow/esm.js.map +1 -1
  417. package/dist/shadow/pcf_opt.js +15 -48
  418. package/dist/shadow/pcf_opt.js.map +1 -1
  419. package/dist/shadow/pcf_pd.js +15 -53
  420. package/dist/shadow/pcf_pd.js.map +1 -1
  421. package/dist/shadow/pcss.js +34 -9
  422. package/dist/shadow/pcss.js.map +1 -1
  423. package/dist/shadow/shadow_impl.js +3 -8
  424. package/dist/shadow/shadow_impl.js.map +1 -1
  425. package/dist/shadow/shadow_region.js +87 -49
  426. package/dist/shadow/shadow_region.js.map +1 -1
  427. package/dist/shadow/shadowmapper.js +93 -53
  428. package/dist/shadow/shadowmapper.js.map +1 -1
  429. package/dist/shadow/ssm.js +18 -72
  430. package/dist/shadow/ssm.js.map +1 -1
  431. package/dist/shadow/vsm.js +3 -3
  432. package/dist/shadow/vsm.js.map +1 -1
  433. package/dist/shapes/shape.js +44 -1
  434. package/dist/shapes/shape.js.map +1 -1
  435. package/dist/utility/buoyancy.js +287 -0
  436. package/dist/utility/buoyancy.js.map +1 -0
  437. package/dist/utility/physical.js +10 -1
  438. package/dist/utility/physical.js.map +1 -1
  439. package/dist/utility/pmrem.js +17 -43
  440. package/dist/utility/pmrem.js.map +1 -1
  441. package/dist/utility/sdf/bake.js +435 -0
  442. package/dist/utility/sdf/bake.js.map +1 -0
  443. package/dist/utility/sdf/bvh.js +576 -0
  444. package/dist/utility/sdf/bvh.js.map +1 -0
  445. package/dist/utility/serialization/manager.js +36 -6
  446. package/dist/utility/serialization/manager.js.map +1 -1
  447. package/dist/utility/serialization/scene/animation.js +77 -7
  448. package/dist/utility/serialization/scene/animation.js.map +1 -1
  449. package/dist/utility/serialization/scene/batch.js +11 -4
  450. package/dist/utility/serialization/scene/batch.js.map +1 -1
  451. package/dist/utility/serialization/scene/camera.js +140 -279
  452. package/dist/utility/serialization/scene/camera.js.map +1 -1
  453. package/dist/utility/serialization/scene/cloth.js +25 -0
  454. package/dist/utility/serialization/scene/cloth.js.map +1 -0
  455. package/dist/utility/serialization/scene/common.js +14 -6
  456. package/dist/utility/serialization/scene/common.js.map +1 -1
  457. package/dist/utility/serialization/scene/foliage.js +821 -0
  458. package/dist/utility/serialization/scene/foliage.js.map +1 -0
  459. package/dist/utility/serialization/scene/hair.js +11 -4
  460. package/dist/utility/serialization/scene/hair.js.map +1 -1
  461. package/dist/utility/serialization/scene/light.js +115 -7
  462. package/dist/utility/serialization/scene/light.js.map +1 -1
  463. package/dist/utility/serialization/scene/material.js +388 -220
  464. package/dist/utility/serialization/scene/material.js.map +1 -1
  465. package/dist/utility/serialization/scene/mesh.js +69 -4
  466. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  467. package/dist/utility/serialization/scene/node.js +140 -5
  468. package/dist/utility/serialization/scene/node.js.map +1 -1
  469. package/dist/utility/serialization/scene/node_physics.js +1318 -0
  470. package/dist/utility/serialization/scene/node_physics.js.map +1 -0
  471. package/dist/utility/serialization/scene/particle.js +11 -4
  472. package/dist/utility/serialization/scene/particle.js.map +1 -1
  473. package/dist/utility/serialization/scene/physics.js +120 -0
  474. package/dist/utility/serialization/scene/physics.js.map +1 -0
  475. package/dist/utility/serialization/scene/primitive.js +124 -10
  476. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  477. package/dist/utility/serialization/scene/scene.js +1687 -51
  478. package/dist/utility/serialization/scene/scene.js.map +1 -1
  479. package/dist/utility/serialization/scene/sprite.js +11 -4
  480. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  481. package/dist/utility/serialization/scene/terrain.js +163 -33
  482. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  483. package/dist/utility/serialization/scene/text.js +11 -4
  484. package/dist/utility/serialization/scene/text.js.map +1 -1
  485. package/dist/utility/serialization/scene/water.js +2473 -114
  486. package/dist/utility/serialization/scene/water.js.map +1 -1
  487. package/dist/utility/serialization/types.js.map +1 -1
  488. package/dist/utility/shprojector.js +97 -109
  489. package/dist/utility/shprojector.js.map +1 -1
  490. package/dist/utility/textures/foam.js +170 -0
  491. package/dist/utility/textures/foam.js.map +1 -0
  492. package/dist/utility/textures/ggxlut.js +17 -43
  493. package/dist/utility/textures/ggxlut.js.map +1 -1
  494. package/dist/utility/textures/ltcdata.js +4 -4
  495. package/dist/utility/textures/ltcdata.js.map +1 -1
  496. package/dist/utility/textures/ltclut.js +16 -6
  497. package/dist/utility/textures/ltclut.js.map +1 -1
  498. package/dist/utility/unsupported.js +24 -0
  499. package/dist/utility/unsupported.js.map +1 -0
  500. package/dist/values.js +13 -1
  501. package/dist/values.js.map +1 -1
  502. package/package.json +25 -12
  503. package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
  504. package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  505. package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  506. package/src/asset/meshoptimizer/LICENSE +21 -0
@@ -0,0 +1,1058 @@
1
+ import { Vector3 } from '@zephyr3d/base';
2
+
3
+ /**
4
+ * Number of scalar parameter columns stored per profile row.
5
+ *
6
+ * @remarks
7
+ * Mirrors the row layout UE5 uses in its `SSProfiles` texture, read back from
8
+ * `UEDigitalHuman.rdc` (SSS::PassOne_Burley lines 143-157).
9
+ *
10
+ * @internal
11
+ */ const SKIN_PROFILE_PARAM_COLUMNS = 6;
12
+ /**
13
+ * Number of texels the baked transmission profile occupies.
14
+ *
15
+ * @remarks
16
+ * `BSSS_TRANSMISSION_PROFILE_SIZE` in UE5 (`SubsurfaceProfileCommon.ush:27`).
17
+ *
18
+ * @internal
19
+ */ const SKIN_TRANSMISSION_LUT_SIZE = 32;
20
+ /**
21
+ * Total columns per profile row: the scalar parameters followed by the baked
22
+ * transmission profile, exactly as UE5 lays out an `SSProfiles` row.
23
+ *
24
+ * @internal
25
+ */ const SKIN_PROFILE_COLUMNS = SKIN_PROFILE_PARAM_COLUMNS + SKIN_TRANSMISSION_LUT_SIZE;
26
+ /** Maximum number of live skin profiles, matching the 8-bit profile id channel. @internal */ const SKIN_PROFILE_CAPACITY = 256;
27
+ /**
28
+ * Gate on {@link SkinProfile}'s constructor.
29
+ *
30
+ * @remarks
31
+ * Module-private, so only {@link SkinProfile.createOwned} can pass it. See the
32
+ * constructor for why a `private` modifier alone is not enough.
33
+ *
34
+ * @internal
35
+ */ const CREATE_TOKEN = Symbol('SkinProfile.create');
36
+ /**
37
+ * Largest optical depth the transmission profile is defined over.
38
+ *
39
+ * @remarks
40
+ * `SSSS_MAX_TRANSMISSION_PROFILE_DISTANCE` in UE5. It appears in two places that
41
+ * have to agree, which is why it lives here rather than next to either of them:
42
+ * the thickness pass encodes its optical depth as `1 - opticalDepth / MAX`, and
43
+ * the transmission BxDF decodes that and divides by `MAX` again to index the
44
+ * baked profile below.
45
+ *
46
+ * @internal
47
+ */ const SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH = 5;
48
+ /**
49
+ * Floor the averaged optical depth is clamped to before encoding.
50
+ *
51
+ * @remarks
52
+ * UE5's `clamp(..., 0.15, 5)` in `CalculateOpticalDepth`.
53
+ *
54
+ * @internal
55
+ */ const SKIN_TRANSMISSION_OPTICAL_DEPTH_FLOOR = 0.15;
56
+ /**
57
+ * Constant added to the optical depth after clamping.
58
+ *
59
+ * @remarks
60
+ * UE5's trailing `+ 0.25` in `CalculateOpticalDepth`.
61
+ *
62
+ * @internal
63
+ */ const SKIN_TRANSMISSION_OPTICAL_DEPTH_BIAS = 0.25;
64
+ /**
65
+ * Largest value the thickness pass can write, and with it the sentinel that
66
+ * separates a real measurement from "no light wrote this".
67
+ *
68
+ * @remarks
69
+ * The encoding is `1 - opticalDepth / MAX` and the optical depth never falls
70
+ * below `FLOOR + BIAS`, so the pass tops out at 0.92 and the cleared value of 1
71
+ * is unreachable — which makes it usable as "no data".
72
+ *
73
+ * The distinction matters because 1 decodes to zero optical depth, the profile's
74
+ * *strongest* entry: read as a measurement it would light every non-transmitting
75
+ * light's pixels at full transmission.
76
+ *
77
+ * @internal
78
+ */ const SKIN_TRANSMISSION_NO_DATA_ENCODING = 1 - (SKIN_TRANSMISSION_OPTICAL_DEPTH_FLOOR + SKIN_TRANSMISSION_OPTICAL_DEPTH_BIAS) / SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH;
79
+ /**
80
+ * Distance, in profile millimetres, the baked transmission profile spans.
81
+ *
82
+ * @remarks
83
+ * `MaxTransmissionProfileDistance * CmToMm = 5 * 10` in
84
+ * `ComputeTransmissionProfileBurley`.
85
+ *
86
+ * The 5 here and the one in {@link SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH} are the
87
+ * same number by construction: UE5's world unit is the centimetre, so an optical
88
+ * depth of 1 at unit extinction *is* one centimetre, and this axis is that span
89
+ * in the millimetres the mean free paths use. The shader indexes the table with
90
+ * optical depth directly — self-consistent only once that is accounted for.
91
+ *
92
+ * The thickness pass must therefore be calibrated against this axis;
93
+ * {@link SKIN_OPTICAL_DEPTH_PER_WORLD_UNIT} is that calibration, and changing
94
+ * this constant without it slides the whole profile along the thickness axis.
95
+ *
96
+ * @internal
97
+ */ const TRANSMISSION_LUT_MAX_DISTANCE_MM = 50;
98
+ /**
99
+ * Radius the baked transmission profile is offset by, in profile millimetres.
100
+ *
101
+ * @remarks
102
+ * `ProfileRadiusOffset` in `BurleyNormalizedSSS.cpp:22`, 0.06 cm, times the same
103
+ * `CmToMm`. It keeps the zero-thickness entry off the singular `r = 0` end of
104
+ * the diffusion profile.
105
+ *
106
+ * @internal
107
+ */ const TRANSMISSION_LUT_RADIUS_OFFSET_MM = 0.6;
108
+ /**
109
+ * Profile-space millimetres per world unit, before the profile's own world unit
110
+ * scale is applied.
111
+ *
112
+ * @remarks
113
+ * This engine is metric throughout, so a world unit is a metre and the profile
114
+ * distances are millimetres. UE5 instead carries a `BURLEY_CM_2_MM = 10` because
115
+ * its world unit is the centimetre; that factor has no analogue here and must
116
+ * not be transcribed (see the diffusion pass, which likewise omits it).
117
+ *
118
+ * @internal
119
+ */ const WORLD_UNITS_TO_PROFILE_MM = 1000;
120
+ /**
121
+ * Optical depth accumulated per world unit of light path, before the profile's
122
+ * `extinctionScale`.
123
+ *
124
+ * @remarks
125
+ * Derived rather than chosen, so the thickness pass and the baked profile cannot
126
+ * drift apart. The BxDF indexes entry `opticalDepth / MAX * (size - 1)` and entry
127
+ * `i` was baked for `i / size * LUT_MAX_MM` profile millimetres, so one unit of
128
+ * optical depth is `LUT_MAX_MM * (size - 1) / size / MAX` millimetres.
129
+ *
130
+ * `worldUnitScale` deliberately does not appear: it divides both the table's axis
131
+ * and the path's conversion into profile space, so it cancels. UE5 likewise keeps
132
+ * it out of `CalculateOpticalDepth`.
133
+ *
134
+ * The `(size - 1) / size` is a deliberate 3% departure from UE5, whose index and
135
+ * axis disagree by that factor. Correcting it makes a path of `t` metres index
136
+ * exactly the entry baked for `t` millimetres.
137
+ *
138
+ * @internal
139
+ */ const SKIN_OPTICAL_DEPTH_PER_WORLD_UNIT = WORLD_UNITS_TO_PROFILE_MM * SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH / (TRANSMISSION_LUT_MAX_DISTANCE_MM * ((SKIN_TRANSMISSION_LUT_SIZE - 1) / SKIN_TRANSMISSION_LUT_SIZE));
140
+ /**
141
+ * `GetSearchLightDiffuseScalingFactor`, the default variant.
142
+ *
143
+ * @remarks
144
+ * `s = 3.5 + 100 (A - 0.33)^4`. The same curve the diffusion evaluates on the
145
+ * GPU (`posteffect/skinsss.ts`, `scalingFactor`); the transmission profile is
146
+ * baked on the CPU and needs its own copy.
147
+ *
148
+ * @internal
149
+ */ function searchLightDiffuseScalingFactor(albedo) {
150
+ const v = albedo - 0.33;
151
+ return 3.5 + 100 * v * v * v * v;
152
+ }
153
+ /**
154
+ * Preset parameters.
155
+ *
156
+ * @remarks
157
+ * `skin` reproduces UE5's `FSubsurfaceProfileStruct` defaults exactly, converted
158
+ * into this engine's metre-based scene units:
159
+ *
160
+ * | UE5 parameter | UE5 default | here |
161
+ * | -------------------- | ------------------------------- | --------------------------- |
162
+ * | SurfaceAlbedo | (0.91058, 0.338275, 0.2718) | `surfaceAlbedo`, unchanged |
163
+ * | MeanFreePathColor | (1, 0.1983/2.229, 0.1607/2.229) | `meanFreePath`, unchanged |
164
+ * | MeanFreePathDistance | 1.2 × 2.229 = 2.6748 cm | `meanFreePathDistance` in m |
165
+ * | WorldUnitScale | 0.1 cm | folded into the distance |
166
+ * | BoundaryColorBleed | white | white |
167
+ *
168
+ * The red-shifted mean free path (roughly 1 : 0.089 : 0.072) and the equally
169
+ * red-shifted albedo are together what make the diffusion read as skin rather
170
+ * than as a neutral blur — the first thing to check when scattering looks washed
171
+ * out. UE5 ships only `skin`; the rest are this engine's own, pitched around the
172
+ * same regime to stay comparable.
173
+ *
174
+ * `boundaryColorBleed` is white throughout, as in UE5: it tints taps belonging to
175
+ * a *different* profile, so anything darker attenuates every profile boundary.
176
+ *
177
+ * @internal
178
+ */ const SKIN_PROFILE_TEMPLATES = {
179
+ skin: {
180
+ surfaceAlbedo: [
181
+ 0.91058,
182
+ 0.338275,
183
+ 0.2718
184
+ ],
185
+ meanFreePath: [
186
+ 1.0,
187
+ 0.0889636,
188
+ 0.0720951
189
+ ],
190
+ meanFreePathDistance: 0.026748,
191
+ worldUnitScale: 1,
192
+ scatterScale: 1,
193
+ boundaryColorBleed: [
194
+ 1,
195
+ 1,
196
+ 1
197
+ ],
198
+ transmissionTint: [
199
+ 1.0,
200
+ 0.42,
201
+ 0.3
202
+ ],
203
+ extinctionScale: 1,
204
+ normalScale: 0.08,
205
+ scatteringDistribution: 0.93,
206
+ ior: 1.55,
207
+ roughness0: 0.75,
208
+ roughness1: 1.3,
209
+ lobeMix: 0.85
210
+ },
211
+ skin_pale: {
212
+ surfaceAlbedo: [
213
+ 0.93,
214
+ 0.4,
215
+ 0.33
216
+ ],
217
+ meanFreePath: [
218
+ 1.0,
219
+ 0.1,
220
+ 0.083
221
+ ],
222
+ meanFreePathDistance: 0.0294,
223
+ worldUnitScale: 1,
224
+ scatterScale: 1,
225
+ boundaryColorBleed: [
226
+ 1,
227
+ 1,
228
+ 1
229
+ ],
230
+ transmissionTint: [
231
+ 1.0,
232
+ 0.48,
233
+ 0.38
234
+ ],
235
+ extinctionScale: 1,
236
+ normalScale: 0.08,
237
+ scatteringDistribution: 0.93,
238
+ ior: 1.55,
239
+ roughness0: 0.72,
240
+ roughness1: 1.25,
241
+ lobeMix: 0.85
242
+ },
243
+ skin_tan: {
244
+ surfaceAlbedo: [
245
+ 0.88,
246
+ 0.3,
247
+ 0.235
248
+ ],
249
+ meanFreePath: [
250
+ 1.0,
251
+ 0.082,
252
+ 0.066
253
+ ],
254
+ meanFreePathDistance: 0.0254,
255
+ worldUnitScale: 1,
256
+ scatterScale: 1,
257
+ boundaryColorBleed: [
258
+ 1,
259
+ 1,
260
+ 1
261
+ ],
262
+ transmissionTint: [
263
+ 1.0,
264
+ 0.38,
265
+ 0.26
266
+ ],
267
+ extinctionScale: 1,
268
+ normalScale: 0.08,
269
+ scatteringDistribution: 0.93,
270
+ ior: 1.55,
271
+ roughness0: 0.76,
272
+ roughness1: 1.32,
273
+ lobeMix: 0.85
274
+ },
275
+ skin_dark: {
276
+ surfaceAlbedo: [
277
+ 0.8,
278
+ 0.24,
279
+ 0.185
280
+ ],
281
+ meanFreePath: [
282
+ 1.0,
283
+ 0.072,
284
+ 0.057
285
+ ],
286
+ meanFreePathDistance: 0.0227,
287
+ worldUnitScale: 1,
288
+ scatterScale: 1,
289
+ boundaryColorBleed: [
290
+ 1,
291
+ 1,
292
+ 1
293
+ ],
294
+ transmissionTint: [
295
+ 1.0,
296
+ 0.3,
297
+ 0.2
298
+ ],
299
+ extinctionScale: 1,
300
+ normalScale: 0.08,
301
+ scatteringDistribution: 0.93,
302
+ ior: 1.55,
303
+ roughness0: 0.78,
304
+ roughness1: 1.35,
305
+ lobeMix: 0.85
306
+ },
307
+ wax: {
308
+ surfaceAlbedo: [
309
+ 0.95,
310
+ 0.9,
311
+ 0.8
312
+ ],
313
+ meanFreePath: [
314
+ 1.0,
315
+ 0.85,
316
+ 0.68
317
+ ],
318
+ meanFreePathDistance: 0.05,
319
+ worldUnitScale: 1,
320
+ scatterScale: 1,
321
+ boundaryColorBleed: [
322
+ 1,
323
+ 1,
324
+ 1
325
+ ],
326
+ transmissionTint: [
327
+ 1.0,
328
+ 0.88,
329
+ 0.74
330
+ ],
331
+ extinctionScale: 1,
332
+ normalScale: 0.05,
333
+ scatteringDistribution: 0.85,
334
+ ior: 1.45,
335
+ roughness0: 0.85,
336
+ roughness1: 1.4,
337
+ lobeMix: 0.8
338
+ },
339
+ jade: {
340
+ surfaceAlbedo: [
341
+ 0.75,
342
+ 0.93,
343
+ 0.86
344
+ ],
345
+ meanFreePath: [
346
+ 0.6,
347
+ 1.0,
348
+ 0.85
349
+ ],
350
+ meanFreePathDistance: 0.045,
351
+ worldUnitScale: 1,
352
+ scatterScale: 1,
353
+ boundaryColorBleed: [
354
+ 1,
355
+ 1,
356
+ 1
357
+ ],
358
+ transmissionTint: [
359
+ 0.68,
360
+ 0.95,
361
+ 0.88
362
+ ],
363
+ extinctionScale: 1,
364
+ normalScale: 0.05,
365
+ scatteringDistribution: 0.7,
366
+ ior: 1.6,
367
+ roughness0: 0.6,
368
+ roughness1: 1.1,
369
+ lobeMix: 0.75
370
+ },
371
+ marble: {
372
+ surfaceAlbedo: [
373
+ 0.95,
374
+ 0.94,
375
+ 0.92
376
+ ],
377
+ meanFreePath: [
378
+ 1.0,
379
+ 0.96,
380
+ 0.92
381
+ ],
382
+ meanFreePathDistance: 0.035,
383
+ worldUnitScale: 1,
384
+ scatterScale: 1,
385
+ boundaryColorBleed: [
386
+ 1,
387
+ 1,
388
+ 1
389
+ ],
390
+ transmissionTint: [
391
+ 0.96,
392
+ 0.95,
393
+ 0.94
394
+ ],
395
+ extinctionScale: 1,
396
+ normalScale: 0.04,
397
+ scatteringDistribution: 0.6,
398
+ ior: 1.55,
399
+ roughness0: 0.55,
400
+ roughness1: 1.05,
401
+ lobeMix: 0.7
402
+ }
403
+ };
404
+ /**
405
+ * Subsurface profile for {@link SSSMaterial}, holding the parameters UE5's
406
+ * Burley diffusion is driven by.
407
+ *
408
+ * @remarks
409
+ * Profiles are packed into a shared GPU table keyed by {@link SkinProfile.id},
410
+ * and materials write that id per pixel. This lets several profiles — face, ears,
411
+ * lips — diffuse independently in a single screen-space pass, which is how UE5
412
+ * drives its subsurface scattering.
413
+ *
414
+ * This is a separate type from the older `SubsurfaceProfile`, which continues to
415
+ * serve `PBRMetallicRoughnessMaterial` and the generic SSS post effect. The two
416
+ * are independent and may be used side by side.
417
+ *
418
+ * @public
419
+ */ class SkinProfile {
420
+ static _profiles = new Array(SKIN_PROFILE_CAPACITY).fill(null);
421
+ static _table = null;
422
+ /** Device {@link SkinProfile._table} belongs to, so a device swap can be spotted. */ static _tableDevice = null;
423
+ static _tableData = null;
424
+ static _tableDirty = true;
425
+ static _defaultProfile = null;
426
+ _id;
427
+ _surfaceAlbedo;
428
+ _meanFreePath;
429
+ _boundaryColorBleed;
430
+ _transmissionTint;
431
+ _preset;
432
+ _meanFreePathDistance;
433
+ _worldUnitScale;
434
+ _scatterScale;
435
+ _extinctionScale;
436
+ _normalScale;
437
+ _scatteringDistribution;
438
+ _ior;
439
+ _roughness0;
440
+ _roughness1;
441
+ _lobeMix;
442
+ _disposed;
443
+ _changeListeners;
444
+ /**
445
+ * Not constructible from outside. Use {@link SSSMaterial.subsurfaceProfile},
446
+ * which owns one for the material's lifetime.
447
+ *
448
+ * @remarks
449
+ * The token check is not redundant with `private`: profiles hold a row of a
450
+ * 256-entry GPU table that only {@link SkinProfile.dispose} returns, and the
451
+ * editor ships as prebuilt JavaScript that drives this class through
452
+ * serialization metadata, where TypeScript's visibility rules do not apply.
453
+ *
454
+ * @internal
455
+ */ constructor(token, preset = 'skin'){
456
+ if (token !== CREATE_TOKEN) {
457
+ throw new Error('SkinProfile is not constructible directly; it is owned by the SSSMaterial that created it.');
458
+ }
459
+ this._surfaceAlbedo = new Vector3();
460
+ this._meanFreePath = new Vector3();
461
+ this._boundaryColorBleed = new Vector3();
462
+ this._transmissionTint = new Vector3();
463
+ this._preset = preset;
464
+ this._meanFreePathDistance = 0;
465
+ this._worldUnitScale = 1;
466
+ this._scatterScale = 1;
467
+ this._extinctionScale = 1;
468
+ this._normalScale = 0.08;
469
+ this._scatteringDistribution = 0.93;
470
+ this._ior = 1.55;
471
+ this._roughness0 = 0.75;
472
+ this._roughness1 = 1.3;
473
+ this._lobeMix = 0.85;
474
+ this._disposed = false;
475
+ this._changeListeners = new Set();
476
+ this._id = SkinProfile.allocateId(this);
477
+ this.applyPreset(preset);
478
+ }
479
+ /**
480
+ * Registers a callback invoked whenever this profile's parameters change.
481
+ *
482
+ * @param listener - The callback to add.
483
+ *
484
+ * @public
485
+ */ addChangeListener(listener) {
486
+ this._changeListeners.add(listener);
487
+ }
488
+ /**
489
+ * Removes a previously registered change listener.
490
+ *
491
+ * @param listener - The callback to remove.
492
+ *
493
+ * @public
494
+ */ removeChangeListener(listener) {
495
+ this._changeListeners.delete(listener);
496
+ }
497
+ /**
498
+ * Creates a profile for a material to own.
499
+ *
500
+ * @remarks
501
+ * The only way to obtain one. The caller takes on releasing its table row — in
502
+ * practice {@link SSSMaterial}, which disposes the profile with itself.
503
+ *
504
+ * @param preset - Preset to start from. Defaults to `'skin'`.
505
+ * @returns The new profile.
506
+ *
507
+ * @internal
508
+ */ static createOwned(preset = 'skin') {
509
+ return new SkinProfile(CREATE_TOKEN, preset);
510
+ }
511
+ /**
512
+ * The shared default skin profile.
513
+ *
514
+ * @remarks
515
+ * A fallback for consumers with no material to ask — the diffusion uses it for
516
+ * pixels whose id is not in the table. Created lazily, since it holds a table
517
+ * row for the lifetime of the process.
518
+ *
519
+ * @public
520
+ */ static getDefault() {
521
+ if (!this._defaultProfile) {
522
+ this._defaultProfile = new SkinProfile(CREATE_TOKEN, 'skin');
523
+ }
524
+ return this._defaultProfile;
525
+ }
526
+ /**
527
+ * Looks up a profile by its packed id.
528
+ *
529
+ * @param id - The profile id, as written to the per-pixel profile channel.
530
+ * @returns The profile, or `null` when the id is unused.
531
+ *
532
+ * @public
533
+ */ static getById(id) {
534
+ return id > 0 && id < SKIN_PROFILE_CAPACITY ? this._profiles[id] : null;
535
+ }
536
+ /**
537
+ * The packed parameter table, rebuilt when any profile has changed.
538
+ *
539
+ * @remarks
540
+ * Rows are indexed by profile id, columns by parameter group. The skin
541
+ * diffusion passes sample this to recover per-pixel scattering parameters.
542
+ *
543
+ * @param device - Device used to create and upload the table.
544
+ * @returns The table texture, or `null` when it could not be created.
545
+ *
546
+ * @public
547
+ */ static getTable(device) {
548
+ // The table is static and outlives any one device. A texture belonging to a
549
+ // dead device silently swallows every `update`, which strands the last values
550
+ // uploaded before the swap on screen until the page is reloaded.
551
+ if (this._table && (this._tableDevice !== device || this._table.disposed)) {
552
+ this._table = null;
553
+ this._tableData = null;
554
+ }
555
+ if (!this._table) {
556
+ this._table = device.createTexture2D('rgba32f', SKIN_PROFILE_COLUMNS, SKIN_PROFILE_CAPACITY, {
557
+ mipmapping: false,
558
+ samplerOptions: {
559
+ minFilter: 'nearest',
560
+ magFilter: 'nearest',
561
+ mipFilter: 'none'
562
+ }
563
+ });
564
+ this._tableDevice = device;
565
+ this._tableData = new Float32Array(SKIN_PROFILE_COLUMNS * SKIN_PROFILE_CAPACITY * 4);
566
+ this._tableDirty = true;
567
+ }
568
+ if (this._tableDirty && this._table && this._tableData) {
569
+ this.packTable(this._tableData);
570
+ this._table.update(this._tableData, 0, 0, SKIN_PROFILE_COLUMNS, SKIN_PROFILE_CAPACITY);
571
+ this._tableDirty = false;
572
+ }
573
+ return this._table;
574
+ }
575
+ /** Number of parameter columns in the packed table. @public */ static get tableColumns() {
576
+ return SKIN_PROFILE_COLUMNS;
577
+ }
578
+ /**
579
+ * Column the baked transmission profile starts at.
580
+ *
581
+ * @remarks
582
+ * UE5's `BSSS_TRANSMISSION_PROFILE_OFFSET`. Entry `i` lives at column
583
+ * `transmissionLutOffset + i`.
584
+ *
585
+ * @public
586
+ */ static get transmissionLutOffset() {
587
+ return SKIN_PROFILE_PARAM_COLUMNS;
588
+ }
589
+ /**
590
+ * Column holding `(worldUnitScale, scatterScale, 0, 0)`.
591
+ *
592
+ * @remarks
593
+ * Read by the transmission thickness pass, which needs the world unit scale to
594
+ * size its normal shrink against the asset.
595
+ *
596
+ * @public
597
+ */ static get scalingParamColumn() {
598
+ return 0;
599
+ }
600
+ /**
601
+ * Column holding `(extinctionScale, normalScale, scatteringDistribution,
602
+ * 1 / ior)`.
603
+ *
604
+ * @remarks
605
+ * UE5's `SSSS_TRANSMISSION_OFFSET`, unpacked by
606
+ * `GetTransmissionProfileParams`.
607
+ *
608
+ * @public
609
+ */ static get transmissionParamColumn() {
610
+ return 4;
611
+ }
612
+ /** Number of entries in the baked transmission profile. @public */ static get transmissionLutSize() {
613
+ return SKIN_TRANSMISSION_LUT_SIZE;
614
+ }
615
+ /** Number of rows in the packed table. @public */ static get tableRows() {
616
+ return SKIN_PROFILE_CAPACITY;
617
+ }
618
+ /**
619
+ * The id this profile occupies in the packed table.
620
+ *
621
+ * @remarks
622
+ * Ids start at 1; 0 means "not skin" in the per-pixel profile channel. The
623
+ * material encodes this as `id / 255`.
624
+ *
625
+ * @public
626
+ */ get id() {
627
+ return this._id;
628
+ }
629
+ /** Normalized id as written to an 8-bit per-pixel channel. @public */ get encodedId() {
630
+ return this._id / 255;
631
+ }
632
+ /** The preset this profile was last initialized from. @public */ get preset() {
633
+ return this._preset;
634
+ }
635
+ set preset(val) {
636
+ if (val !== this._preset) {
637
+ this.applyPreset(val);
638
+ }
639
+ }
640
+ /**
641
+ * Per-channel scattering albedo.
642
+ *
643
+ * @remarks
644
+ * Drives Burley's shaping term `s = (albedo - 0.33)^4 * 100 + 3.5`, so values
645
+ * near 0.33 give the widest diffusion and values at either extreme tighten it.
646
+ *
647
+ * @public
648
+ */ get surfaceAlbedo() {
649
+ return this._surfaceAlbedo;
650
+ }
651
+ set surfaceAlbedo(val) {
652
+ if (!val.equalsTo(this._surfaceAlbedo)) {
653
+ this._surfaceAlbedo.set(val);
654
+ this.notifyChanged();
655
+ }
656
+ }
657
+ /**
658
+ * Per-channel diffuse mean free path, as a ratio.
659
+ *
660
+ * @remarks
661
+ * Scaled by {@link SkinProfile.meanFreePathDistance} to reach world units. The
662
+ * red channel is normally much longer than blue, which is what makes thin
663
+ * geometry such as an ear rim glow red.
664
+ *
665
+ * @public
666
+ */ get meanFreePath() {
667
+ return this._meanFreePath;
668
+ }
669
+ set meanFreePath(val) {
670
+ if (!val.equalsTo(this._meanFreePath)) {
671
+ this._meanFreePath.set(val);
672
+ this.notifyChanged();
673
+ }
674
+ }
675
+ /** Mean free path of the widest channel, in profile space. @public */ get meanFreePathDistance() {
676
+ return this._meanFreePathDistance;
677
+ }
678
+ set meanFreePathDistance(val) {
679
+ const next = Math.max(0, val ?? 0);
680
+ if (next !== this._meanFreePathDistance) {
681
+ this._meanFreePathDistance = next;
682
+ this.notifyChanged();
683
+ }
684
+ }
685
+ /**
686
+ * Profile-space to world-unit conversion, for scenes not authored in metres.
687
+ *
688
+ * @remarks
689
+ * Applied once, when the diffusion converts a scatter radius into a screen
690
+ * offset — the same place UE5 applies it, in `CalculateBurleyScale`. It is
691
+ * deliberately absent from {@link SkinProfile.getScatterDistance}.
692
+ *
693
+ * @public
694
+ */ get worldUnitScale() {
695
+ return this._worldUnitScale;
696
+ }
697
+ set worldUnitScale(val) {
698
+ const next = Math.max(0.01, val ?? 1);
699
+ if (next !== this._worldUnitScale) {
700
+ this._worldUnitScale = next;
701
+ this.notifyChanged();
702
+ }
703
+ }
704
+ /** Overall multiplier on the diffusion width. @public */ get scatterScale() {
705
+ return this._scatterScale;
706
+ }
707
+ set scatterScale(val) {
708
+ const next = Math.max(0, val ?? 1);
709
+ if (next !== this._scatterScale) {
710
+ this._scatterScale = next;
711
+ this.notifyChanged();
712
+ }
713
+ }
714
+ /**
715
+ * Per-channel tint applied where two different profiles meet.
716
+ *
717
+ * @remarks
718
+ * Taps that belong to another profile are attenuated by this instead of being
719
+ * rejected outright, so a face/lip boundary softens rather than forming a seam.
720
+ *
721
+ * @public
722
+ */ get boundaryColorBleed() {
723
+ return this._boundaryColorBleed;
724
+ }
725
+ set boundaryColorBleed(val) {
726
+ if (!val.equalsTo(this._boundaryColorBleed)) {
727
+ this._boundaryColorBleed.set(val);
728
+ this.notifyChanged();
729
+ }
730
+ }
731
+ /** Tint applied to light transmitted through thin geometry. @public */ get transmissionTint() {
732
+ return this._transmissionTint;
733
+ }
734
+ set transmissionTint(val) {
735
+ if (!val.equalsTo(this._transmissionTint)) {
736
+ this._transmissionTint.set(val);
737
+ this.notifyChanged();
738
+ }
739
+ }
740
+ /** Extinction multiplier for transmission attenuation. @public */ get extinctionScale() {
741
+ return this._extinctionScale;
742
+ }
743
+ set extinctionScale(val) {
744
+ const next = Math.max(0, val ?? 1);
745
+ if (next !== this._extinctionScale) {
746
+ this._extinctionScale = next;
747
+ this.notifyChanged();
748
+ }
749
+ }
750
+ /** How much surface normal detail survives the diffusion. @public */ get normalScale() {
751
+ return this._normalScale;
752
+ }
753
+ set normalScale(val) {
754
+ const next = Math.max(0, val ?? 0);
755
+ if (next !== this._normalScale) {
756
+ this._normalScale = next;
757
+ this.notifyChanged();
758
+ }
759
+ }
760
+ /**
761
+ * Henyey-Greenstein asymmetry of the transmitted light, in `[-1, 1]`.
762
+ *
763
+ * @remarks
764
+ * UE5's `ScatteringDistribution`, default 0.93 for skin. Positive values throw
765
+ * the transmitted light forward, concentrating a backlit ear's glow where the
766
+ * light shines through rather than spreading it as a uniform wash.
767
+ *
768
+ * Only the transmission BxDF uses this; the screen-space diffusion is isotropic
769
+ * and ignores it. Stored raw, unlike UE5's `[0, 1]` remap for an 8-bit texture.
770
+ *
771
+ * @public
772
+ */ get scatteringDistribution() {
773
+ return this._scatteringDistribution;
774
+ }
775
+ set scatteringDistribution(val) {
776
+ const next = Math.min(1, Math.max(-1, val ?? 0));
777
+ if (next !== this._scatteringDistribution) {
778
+ this._scatteringDistribution = next;
779
+ this.notifyChanged();
780
+ }
781
+ }
782
+ /**
783
+ * Index of refraction used to bend the view ray before the phase function.
784
+ *
785
+ * @remarks
786
+ * UE5's `IOR`, default 1.55 for skin, stored in the table as `1 / ior` since
787
+ * that is the form `refract` takes. This drives transmission only — the
788
+ * specular Fresnel stays on the dielectric `F0 = 0.08 * Specular` mapping, as
789
+ * {@link SSSMaterial.specularF0} notes.
790
+ *
791
+ * @public
792
+ */ get ior() {
793
+ return this._ior;
794
+ }
795
+ set ior(val) {
796
+ const next = Math.min(3, Math.max(1, val ?? 1.55));
797
+ if (next !== this._ior) {
798
+ this._ior = next;
799
+ this.notifyChanged();
800
+ }
801
+ }
802
+ /**
803
+ * Multiplier on the material roughness for the narrow specular lobe.
804
+ *
805
+ * @remarks
806
+ * A direct multiplier, so 1 leaves the material roughness alone and skin's
807
+ * default 0.75 tightens the narrow lobe. The 0.5..2 range matches UE5's, whose
808
+ * halve-on-pack encoding has no counterpart here.
809
+ *
810
+ * @public
811
+ */ get roughness0() {
812
+ return this._roughness0;
813
+ }
814
+ set roughness0(val) {
815
+ const next = Math.min(2, Math.max(0.5, val ?? 0.75));
816
+ if (next !== this._roughness0) {
817
+ this._roughness0 = next;
818
+ this.notifyChanged();
819
+ }
820
+ }
821
+ /**
822
+ * Multiplier on the material roughness for the wide specular lobe.
823
+ *
824
+ * @remarks
825
+ * Same scaling as {@link SkinProfile.roughness0}; skin's default 1.3 broadens
826
+ * the second lobe, giving it a soft sheen alongside the tighter highlight.
827
+ *
828
+ * @public
829
+ */ get roughness1() {
830
+ return this._roughness1;
831
+ }
832
+ set roughness1(val) {
833
+ const next = Math.min(2, Math.max(0.5, val ?? 1.3));
834
+ if (next !== this._roughness1) {
835
+ this._roughness1 = next;
836
+ this.notifyChanged();
837
+ }
838
+ }
839
+ /** Blend between the narrow and wide specular lobes. @public */ get lobeMix() {
840
+ return this._lobeMix;
841
+ }
842
+ set lobeMix(val) {
843
+ const next = Math.min(0.9, Math.max(0.1, val ?? 0.85));
844
+ if (next !== this._lobeMix) {
845
+ this._lobeMix = next;
846
+ this.notifyChanged();
847
+ }
848
+ }
849
+ /**
850
+ * Per-channel diffusion distance, in profile space.
851
+ *
852
+ * @remarks
853
+ * Deliberately *not* scaled by {@link SkinProfile.worldUnitScale}, which UE5
854
+ * applies later in `CalculateBurleyScale`; applying it in both places would
855
+ * make the diffusion scale with its square. The result must therefore be read
856
+ * together with that factor to reach world units.
857
+ *
858
+ * @returns Mean free path scaled by the profile's distance and scatter scale.
859
+ *
860
+ * @public
861
+ */ getScatterDistance() {
862
+ const s = this._meanFreePathDistance * this._scatterScale;
863
+ return new Vector3(this._meanFreePath.x * s, this._meanFreePath.y * s, this._meanFreePath.z * s);
864
+ }
865
+ /**
866
+ * Overwrites every parameter from another profile.
867
+ *
868
+ * @remarks
869
+ * The only way to transfer a look, since profiles cannot be shared or cloned.
870
+ * The table row is untouched: it identifies the profile, not its contents.
871
+ *
872
+ * @param other - Profile to copy the parameters from.
873
+ *
874
+ * @public
875
+ */ copyFrom(other) {
876
+ this._preset = other._preset;
877
+ this._surfaceAlbedo.set(other._surfaceAlbedo);
878
+ this._meanFreePath.set(other._meanFreePath);
879
+ this._boundaryColorBleed.set(other._boundaryColorBleed);
880
+ this._transmissionTint.set(other._transmissionTint);
881
+ this._meanFreePathDistance = other._meanFreePathDistance;
882
+ this._worldUnitScale = other._worldUnitScale;
883
+ this._scatterScale = other._scatterScale;
884
+ this._extinctionScale = other._extinctionScale;
885
+ this._normalScale = other._normalScale;
886
+ this._scatteringDistribution = other._scatteringDistribution;
887
+ this._ior = other._ior;
888
+ this._roughness0 = other._roughness0;
889
+ this._roughness1 = other._roughness1;
890
+ this._lobeMix = other._lobeMix;
891
+ this.notifyChanged();
892
+ }
893
+ /**
894
+ * Releases this profile's table slot.
895
+ *
896
+ * @public
897
+ */ dispose() {
898
+ if (this._disposed) {
899
+ return;
900
+ }
901
+ this._disposed = true;
902
+ if (SkinProfile._profiles[this._id] === this) {
903
+ SkinProfile._profiles[this._id] = null;
904
+ SkinProfile.markDirty();
905
+ }
906
+ }
907
+ applyPreset(preset) {
908
+ const t = SKIN_PROFILE_TEMPLATES[preset] ?? SKIN_PROFILE_TEMPLATES.skin;
909
+ this._preset = preset;
910
+ this._surfaceAlbedo.setXYZ(t.surfaceAlbedo[0], t.surfaceAlbedo[1], t.surfaceAlbedo[2]);
911
+ this._meanFreePath.setXYZ(t.meanFreePath[0], t.meanFreePath[1], t.meanFreePath[2]);
912
+ this._boundaryColorBleed.setXYZ(t.boundaryColorBleed[0], t.boundaryColorBleed[1], t.boundaryColorBleed[2]);
913
+ this._transmissionTint.setXYZ(t.transmissionTint[0], t.transmissionTint[1], t.transmissionTint[2]);
914
+ this._meanFreePathDistance = t.meanFreePathDistance;
915
+ this._worldUnitScale = t.worldUnitScale;
916
+ this._scatterScale = t.scatterScale;
917
+ this._extinctionScale = t.extinctionScale;
918
+ this._normalScale = t.normalScale;
919
+ this._scatteringDistribution = t.scatteringDistribution;
920
+ this._ior = t.ior;
921
+ this._roughness0 = t.roughness0;
922
+ this._roughness1 = t.roughness1;
923
+ this._lobeMix = t.lobeMix;
924
+ this.notifyChanged();
925
+ }
926
+ static allocateId(profile) {
927
+ for(let i = 1; i < SKIN_PROFILE_CAPACITY; i++){
928
+ if (!this._profiles[i]) {
929
+ this._profiles[i] = profile;
930
+ this.markDirty();
931
+ return i;
932
+ }
933
+ }
934
+ throw new Error('SkinProfile limit exceeded');
935
+ }
936
+ static markDirty() {
937
+ this._tableDirty = true;
938
+ }
939
+ /**
940
+ * Flags the packed table for a rebuild and notifies materials using this
941
+ * profile so they can refresh their uniforms.
942
+ */ notifyChanged() {
943
+ SkinProfile.markDirty();
944
+ this._changeListeners.forEach((listener)=>listener());
945
+ }
946
+ static packTable(out) {
947
+ out.fill(0);
948
+ const stride = SKIN_PROFILE_COLUMNS * 4;
949
+ for(let id = 1; id < SKIN_PROFILE_CAPACITY; id++){
950
+ const p = this._profiles[id];
951
+ if (!p) {
952
+ continue;
953
+ }
954
+ const row = id * stride;
955
+ const write = (column, x, y, z, w)=>{
956
+ const o = row + column * 4;
957
+ out[o] = x;
958
+ out[o + 1] = y;
959
+ out[o + 2] = z;
960
+ out[o + 3] = w;
961
+ };
962
+ // The diffusion draws its radii from one representative channel, kept in
963
+ // the `w` of both rows as UE5 does, and evaluates all three kernels at
964
+ // those radii. The widest channel represents, so the sample distribution
965
+ // covers every channel's tail.
966
+ const d = p.getScatterDistance();
967
+ const widest = Math.max(d.x, d.y, d.z);
968
+ const albedoForSampling = widest > 0 ? [
969
+ p._surfaceAlbedo.x,
970
+ p._surfaceAlbedo.y,
971
+ p._surfaceAlbedo.z
972
+ ][[
973
+ d.x,
974
+ d.y,
975
+ d.z
976
+ ].indexOf(widest)] : p._surfaceAlbedo.x;
977
+ write(0, p._worldUnitScale, p._scatterScale, 0, 0);
978
+ write(1, p._surfaceAlbedo.x, p._surfaceAlbedo.y, p._surfaceAlbedo.z, albedoForSampling);
979
+ write(2, d.x, d.y, d.z, widest);
980
+ write(3, p._boundaryColorBleed.x, p._boundaryColorBleed.y, p._boundaryColorBleed.z, 0);
981
+ write(4, p._extinctionScale, p._normalScale, p._scatteringDistribution, 1 / Math.max(p._ior, 1e-4));
982
+ write(5, p._roughness0, p._roughness1, p._lobeMix, 0);
983
+ // The LUT columns are consecutive within the row, so the baked profile
984
+ // drops straight in after the scalar parameters.
985
+ p.writeTransmissionProfile(out, row + SKIN_PROFILE_PARAM_COLUMNS * 4);
986
+ }
987
+ }
988
+ /**
989
+ * Bakes this profile's transmission table.
990
+ *
991
+ * @remarks
992
+ * `ComputeTransmissionProfileBurley` (`BurleyNormalizedSSS.cpp:171`),
993
+ * transcribed. Each entry is the Burley diffusion profile integrated from that
994
+ * radius out to infinity,
995
+ *
996
+ * ```
997
+ * T(r) = 0.25 A (exp(-s r / L) + 3 exp(-s r / 3L))
998
+ * ```
999
+ *
1000
+ * evaluated per channel at its own `s` and `L`, times the transmission tint.
1001
+ * The surface albedo is absent — UE5 passes white for `A` — so this carries
1002
+ * only the *shape* of the falloff; the base colour is applied where the
1003
+ * transmission joins the diffuse.
1004
+ *
1005
+ * The alpha is `exp(-distance x extinctionScale)`, UE5's separate "SSSS shadow"
1006
+ * curve. The BxDF reads `.rgb` only, but storing it costs nothing.
1007
+ *
1008
+ * @param out - Destination, `4 x transmissionLutSize` floats from `offset`.
1009
+ * @param offset - Index of the first float to write.
1010
+ *
1011
+ * @public
1012
+ */ writeTransmissionProfile(out, offset = 0) {
1013
+ // The world unit scale applies to this table's *distance axis*, not to the
1014
+ // mean free paths, which UE5 likewise leaves unscaled.
1015
+ const invUnitScale = 1 / Math.max(this._worldUnitScale, 1e-4);
1016
+ const d = this.getScatterDistance();
1017
+ const l = [
1018
+ d.x * WORLD_UNITS_TO_PROFILE_MM,
1019
+ d.y * WORLD_UNITS_TO_PROFILE_MM,
1020
+ d.z * WORLD_UNITS_TO_PROFILE_MM
1021
+ ];
1022
+ const s = [
1023
+ searchLightDiffuseScalingFactor(this._surfaceAlbedo.x),
1024
+ searchLightDiffuseScalingFactor(this._surfaceAlbedo.y),
1025
+ searchLightDiffuseScalingFactor(this._surfaceAlbedo.z)
1026
+ ];
1027
+ const tint = [
1028
+ this._transmissionTint.x,
1029
+ this._transmissionTint.y,
1030
+ this._transmissionTint.z
1031
+ ];
1032
+ const offsetMM = TRANSMISSION_LUT_RADIUS_OFFSET_MM * invUnitScale;
1033
+ for(let i = 0; i < SKIN_TRANSMISSION_LUT_SIZE; i++){
1034
+ const o = offset + i * 4;
1035
+ // UE5's `bMakeLastPixelBlack`: 50 mm leaves a red tail still visible after
1036
+ // tone mapping, so forcing the last entry black is what makes anything
1037
+ // thicker than the table stop transmitting outright.
1038
+ if (i === SKIN_TRANSMISSION_LUT_SIZE - 1) {
1039
+ out[o] = 0;
1040
+ out[o + 1] = 0;
1041
+ out[o + 2] = 0;
1042
+ out[o + 3] = 0;
1043
+ break;
1044
+ }
1045
+ // Note the divisor is the table size, not `size - 1`: the last entry is
1046
+ // blacked out anyway, so UE5 spends the axis on the entries that survive.
1047
+ const distanceMM = i / SKIN_TRANSMISSION_LUT_SIZE * TRANSMISSION_LUT_MAX_DISTANCE_MM * invUnitScale;
1048
+ const r = distanceMM + offsetMM;
1049
+ for(let c = 0; c < 3; c++){
1050
+ out[o + c] = l[c] > 0 ? 0.25 * (Math.exp(-s[c] * r / l[c]) + 3 * Math.exp(-s[c] * r / (3 * l[c]))) * tint[c] : 0;
1051
+ }
1052
+ out[o + 3] = Math.exp(-distanceMM * this._extinctionScale);
1053
+ }
1054
+ }
1055
+ }
1056
+
1057
+ export { SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH, SKIN_OPTICAL_DEPTH_PER_WORLD_UNIT, SKIN_TRANSMISSION_NO_DATA_ENCODING, SKIN_TRANSMISSION_OPTICAL_DEPTH_BIAS, SKIN_TRANSMISSION_OPTICAL_DEPTH_FLOOR, SkinProfile };
1058
+ //# sourceMappingURL=skinprofile.js.map