@zephyr3d/scene 0.9.25 → 0.9.27

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 (299) hide show
  1. package/dist/animation/animationset.js +92 -30
  2. package/dist/animation/animationset.js.map +1 -1
  3. package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
  4. package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
  5. package/dist/animation/joint_dynamics/controller.js +7 -5
  6. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  7. package/dist/animation/joint_dynamics/convex_collider.js +320 -0
  8. package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
  9. package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
  10. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  11. package/dist/animation/morphtrack.js +3 -4
  12. package/dist/animation/morphtrack.js.map +1 -1
  13. package/dist/animation/skeleton.js +79 -25
  14. package/dist/animation/skeleton.js.map +1 -1
  15. package/dist/animation/spring/multi_chain_spring_system.js +577 -179
  16. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  17. package/dist/animation/spring/spring_collider.js +4 -3
  18. package/dist/animation/spring/spring_collider.js.map +1 -1
  19. package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
  20. package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
  21. package/dist/animation/spring/spring_particle.js +2 -0
  22. package/dist/animation/spring/spring_particle.js.map +1 -1
  23. package/dist/animation/spring/spring_system.js +11 -3
  24. package/dist/animation/spring/spring_system.js.map +1 -1
  25. package/dist/animation/spring_modifier.js +10 -0
  26. package/dist/animation/spring_modifier.js.map +1 -1
  27. package/dist/app/engine.js +53 -43
  28. package/dist/app/engine.js.map +1 -1
  29. package/dist/app/scriptingsystem.js +119 -6
  30. package/dist/app/scriptingsystem.js.map +1 -1
  31. package/dist/app/scriptregistry.js +3 -2
  32. package/dist/app/scriptregistry.js.map +1 -1
  33. package/dist/asset/assetmanager.js +284 -67
  34. package/dist/asset/assetmanager.js.map +1 -1
  35. package/dist/asset/loaders/zhair/zhair_format.js +288 -0
  36. package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
  37. package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
  38. package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
  39. package/dist/asset/model.js +184 -69
  40. package/dist/asset/model.js.map +1 -1
  41. package/dist/camera/camera.js +268 -44
  42. package/dist/camera/camera.js.map +1 -1
  43. package/dist/camera/perspectivecamera.js +3 -1
  44. package/dist/camera/perspectivecamera.js.map +1 -1
  45. package/dist/index.d.ts +9044 -5139
  46. package/dist/index.js +17 -6
  47. package/dist/index.js.map +1 -1
  48. package/dist/material/eye.js +932 -0
  49. package/dist/material/eye.js.map +1 -0
  50. package/dist/material/hair.js +917 -0
  51. package/dist/material/hair.js.map +1 -0
  52. package/dist/material/hairstrand.js +842 -0
  53. package/dist/material/hairstrand.js.map +1 -0
  54. package/dist/material/hairstrand_data.js +162 -0
  55. package/dist/material/hairstrand_data.js.map +1 -0
  56. package/dist/material/lambert.js +5 -2
  57. package/dist/material/lambert.js.map +1 -1
  58. package/dist/material/material.js +48 -10
  59. package/dist/material/material.js.map +1 -1
  60. package/dist/material/meshmaterial.js +120 -19
  61. package/dist/material/meshmaterial.js.map +1 -1
  62. package/dist/material/mixins/albedocolor.js +1 -1
  63. package/dist/material/mixins/albedocolor.js.map +1 -1
  64. package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
  65. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  66. package/dist/material/mixins/lightmodel/lambert.js +5 -4
  67. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  68. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
  69. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  70. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
  71. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  72. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
  73. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  74. package/dist/material/mixins/lit.js +106 -19
  75. package/dist/material/mixins/lit.js.map +1 -1
  76. package/dist/material/mixins/pbr/common.js +4 -4
  77. package/dist/material/mixins/pbr/common.js.map +1 -1
  78. package/dist/material/mixins/texture.js +48 -11
  79. package/dist/material/mixins/texture.js.map +1 -1
  80. package/dist/material/mtoon.js +6 -5
  81. package/dist/material/mtoon.js.map +1 -1
  82. package/dist/material/pbrblueprint.js +146 -28
  83. package/dist/material/pbrblueprint.js.map +1 -1
  84. package/dist/material/pbrblueprint_instance.js +501 -0
  85. package/dist/material/pbrblueprint_instance.js.map +1 -0
  86. package/dist/material/pbrmr.js +15 -16
  87. package/dist/material/pbrmr.js.map +1 -1
  88. package/dist/material/shader/helper.js +734 -23
  89. package/dist/material/shader/helper.js.map +1 -1
  90. package/dist/material/shader/terrain_blend.js +55 -0
  91. package/dist/material/shader/terrain_blend.js.map +1 -0
  92. package/dist/material/skin.js +192 -32
  93. package/dist/material/skin.js.map +1 -1
  94. package/dist/material/spriteblueprint.js +16 -1
  95. package/dist/material/spriteblueprint.js.map +1 -1
  96. package/dist/material/subsurfaceprofile.js +86 -23
  97. package/dist/material/subsurfaceprofile.js.map +1 -1
  98. package/dist/material/water.js +2059 -72
  99. package/dist/material/water.js.map +1 -1
  100. package/dist/posteffect/bloom.js +167 -7
  101. package/dist/posteffect/bloom.js.map +1 -1
  102. package/dist/posteffect/coloradjust.js +2 -1
  103. package/dist/posteffect/coloradjust.js.map +1 -1
  104. package/dist/posteffect/compositor.js.map +1 -1
  105. package/dist/posteffect/fxaa.js +2 -1
  106. package/dist/posteffect/fxaa.js.map +1 -1
  107. package/dist/posteffect/grayscale.js +3 -1
  108. package/dist/posteffect/grayscale.js.map +1 -1
  109. package/dist/posteffect/motionblur.js +664 -56
  110. package/dist/posteffect/motionblur.js.map +1 -1
  111. package/dist/posteffect/posteffect.js +12 -1
  112. package/dist/posteffect/posteffect.js.map +1 -1
  113. package/dist/posteffect/sao.js +46 -44
  114. package/dist/posteffect/sao.js.map +1 -1
  115. package/dist/posteffect/skinsss.js +368 -69
  116. package/dist/posteffect/skinsss.js.map +1 -1
  117. package/dist/posteffect/ssgi.js +16 -9
  118. package/dist/posteffect/ssgi.js.map +1 -1
  119. package/dist/posteffect/ssr.js +3 -0
  120. package/dist/posteffect/ssr.js.map +1 -1
  121. package/dist/posteffect/sss.js +121 -36
  122. package/dist/posteffect/sss.js.map +1 -1
  123. package/dist/posteffect/taa.js.map +1 -1
  124. package/dist/posteffect/tonemap.js +44 -1
  125. package/dist/posteffect/tonemap.js.map +1 -1
  126. package/dist/render/abuffer_oit.js +8 -2
  127. package/dist/render/abuffer_oit.js.map +1 -1
  128. package/dist/render/clipmap.js +156 -5
  129. package/dist/render/clipmap.js.map +1 -1
  130. package/dist/render/cull_visitor.js +20 -0
  131. package/dist/render/cull_visitor.js.map +1 -1
  132. package/dist/render/depthpass.js +36 -2
  133. package/dist/render/depthpass.js.map +1 -1
  134. package/dist/render/drawable_mixin.js +26 -1
  135. package/dist/render/drawable_mixin.js.map +1 -1
  136. package/dist/render/dualdepthpeeling_oit.js +3 -2
  137. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  138. package/dist/render/envlight.js +1 -1
  139. package/dist/render/envlight.js.map +1 -1
  140. package/dist/render/fbm_wavegenerator.js +6 -3
  141. package/dist/render/fbm_wavegenerator.js.map +1 -1
  142. package/dist/render/fft_wavegenerator.js +57 -15
  143. package/dist/render/fft_wavegenerator.js.map +1 -1
  144. package/dist/render/gerstner_wavegenerator.js +130 -20
  145. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  146. package/dist/render/globalbindgroup_allocator.js +3 -0
  147. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  148. package/dist/render/hzb.js +80 -22
  149. package/dist/render/hzb.js.map +1 -1
  150. package/dist/render/lightpass.js +6 -3
  151. package/dist/render/lightpass.js.map +1 -1
  152. package/dist/render/oit.js +29 -0
  153. package/dist/render/oit.js.map +1 -0
  154. package/dist/render/render_queue.js +39 -0
  155. package/dist/render/render_queue.js.map +1 -1
  156. package/dist/render/renderer.js +4 -0
  157. package/dist/render/renderer.js.map +1 -1
  158. package/dist/render/rendergraph/blackboard.js +1 -0
  159. package/dist/render/rendergraph/blackboard.js.map +1 -1
  160. package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
  161. package/dist/render/rendergraph/executor.js +2 -0
  162. package/dist/render/rendergraph/executor.js.map +1 -1
  163. package/dist/render/rendergraph/forward_plus_builder.js +379 -10
  164. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  165. package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
  166. package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
  167. package/dist/render/rendergraph/history_resources.js +8 -1
  168. package/dist/render/rendergraph/history_resources.js.map +1 -1
  169. package/dist/render/rendergraph/rendergraph.js.map +1 -1
  170. package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
  171. package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
  172. package/dist/render/rendergraph/types.js.map +1 -1
  173. package/dist/render/shadow_mask_pass.js +73 -8
  174. package/dist/render/shadow_mask_pass.js.map +1 -1
  175. package/dist/render/sky.js +166 -126
  176. package/dist/render/sky.js.map +1 -1
  177. package/dist/render/underwater.js +624 -0
  178. package/dist/render/underwater.js.map +1 -0
  179. package/dist/render/water_caustics.js +1380 -0
  180. package/dist/render/water_caustics.js.map +1 -0
  181. package/dist/render/weightedblended_oit.js +3 -2
  182. package/dist/render/weightedblended_oit.js.map +1 -1
  183. package/dist/scene/basesprite.js +8 -0
  184. package/dist/scene/basesprite.js.map +1 -1
  185. package/dist/scene/batchgroup.js +3 -0
  186. package/dist/scene/batchgroup.js.map +1 -1
  187. package/dist/scene/environment.js +4 -1
  188. package/dist/scene/environment.js.map +1 -1
  189. package/dist/scene/hair.js +963 -0
  190. package/dist/scene/hair.js.map +1 -0
  191. package/dist/scene/light.js +5 -1
  192. package/dist/scene/light.js.map +1 -1
  193. package/dist/scene/mesh.js +481 -47
  194. package/dist/scene/mesh.js.map +1 -1
  195. package/dist/scene/meshdrawable.js +6 -0
  196. package/dist/scene/meshdrawable.js.map +1 -1
  197. package/dist/scene/msdftext.js +3 -0
  198. package/dist/scene/msdftext.js.map +1 -1
  199. package/dist/scene/msdftextsprite.js +3 -0
  200. package/dist/scene/msdftextsprite.js.map +1 -1
  201. package/dist/scene/particlesys.js +6 -0
  202. package/dist/scene/particlesys.js.map +1 -1
  203. package/dist/scene/scene.js +3 -0
  204. package/dist/scene/scene.js.map +1 -1
  205. package/dist/scene/scene_node.js +6 -1
  206. package/dist/scene/scene_node.js.map +1 -1
  207. package/dist/scene/terrain-cm/grass.js +3 -0
  208. package/dist/scene/terrain-cm/grass.js.map +1 -1
  209. package/dist/scene/terrain-cm/grassmaterial.js +3 -0
  210. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  211. package/dist/scene/terrain-cm/terrain-cm.js +6 -0
  212. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  213. package/dist/scene/terrain-cm/texture_cache.js +309 -0
  214. package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
  215. package/dist/scene/water.js +546 -4
  216. package/dist/scene/water.js.map +1 -1
  217. package/dist/shaders/atmosphere.js.map +1 -1
  218. package/dist/shaders/diffusion.js +54 -0
  219. package/dist/shaders/diffusion.js.map +1 -0
  220. package/dist/shaders/shadow.js +33 -21
  221. package/dist/shaders/shadow.js.map +1 -1
  222. package/dist/shaders/temporal.js +71 -33
  223. package/dist/shaders/temporal.js.map +1 -1
  224. package/dist/shaders/water.js +22 -19
  225. package/dist/shaders/water.js.map +1 -1
  226. package/dist/shaders/water_medium.js +56 -0
  227. package/dist/shaders/water_medium.js.map +1 -0
  228. package/dist/shadow/dom.js +546 -0
  229. package/dist/shadow/dom.js.map +1 -0
  230. package/dist/shadow/pcf_opt.js +3 -5
  231. package/dist/shadow/pcf_opt.js.map +1 -1
  232. package/dist/shadow/pcf_pd.js +1 -1
  233. package/dist/shadow/pcf_pd.js.map +1 -1
  234. package/dist/shadow/receiver_bias.js +41 -0
  235. package/dist/shadow/receiver_bias.js.map +1 -0
  236. package/dist/shadow/shader.js +20 -60
  237. package/dist/shadow/shader.js.map +1 -1
  238. package/dist/shadow/shadow_impl.js +89 -0
  239. package/dist/shadow/shadow_impl.js.map +1 -1
  240. package/dist/shadow/shadow_region.js +1 -1
  241. package/dist/shadow/shadow_region.js.map +1 -1
  242. package/dist/shadow/shadowmapper.js +150 -37
  243. package/dist/shadow/shadowmapper.js.map +1 -1
  244. package/dist/shapes/shape.js +11 -2
  245. package/dist/shapes/shape.js.map +1 -1
  246. package/dist/shapes/sphere.js +50 -9
  247. package/dist/shapes/sphere.js.map +1 -1
  248. package/dist/utility/blueprint/common/constants.js +112 -2
  249. package/dist/utility/blueprint/common/constants.js.map +1 -1
  250. package/dist/utility/blueprint/material/func.js +142 -49
  251. package/dist/utility/blueprint/material/func.js.map +1 -1
  252. package/dist/utility/blueprint/material/ir.js +490 -25
  253. package/dist/utility/blueprint/material/ir.js.map +1 -1
  254. package/dist/utility/blueprint/material/texture.js +898 -4
  255. package/dist/utility/blueprint/material/texture.js.map +1 -1
  256. package/dist/utility/serialization/manager.js +434 -101
  257. package/dist/utility/serialization/manager.js.map +1 -1
  258. package/dist/utility/serialization/scene/animation.js +401 -5
  259. package/dist/utility/serialization/scene/animation.js.map +1 -1
  260. package/dist/utility/serialization/scene/batch.js +4 -0
  261. package/dist/utility/serialization/scene/batch.js.map +1 -1
  262. package/dist/utility/serialization/scene/camera.js +186 -26
  263. package/dist/utility/serialization/scene/camera.js.map +1 -1
  264. package/dist/utility/serialization/scene/common.js +26 -5
  265. package/dist/utility/serialization/scene/common.js.map +1 -1
  266. package/dist/utility/serialization/scene/hair.js +1141 -0
  267. package/dist/utility/serialization/scene/hair.js.map +1 -0
  268. package/dist/utility/serialization/scene/light.js +84 -4
  269. package/dist/utility/serialization/scene/light.js.map +1 -1
  270. package/dist/utility/serialization/scene/material.js +1867 -166
  271. package/dist/utility/serialization/scene/material.js.map +1 -1
  272. package/dist/utility/serialization/scene/mesh.js +231 -14
  273. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  274. package/dist/utility/serialization/scene/node.js +141 -7
  275. package/dist/utility/serialization/scene/node.js.map +1 -1
  276. package/dist/utility/serialization/scene/particle.js +4 -0
  277. package/dist/utility/serialization/scene/particle.js.map +1 -1
  278. package/dist/utility/serialization/scene/primitive.js +18 -0
  279. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  280. package/dist/utility/serialization/scene/sprite.js +4 -0
  281. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  282. package/dist/utility/serialization/scene/terrain.js +4 -0
  283. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  284. package/dist/utility/serialization/scene/text.js +4 -0
  285. package/dist/utility/serialization/scene/text.js.map +1 -1
  286. package/dist/utility/serialization/scene/water.js +1105 -21
  287. package/dist/utility/serialization/scene/water.js.map +1 -1
  288. package/dist/utility/serialization/types.js.map +1 -1
  289. package/dist/utility/shprojector.js +9 -8
  290. package/dist/utility/shprojector.js.map +1 -1
  291. package/dist/values.js +49 -2
  292. package/dist/values.js.map +1 -1
  293. package/package.json +3 -3
  294. package/dist/render/rendergraph/frame_graph_context.js +0 -41
  295. package/dist/render/rendergraph/frame_graph_context.js.map +0 -1
  296. package/dist/shaders/ssgi.js +0 -63
  297. package/dist/shaders/ssgi.js.map +0 -1
  298. package/dist/shaders/ssgi_reprojection.js +0 -118
  299. package/dist/shaders/ssgi_reprojection.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"material.js","sources":["../../src/material/material.ts"],"sourcesContent":["import type { AbstractDevice, BindGroup, GPUProgram, RenderStateSet } from '@zephyr3d/device';\r\nimport { ProgramBuilder } from '@zephyr3d/device';\r\nimport type { Primitive } from '../render/primitive';\r\nimport type { DrawContext } from '../render/drawable';\r\nimport { QUEUE_OPAQUE } from '../values';\r\nimport { RenderBundleWrapper } from '../render/renderbundle_wrapper';\r\nimport { Disposable } from '@zephyr3d/base';\r\nimport type { Clonable, IDisposable, Nullable } from '@zephyr3d/base';\r\nimport { getEngine } from '../app/api';\r\n\r\ntype MaterialState = {\r\n program: Nullable<GPUProgram>;\r\n bindGroup: Nullable<BindGroup>;\r\n bindGroupTag: string;\r\n renderStateSet: RenderStateSet;\r\n materialTag: number;\r\n};\r\n\r\n/**\r\n * Base class for all materials.\r\n *\r\n * Responsibilities:\r\n * - Defines a multi-pass rendering interface (`numPasses`, `apply`, `bind`, `draw`, `drawPrimitive`).\r\n * - Builds and caches GPU shader programs per pass and per-render-context hash.\r\n * - Manages a per-material bind group (typically at index 2) for uniforms and resources.\r\n * - Updates render states per pass (`updateRenderStates`) and uploads uniforms (`_applyUniforms`).\r\n * - Tracks \"option\" changes that affect shader variant hashing and render bundles.\r\n *\r\n * Caching and hashing:\r\n * - `createHash(pass)` produces a stable hash representing shader variant options for a pass.\r\n * Override `_createHash()` in subclasses to encode feature toggles (defines, keywords, macros).\r\n * - The global hash used to key `MaterialState` also includes `ctx.materialFlags` and\r\n * `ctx.shaderVariantHash` when provided, allowing context-sensitive variants without\r\n * coupling shader programs to runtime texture identities.\r\n * - GPU programs are additionally memoized in a global static `_programCache` across materials\r\n * by `constructor.name` + hash, to avoid recompilation of identical variants.\r\n *\r\n * Bind groups and uniforms:\r\n * - If a program declares a bind group layout at index 2 (i.e., `bindGroupLayouts.length > 2`),\r\n * `apply()` will allocate the group and keep it in the state. Subclasses should fill it in\r\n * `_applyUniforms()`.\r\n * - `applyUniforms()` only calls `_applyUniforms()` when `needUpdate` is true, based on\r\n * `_optionTag` changes (see `optionChanged()`).\r\n *\r\n * Lifecycle:\r\n * - Constructed materials register a persistent ID into a global registry for serialization.\r\n * - `apply(ctx)` prepares all passes: creates/gets state, programs, bind groups, render states,\r\n * and uploads uniforms as needed.\r\n * - `bind(device, pass)` binds the program, bind group (index 2), and render states.\r\n * - `draw(primitive, ctx, numInstances)` runs all passes, calling `bind()` and `drawPrimitive()`.\r\n * - `onDispose()` releases bind groups and registry entries.\r\n *\r\n * Extending:\r\n * - Override `_createProgram(pb, ctx, pass)` to build a shader.\r\n * - Override `_applyUniforms(bindGroup, ctx, pass)` to upload uniforms and resources.\r\n * - Override `updateRenderStates(pass, renderStates, ctx)` to set depth, blend, cull, etc.\r\n * - Override `_createHash()` to encode options that affect program compilation.\r\n * - Override `supportLighting`, `supportInstancing`, `isTransparentPass`, `getQueueType`, etc.\r\n *\r\n * Thread-safety:\r\n * - Intended for main-thread use in a renderer driving WebGPU/WebGL-like devices.\r\n *\r\n * @public\r\n */\r\nexport class Material extends Disposable implements Clonable<Material>, IDisposable {\r\n /**\r\n * Monotonic instance ID counter.\r\n * @internal\r\n */\r\n private static _nextId = 0;\r\n /**\r\n * Global program cache keyed by global hash\r\n */\r\n private static _programCache: { [hash: string]: GPUProgram } = {};\r\n /**\r\n * Per-material state cache keyed by global hash (material + context + pass).\r\n * @internal\r\n */\r\n private _states: { [hash: string]: MaterialState };\r\n /**\r\n * Number of rendering passes.\r\n * Subclasses can increase this to implement multi-pass rendering.\r\n * @internal\r\n */\r\n protected _numPasses: number;\r\n /**\r\n * Per-pass hash cached results. Length scales with `numPasses`.\r\n * @internal\r\n */\r\n protected _hash: Nullable<string>[];\r\n /**\r\n * Incremented each time options change (via `optionChanged`), used to decide whether\r\n * uniforms need update on next `apply()`.\r\n * @internal\r\n */\r\n private _optionTag: number;\r\n /**\r\n * Unique runtime instance ID.\r\n * @internal\r\n */\r\n protected readonly _id: number;\r\n /**\r\n * Latest computed global hash per pass, set during `apply()`, read in `bind()`.\r\n * @internal\r\n */\r\n private _currentHash: string[];\r\n /**\r\n * Incremented when material states affecting render bundles change (e.g., bind group id changes\r\n * or `optionChanged(true)`), used to trigger re-recording of render bundles.\r\n * @internal\r\n */\r\n private _changeTag: number;\r\n /**\r\n * Unique program id counter for naming compiled programs.\r\n * @internal\r\n */\r\n private _nextProgramId = 0;\r\n /**\r\n * Create a new material instance.\r\n *\r\n * - Initializes one pass by default.\r\n * - Prepares per-pass hash storage and change tracking.\r\n * - Registers a persistent ID in the global registry.\r\n */\r\n constructor() {\r\n super();\r\n this._id = ++Material._nextId;\r\n this._nextProgramId = 0;\r\n this._states = {};\r\n this._numPasses = 1;\r\n this._hash = [null];\r\n this._optionTag = 0;\r\n this._changeTag = 0;\r\n this._currentHash = [];\r\n }\r\n /**\r\n * Create a shallow clone of this material.\r\n *\r\n * Note: The base implementation returns a base `Material`. Subclasses should\r\n * override to return their own type and copy custom fields.\r\n */\r\n clone() {\r\n const other = new Material();\r\n other.copyFrom(this);\r\n return other;\r\n }\r\n /**\r\n * Copy basic properties from another material.\r\n *\r\n * Disposes existing bind groups/states, copies `numPasses`. Subclasses should\r\n * extend this to copy their own fields and call `optionChanged(true)` if\r\n * shader-affecting options differ.\r\n *\r\n * @param other - Source material.\r\n */\r\n copyFrom(other: this) {\r\n this.clearCache();\r\n this._numPasses = other._numPasses;\r\n getEngine().resourceManager.setAssetId(this, getEngine().resourceManager.getAssetId(other.coreMaterial));\r\n }\r\n /**\r\n * Incremented when the material’s GPU-relevant state changes and render bundles\r\n * may need to be rebuilt.\r\n */\r\n get changeTag() {\r\n return this._changeTag;\r\n }\r\n /**\r\n * Runtime-unique numeric identifier for the material instance.\r\n */\r\n get instanceId() {\r\n return this._id;\r\n }\r\n /**\r\n * Number of rendering passes this material uses.\r\n *\r\n * Increasing this will expand the per-pass hash cache; make sure to implement\r\n * `createHash(pass)`, `_createProgram(pb, ctx, pass)`, and `updateRenderStates(pass, ...)`\r\n * accordingly for each pass.\r\n */\r\n get numPasses() {\r\n return this._numPasses;\r\n }\r\n set numPasses(val) {\r\n while (this._hash.length < val) {\r\n this._hash.push(null);\r\n }\r\n this._numPasses = val;\r\n }\r\n /**\r\n * Get or compute the per-pass shader hash used for program caching.\r\n *\r\n * Calls `createHash(pass)` lazily and caches the result.\r\n * @internal\r\n */\r\n protected getHash(pass: number) {\r\n if (this._hash[pass] === null) {\r\n this._hash[pass] = this.createHash(pass);\r\n }\r\n return this._hash[pass];\r\n }\r\n /**\r\n * Return the queue type to which this material belongs.\r\n *\r\n * Override this in transparent or special materials (e.g., post-process).\r\n */\r\n getQueueType(): number {\r\n return QUEUE_OPAQUE;\r\n }\r\n /**\r\n * Whether the given pass is transparent.\r\n *\r\n * Used to place draw calls into appropriate render queues and set blending states.\r\n */\r\n isTransparentPass(_pass: number): boolean {\r\n return false;\r\n }\r\n /**\r\n * Whether this material's shading is affected by scene lights.\r\n *\r\n * Override and return `false` for unlit materials.\r\n */\r\n supportLighting(): boolean {\r\n return true;\r\n }\r\n /**\r\n * Whether this material supports hardware instancing.\r\n *\r\n * Override and return `false` if per-instance data is not supported in the shader.\r\n */\r\n supportInstancing(): boolean {\r\n return true;\r\n }\r\n /** Returns true if this is a instance of material */\r\n isBatchable(): boolean {\r\n return false;\r\n }\r\n\r\n /**\r\n * Whether this material requires the scene color texture (e.g., for refraction).\r\n */\r\n needSceneColor(): boolean {\r\n return false;\r\n }\r\n /**\r\n * Whether this material requires the linear scene depth texture (e.g., for depth-aware effects).\r\n */\r\n needSceneDepth(): boolean {\r\n return false;\r\n }\r\n /**\r\n * Create a material instance (instance-uniform-driven variant).\r\n *\r\n * Base returns `null`. Subclasses that support instancing can return a lightweight instance.\r\n */\r\n createInstance(): this {\r\n throw new Error('Abstract function call');\r\n }\r\n /**\r\n * Returns the core material that owns GPU state.\r\n *\r\n * Instances may delegate to a shared core to reuse compiled programs and caches.\r\n * @public\r\n */\r\n get coreMaterial(): this {\r\n return this;\r\n }\r\n /**\r\n * Prepare the material for drawing across all passes for the given draw context.\r\n *\r\n * Steps per pass:\r\n * - Compute global hash (material variant + context).\r\n * - Retrieve or build the GPU program, cache in `_programCache`.\r\n * - Create per-material bind group (index 2) if the program exposes it.\r\n * - Update uniforms if `_optionTag` indicates changes since last apply.\r\n * - Update and cache render states for the pass.\r\n * - Detect bind group GPU ID changes to bump `changeTag` and notify `RenderBundleWrapper`.\r\n *\r\n * @param ctx - Draw context (device, flags, pass hash, instance data, etc.).\r\n * @returns `true` if successful; `false` if any pass lacks a valid program.\r\n */\r\n apply(ctx: DrawContext) {\r\n for (let pass = 0; pass < this._numPasses; pass++) {\r\n const hash = this.calcGlobalHash(ctx, pass);\r\n let state = this._states[hash];\r\n if (!state) {\r\n let program = Material._programCache[hash];\r\n if (!program) {\r\n program = this.createProgram(ctx, pass) ?? null;\r\n if (!program) {\r\n console.error('[Material.apply] program build failed', {\r\n material: this.constructor.name,\r\n pass,\r\n renderPassType: ctx.renderPass?.type ?? null,\r\n materialFlags: ctx.materialFlags,\r\n queue: ctx.queue,\r\n lightBlending: ctx.lightBlending,\r\n hasOIT: !!ctx.oit,\r\n hasShadowLight: !!ctx.currentShadowLight,\r\n renderPassHash: ctx.renderPassHash\r\n });\r\n state = {\r\n program: null,\r\n bindGroup: null,\r\n bindGroupTag: '',\r\n renderStateSet: ctx.device.createRenderStateSet(),\r\n materialTag: -1\r\n };\r\n this._states[hash] = state;\r\n return false;\r\n }\r\n program.name = `@${this.constructor.name}_program_${this._nextProgramId++}`;\r\n Material._programCache[hash] = program;\r\n }\r\n const bindGroup =\r\n program.bindGroupLayouts.length > 2\r\n ? ctx.device.createBindGroup(program.bindGroupLayouts[2])\r\n : null;\r\n state = {\r\n program,\r\n bindGroup,\r\n bindGroupTag: bindGroup?.getVersion() ?? '',\r\n renderStateSet: ctx.device.createRenderStateSet(),\r\n materialTag: -1\r\n };\r\n this._states[hash] = state;\r\n }\r\n if (!state.program) {\r\n return false;\r\n }\r\n if (state.bindGroup) {\r\n this.applyUniforms(state.bindGroup, ctx, state.materialTag !== this._optionTag, pass);\r\n }\r\n state.materialTag = this._optionTag;\r\n this.updateRenderStates(pass, state.renderStateSet, ctx);\r\n this._currentHash[pass] = hash;\r\n if (state.bindGroup) {\r\n const id = state.bindGroup.getVersion();\r\n if (id !== state.bindGroupTag) {\r\n state.bindGroupTag = id;\r\n this._changeTag++;\r\n RenderBundleWrapper.materialChanged(this.coreMaterial);\r\n }\r\n }\r\n }\r\n return true;\r\n }\r\n /**\r\n * Bind the program, bind group, and render states for the specified pass.\r\n *\r\n * Must be called after `apply(ctx)` for the same pass.\r\n *\r\n * @param device - Rendering device.\r\n * @param pass - Pass index to bind.\r\n * @returns `true` on success; `false` if state or program missing.\r\n * @internal\r\n */\r\n bind(device: AbstractDevice, pass: number) {\r\n const hash = this._currentHash[pass];\r\n const state = this._states[hash];\r\n if (!state) {\r\n console.error('Material.bind() failed: state not found');\r\n return false;\r\n }\r\n if (!state.program) {\r\n return false;\r\n }\r\n device.setProgram(state.program);\r\n if (state.bindGroup) {\r\n device.setBindGroup(2, state.bindGroup);\r\n }\r\n device.setRenderStates(state.renderStateSet);\r\n return true;\r\n }\r\n /**\r\n * Compute the global hash for the given pass and draw context.\r\n *\r\n * Includes:\r\n * - Per-pass material hash from `getHash(pass)`.\r\n * - `ctx.materialFlags` for context-dependent toggles.\r\n * - `ctx.shaderVariantHash` for shader layout/feature variants, falling back\r\n * to `ctx.renderPassHash` for passes without a shader-only hash.\r\n * @internal\r\n */\r\n private calcGlobalHash(ctx: DrawContext, pass: number) {\r\n const shaderVariantHash = ctx.shaderVariantHash ?? ctx.renderPassHash;\r\n return `${this.getHash(pass)}:${ctx.materialFlags}:${shaderVariantHash}:${\r\n ctx.drawEnvLight ? 1 : 0\r\n }:${ctx.currentShadowLight ? 1 : 0}:${ctx.lightBlending ? 1 : 0}`;\r\n }\r\n /**\r\n * Draw a primitive for all passes using this material.\r\n *\r\n * Calls `bind()` then `drawPrimitive()` per pass. If `numInstances` is zero,\r\n * and `ctx.instanceData` exists, uses `ctx.instanceData.numInstances`.\r\n *\r\n * @param primitive - Geometry to draw.\r\n * @param ctx - Draw context.\r\n * @param numInstances - Instance count; 0 means auto-detect from context.\r\n * @internal\r\n */\r\n draw(primitive: Primitive, ctx: DrawContext, numInstances = 0) {\r\n for (let pass = 0; pass < this._numPasses; pass++) {\r\n this.bind(ctx.device, pass);\r\n this.drawPrimitive(pass, primitive, ctx, numInstances);\r\n }\r\n }\r\n /**\r\n * Conditionally update uniforms/resources into the material bind group.\r\n *\r\n * Delegates to `_applyUniforms()` when `needUpdate` is true (based on `_optionTag` check).\r\n *\r\n * @param bindGroup - Material bind group at index 2 (may be `null` if program has no layout).\r\n * @param ctx - Draw context.\r\n * @param needUpdate - Whether uniforms need to be refreshed.\r\n * @param pass - Pass index.\r\n */\r\n applyUniforms(bindGroup: BindGroup, ctx: DrawContext, needUpdate: boolean, pass: number) {\r\n if (needUpdate) {\r\n this._applyUniforms(bindGroup, ctx, pass);\r\n }\r\n }\r\n /**\r\n * Notify the material that some option changed.\r\n *\r\n * - Always increments `_optionTag`. This will trigger uniforms update on next `apply()`.\r\n * - If `changeHash` is true, clears per-pass hashes so programs/states will be rebuilt,\r\n * increments `_changeTag`, and notifies `RenderBundleWrapper` to regenerate recorded bundles.\r\n *\r\n * @param changeHash - Set true if the change affects shader compilation or render states.\r\n * @internal\r\n */\r\n optionChanged(changeHash: boolean) {\r\n this._optionTag++;\r\n if (changeHash) {\r\n for (let i = 0; i < this._numPasses; i++) {\r\n this._hash[i] = null;\r\n }\r\n this._changeTag++;\r\n RenderBundleWrapper.materialChanged(this.coreMaterial);\r\n }\r\n }\r\n clearCache() {\r\n for (const k in this._states) {\r\n this._states[k]?.bindGroup?.dispose();\r\n Material._programCache[k]?.dispose();\r\n delete Material._programCache[k];\r\n }\r\n this._states = {};\r\n }\r\n /**\r\n * Convert a pass index to a hash seed string.\r\n *\r\n * Subclasses may override to encode per-pass role (e.g., \"depth\", \"forward\", \"shadow\").\r\n *\r\n * @param pass - Pass number.\r\n * @returns String used when building full hash.\r\n */\r\n passToHash(pass: number) {\r\n return String(pass);\r\n }\r\n /**\r\n * Build the material hash for a pass (excluding context-dependent parts).\r\n *\r\n * Default formula: `${constructor.name}|${pass}|${_createHash()}`\r\n *\r\n * @param pass - Pass number.\r\n * @returns Hash string used in program caching.\r\n * @internal\r\n */\r\n createHash(pass: number) {\r\n return `${this.constructor.name}|${this.passToHash(pass)}|${this._createHash()}`;\r\n }\r\n /**\r\n * Issue the actual draw call for a pass.\r\n *\r\n * Override for custom per-pass draw behavior if necessary. The default implementation:\r\n * - Draws instanced if `numInstances > 0`.\r\n * - Else uses `ctx.instanceData.numInstances` if available.\r\n * - Else issues a non-instanced draw.\r\n *\r\n * @param pass - Pass number.\r\n * @param primitive - Primitive to draw.\r\n * @param ctx - Draw context.\r\n * @param numInstances - Explicit instance count (0 = auto).\r\n */\r\n drawPrimitive(pass: number, primitive: Primitive, ctx: DrawContext, numInstances: number) {\r\n if (numInstances > 0) {\r\n primitive.drawInstanced(numInstances);\r\n } else if (ctx.instanceData) {\r\n primitive.drawInstanced(ctx.instanceData.numInstances);\r\n } else {\r\n primitive.draw();\r\n }\r\n }\r\n /**\r\n * Dispose the material and release GPU-side resource references.\r\n *\r\n * - Unregisters from the global registry.\r\n * - Disposes the per-material bind groups kept in `_states`.\r\n */\r\n protected onDispose() {\r\n super.onDispose();\r\n this.clearCache();\r\n }\r\n /**\r\n * Build the GPU program for a pass.\r\n *\r\n * Default creates a `ProgramBuilder` and calls `_createProgram(pb, ctx, pass)`.\r\n * Subclasses should override `_createProgram` instead of this method unless\r\n * they need to replace builder instantiation.\r\n *\r\n * @param ctx - Draw context.\r\n * @param pass - Pass number.\r\n * @returns The compiled `GPUProgram`, or `null` on failure.\r\n * @internal\r\n */\r\n protected createProgram(ctx: DrawContext, pass: number) {\r\n const pb = new ProgramBuilder(ctx.device);\r\n return this._createProgram(pb, ctx, pass);\r\n }\r\n /**\r\n * Create and compile the shader program for this material/pass.\r\n *\r\n * Implement in subclasses:\r\n * - Define shader stages, entry points, macros/defines, and resource layouts.\r\n * - Return a compiled `GPUProgram`.\r\n *\r\n * @param pb - Program builder.\r\n * @param ctx - Draw context.\r\n * @param _pass - Pass number.\r\n * @returns The created program, or `null` on failure.\r\n */\r\n protected _createProgram(_pb: ProgramBuilder, _ctx: DrawContext, _pass: number): GPUProgram {\r\n throw new Error('Abstract function call');\r\n }\r\n /**\r\n * Upload uniforms and bind resources to the per-material bind group (index 2).\r\n *\r\n * Implement in subclasses to:\r\n * - Write uniform buffers/textures/samplers to the `bindGroup`.\r\n * - Respect the current `pass` and `ctx`.\r\n *\r\n * @param _bindGroup - The bind group to populate.\r\n * @param _ctx - Draw context.\r\n * @param _pass - Pass number.\r\n */\r\n protected _applyUniforms(_bindGroup: BindGroup, _ctx: DrawContext, _pass: number) {}\r\n /**\r\n * Update render states (depth/stencil, blending, rasterization) for the pass.\r\n *\r\n * Implement in subclasses based on transparency, double-sidedness, depth writes/tests,\r\n * color mask, stencil ops, etc., and any context flags in `ctx`.\r\n *\r\n * @param _pass - Current pass index.\r\n * @param _renderStates - Render state set to mutate.\r\n * @param _ctx - Draw context.\r\n */\r\n protected updateRenderStates(_pass: number, _renderStates: RenderStateSet, _ctx: DrawContext) {}\r\n /**\r\n * Compute the material-specific portion of the shader hash for the current options.\r\n *\r\n * Subclasses should override to include macro/define sets that influence program compilation.\r\n * Example return: `\"USE_NORMALMAP=1;ALPHA_MODE=BLEND;RECEIVE_SHADOWS=1\"`.\r\n *\r\n * @returns Hash fragment string (no context/pass info).\r\n */\r\n protected _createHash() {\r\n return '';\r\n }\r\n /**\r\n * Whether this is a lightweight instance of a core material.\r\n *\r\n * Instances typically share GPU programs with a core and only override instance uniforms.\r\n * @internal\r\n */\r\n get $isInstance(): boolean {\r\n return false;\r\n }\r\n /**\r\n * Instance-uniform buffer for material instances, if supported.\r\n *\r\n * Returned as a typed Float32 view over a backing ArrayBuffer.\r\n * @internal\r\n */\r\n get $instanceUniforms(): Float32Array<ArrayBuffer> {\r\n throw new Error('Abstract function call');\r\n }\r\n}\r\n"],"names":["Material","Disposable","_nextId","_programCache","_nextProgramId","_id","_states","_numPasses","_hash","_optionTag","_changeTag","_currentHash","clone","other","copyFrom","clearCache","getEngine","resourceManager","setAssetId","getAssetId","coreMaterial","changeTag","instanceId","numPasses","val","length","push","getHash","pass","createHash","getQueueType","QUEUE_OPAQUE","isTransparentPass","_pass","supportLighting","supportInstancing","isBatchable","needSceneColor","needSceneDepth","createInstance","Error","apply","ctx","hash","calcGlobalHash","state","program","createProgram","console","error","material","name","renderPassType","renderPass","type","materialFlags","queue","lightBlending","hasOIT","oit","hasShadowLight","currentShadowLight","renderPassHash","bindGroup","bindGroupTag","renderStateSet","device","createRenderStateSet","materialTag","bindGroupLayouts","createBindGroup","getVersion","applyUniforms","updateRenderStates","id","RenderBundleWrapper","materialChanged","bind","setProgram","setBindGroup","setRenderStates","shaderVariantHash","drawEnvLight","draw","primitive","numInstances","drawPrimitive","needUpdate","_applyUniforms","optionChanged","changeHash","i","k","dispose","passToHash","String","_createHash","drawInstanced","instanceData","onDispose","pb","ProgramBuilder","_createProgram","_pb","_ctx","_bindGroup","_renderStates","$isInstance","$instanceUniforms"],"mappings":"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"}
1
+ {"version":3,"file":"material.js","sources":["../../src/material/material.ts"],"sourcesContent":["import type { AbstractDevice, BindGroup, GPUProgram, RenderStateSet } from '@zephyr3d/device';\r\nimport { ProgramBuilder } from '@zephyr3d/device';\r\nimport type { Primitive } from '../render/primitive';\r\nimport type { DrawContext } from '../render/drawable';\r\nimport { QUEUE_OPAQUE } from '../values';\r\nimport { RenderBundleWrapper } from '../render/renderbundle_wrapper';\r\nimport { Disposable } from '@zephyr3d/base';\r\nimport type { Clonable, IDisposable, Nullable } from '@zephyr3d/base';\r\nimport { getEngine } from '../app/api';\r\nimport type { OIT } from '../render';\r\n\r\ntype MaterialState = {\r\n program: Nullable<GPUProgram>;\r\n bindGroup: Nullable<BindGroup>;\r\n bindGroupTag: string;\r\n renderStateSet: RenderStateSet;\r\n materialTag: number;\r\n OIT: Nullable<OIT>;\r\n oitUniformTag: number;\r\n};\r\n\r\ntype ProgramCacheEntry = {\r\n program: GPUProgram;\r\n refCount: number;\r\n};\r\n\r\n/**\r\n * Base class for all materials.\r\n *\r\n * Responsibilities:\r\n * - Defines a multi-pass rendering interface (`numPasses`, `apply`, `bind`, `draw`, `drawPrimitive`).\r\n * - Builds and caches GPU shader programs per pass and per-render-context hash.\r\n * - Manages a per-material bind group (typically at index 2) for uniforms and resources.\r\n * - Updates render states per pass (`updateRenderStates`) and uploads uniforms (`_applyUniforms`).\r\n * - Tracks \"option\" changes that affect shader variant hashing and render bundles.\r\n *\r\n * Caching and hashing:\r\n * - `createHash(pass)` produces a stable hash representing shader variant options for a pass.\r\n * Override `_createHash()` in subclasses to encode feature toggles (defines, keywords, macros).\r\n * - The global hash used to key `MaterialState` also includes `ctx.materialFlags` and\r\n * `ctx.shaderVariantHash` when provided, allowing context-sensitive variants without\r\n * coupling shader programs to runtime texture identities.\r\n * - GPU programs are additionally memoized in a global static `_programCache` across materials\r\n * by `constructor.name` + hash, to avoid recompilation of identical variants.\r\n *\r\n * Bind groups and uniforms:\r\n * - If a program declares a bind group layout at index 2 (i.e., `bindGroupLayouts.length > 2`),\r\n * `apply()` will allocate the group and keep it in the state. Subclasses should fill it in\r\n * `_applyUniforms()`.\r\n * - `applyUniforms()` only calls `_applyUniforms()` when `needUpdate` is true, based on\r\n * `_optionTag` changes (see `optionChanged()`).\r\n *\r\n * Lifecycle:\r\n * - Constructed materials register a persistent ID into a global registry for serialization.\r\n * - `apply(ctx)` prepares all passes: creates/gets state, programs, bind groups, render states,\r\n * and uploads uniforms as needed.\r\n * - `bind(device, pass)` binds the program, bind group (index 2), and render states.\r\n * - `draw(primitive, ctx, numInstances)` runs all passes, calling `bind()` and `drawPrimitive()`.\r\n * - `onDispose()` releases bind groups and registry entries.\r\n *\r\n * Extending:\r\n * - Override `_createProgram(pb, ctx, pass)` to build a shader.\r\n * - Override `_applyUniforms(bindGroup, ctx, pass)` to upload uniforms and resources.\r\n * - Override `updateRenderStates(pass, renderStates, ctx)` to set depth, blend, cull, etc.\r\n * - Override `_createHash()` to encode options that affect program compilation.\r\n * - Override `supportLighting`, `supportInstancing`, `isTransparentPass`, `getQueueType`, etc.\r\n *\r\n * Thread-safety:\r\n * - Intended for main-thread use in a renderer driving WebGPU/WebGL-like devices.\r\n *\r\n * @public\r\n */\r\nexport class Material extends Disposable implements Clonable<Material>, IDisposable {\r\n /**\r\n * Monotonic instance ID counter.\r\n * @internal\r\n */\r\n private static _nextId = 0;\r\n /**\r\n * Global program cache keyed by global hash\r\n */\r\n private static _programCache: { [hash: string]: ProgramCacheEntry } = {};\r\n /**\r\n * Per-material state cache keyed by global hash (material + context + pass).\r\n * @internal\r\n */\r\n private _states: { [hash: string]: MaterialState };\r\n /**\r\n * Number of rendering passes.\r\n * Subclasses can increase this to implement multi-pass rendering.\r\n * @internal\r\n */\r\n protected _numPasses: number;\r\n /**\r\n * Per-pass hash cached results. Length scales with `numPasses`.\r\n * @internal\r\n */\r\n protected _hash: Nullable<string>[];\r\n /**\r\n * Incremented each time options change (via `optionChanged`), used to decide whether\r\n * uniforms need update on next `apply()`.\r\n * @internal\r\n */\r\n private _optionTag: number;\r\n /**\r\n * Unique runtime instance ID.\r\n * @internal\r\n */\r\n protected readonly _id: number;\r\n /**\r\n * Latest computed global hash per pass, set during `apply()`, read in `bind()`.\r\n * @internal\r\n */\r\n private _currentHash: string[];\r\n /**\r\n * Incremented when material states affecting render bundles change (e.g., bind group id changes\r\n * or `optionChanged(true)`), used to trigger re-recording of render bundles.\r\n * @internal\r\n */\r\n private _changeTag: number;\r\n /**\r\n * Unique program id counter for naming compiled programs.\r\n * @internal\r\n */\r\n private _nextProgramId = 0;\r\n /**\r\n * Create a new material instance.\r\n *\r\n * - Initializes one pass by default.\r\n * - Prepares per-pass hash storage and change tracking.\r\n * - Registers a persistent ID in the global registry.\r\n */\r\n constructor() {\r\n super();\r\n this._id = ++Material._nextId;\r\n this._nextProgramId = 0;\r\n this._states = {};\r\n this._numPasses = 1;\r\n this._hash = [null];\r\n this._optionTag = 0;\r\n this._changeTag = 0;\r\n this._currentHash = [];\r\n }\r\n /**\r\n * Create a shallow clone of this material.\r\n *\r\n * Note: The base implementation returns a base `Material`. Subclasses should\r\n * override to return their own type and copy custom fields.\r\n */\r\n clone() {\r\n const other = new Material();\r\n other.copyFrom(this);\r\n return other;\r\n }\r\n /**\r\n * Copy basic properties from another material.\r\n *\r\n * Disposes existing bind groups/states, copies `numPasses`. Subclasses should\r\n * extend this to copy their own fields and call `optionChanged(true)` if\r\n * shader-affecting options differ.\r\n *\r\n * @param other - Source material.\r\n */\r\n copyFrom(other: this) {\r\n this.clearCache();\r\n this._numPasses = other._numPasses;\r\n getEngine().resourceManager.setAssetId(this, getEngine().resourceManager.getAssetId(other.coreMaterial));\r\n }\r\n /**\r\n * Incremented when the material’s GPU-relevant state changes and render bundles\r\n * may need to be rebuilt.\r\n */\r\n get changeTag() {\r\n return this._changeTag;\r\n }\r\n /**\r\n * Runtime-unique numeric identifier for the material instance.\r\n */\r\n get instanceId() {\r\n return this._id;\r\n }\r\n /**\r\n * Number of rendering passes this material uses.\r\n *\r\n * Increasing this will expand the per-pass hash cache; make sure to implement\r\n * `createHash(pass)`, `_createProgram(pb, ctx, pass)`, and `updateRenderStates(pass, ...)`\r\n * accordingly for each pass.\r\n */\r\n get numPasses() {\r\n return this._numPasses;\r\n }\r\n set numPasses(val) {\r\n while (this._hash.length < val) {\r\n this._hash.push(null);\r\n }\r\n this._numPasses = val;\r\n }\r\n /**\r\n * Get the number of passes this material will render for the given draw context.\r\n *\r\n * Default returns `numPasses`. Subclasses may override to skip passes based on context flags.\r\n * @param _ctx - Draw context (device, flags, pass hash, instance data, etc.).\r\n * @returns Number of passes to render for this context.\r\n */\r\n protected getNumPassesForRender(_ctx: DrawContext) {\r\n return this._numPasses;\r\n }\r\n /**\r\n * Get or compute the per-pass shader hash used for program caching.\r\n *\r\n * Calls `createHash(pass)` lazily and caches the result.\r\n * @internal\r\n */\r\n protected getHash(pass: number) {\r\n if (this._hash[pass] === null) {\r\n this._hash[pass] = this.createHash(pass);\r\n }\r\n return this._hash[pass];\r\n }\r\n /**\r\n * Return the queue type to which this material belongs.\r\n *\r\n * Override this in transparent or special materials (e.g., post-process).\r\n */\r\n getQueueType(): number {\r\n return QUEUE_OPAQUE;\r\n }\r\n /**\r\n * Whether the given pass is transparent.\r\n *\r\n * Used to place draw calls into appropriate render queues and set blending states.\r\n */\r\n isTransparentPass(_pass: number): boolean {\r\n return false;\r\n }\r\n /**\r\n * Whether this material's shading is affected by scene lights.\r\n *\r\n * Override and return `false` for unlit materials.\r\n */\r\n supportLighting(): boolean {\r\n return true;\r\n }\r\n /**\r\n * Whether this material supports hardware instancing.\r\n *\r\n * Override and return `false` if per-instance data is not supported in the shader.\r\n */\r\n supportInstancing(): boolean {\r\n return true;\r\n }\r\n /** Returns true if this is a instance of material */\r\n isBatchable(): boolean {\r\n return false;\r\n }\r\n\r\n /**\r\n * Whether this material requires the scene color texture (e.g., for refraction).\r\n */\r\n needSceneColor(): boolean {\r\n return false;\r\n }\r\n /**\r\n * Whether this material requires the linear scene depth texture (e.g., for depth-aware effects).\r\n */\r\n needSceneDepth(): boolean {\r\n return false;\r\n }\r\n /**\r\n * Create a material instance (instance-uniform-driven variant).\r\n *\r\n * Base returns `null`. Subclasses that support instancing can return a lightweight instance.\r\n */\r\n createInstance(): this {\r\n throw new Error('Abstract function call');\r\n }\r\n /**\r\n * Returns the core material that owns GPU state.\r\n *\r\n * Instances may delegate to a shared core to reuse compiled programs and caches.\r\n * @public\r\n */\r\n get coreMaterial(): this {\r\n return this;\r\n }\r\n /**\r\n * Prepare the material for drawing across all passes for the given draw context.\r\n *\r\n * Steps per pass:\r\n * - Compute global hash (material variant + context).\r\n * - Retrieve or build the GPU program, cache in `_programCache`.\r\n * - Create per-material bind group (index 2) if the program exposes it.\r\n * - Update uniforms if `_optionTag` indicates changes since last apply.\r\n * - Update and cache render states for the pass.\r\n * - Detect bind group GPU ID changes to bump `changeTag` and notify `RenderBundleWrapper`.\r\n *\r\n * @param ctx - Draw context (device, flags, pass hash, instance data, etc.).\r\n * @returns `true` if successful; `false` if any pass lacks a valid program.\r\n */\r\n apply(ctx: DrawContext) {\r\n const numPasses = this.getNumPassesForRender(ctx);\r\n for (let pass = 0; pass < numPasses; pass++) {\r\n const hash = this.calcGlobalHash(ctx, pass);\r\n let state = this._states[hash];\r\n if (!state) {\r\n let programEntry = Material._programCache[hash];\r\n let program = programEntry?.program;\r\n if (!program) {\r\n program = this.createProgram(ctx, pass) ?? null;\r\n if (!program) {\r\n console.error('[Material.apply] program build failed', {\r\n material: this.constructor.name,\r\n pass,\r\n renderPassType: ctx.renderPass?.type ?? null,\r\n materialFlags: ctx.materialFlags,\r\n queue: ctx.queue,\r\n lightBlending: ctx.lightBlending,\r\n hasOIT: !!ctx.oit,\r\n hasShadowLight: !!ctx.currentShadowLight,\r\n renderPassHash: ctx.renderPassHash\r\n });\r\n state = {\r\n program: null,\r\n bindGroup: null,\r\n bindGroupTag: '',\r\n renderStateSet: ctx.device.createRenderStateSet(),\r\n materialTag: -1,\r\n OIT: null,\r\n oitUniformTag: -1\r\n };\r\n this._states[hash] = state;\r\n return false;\r\n }\r\n program.name = `@${this.constructor.name}_program_${this._nextProgramId++}`;\r\n programEntry = { program, refCount: 0 };\r\n Material._programCache[hash] = programEntry;\r\n }\r\n programEntry!.refCount++;\r\n const bindGroup =\r\n program.bindGroupLayouts.length > 2\r\n ? ctx.device.createBindGroup(program.bindGroupLayouts[2])\r\n : null;\r\n state = {\r\n program,\r\n bindGroup,\r\n bindGroupTag: bindGroup?.getVersion() ?? '',\r\n renderStateSet: ctx.device.createRenderStateSet(),\r\n materialTag: -1,\r\n OIT: null,\r\n oitUniformTag: -1\r\n };\r\n this._states[hash] = state;\r\n }\r\n if (!state.program) {\r\n return false;\r\n }\r\n if (state.bindGroup) {\r\n if (ctx.oit && (state.OIT !== ctx.oit || state.oitUniformTag !== ctx.oit.uniformTag)) {\r\n state.OIT = ctx.oit;\r\n state.oitUniformTag = ctx.oit.uniformTag;\r\n ctx.oit.applyUniforms(ctx, state.bindGroup);\r\n }\r\n this.applyUniforms(state.bindGroup, ctx, state.materialTag !== this._optionTag, pass);\r\n }\r\n state.materialTag = this._optionTag;\r\n this.updateRenderStates(pass, state.renderStateSet, ctx);\r\n this._currentHash[pass] = hash;\r\n if (state.bindGroup) {\r\n const id = state.bindGroup.getVersion();\r\n if (id !== state.bindGroupTag) {\r\n state.bindGroupTag = id;\r\n this._changeTag++;\r\n RenderBundleWrapper.materialChanged(this.coreMaterial);\r\n }\r\n }\r\n }\r\n return true;\r\n }\r\n /**\r\n * Bind the program, bind group, and render states for the specified pass.\r\n *\r\n * Must be called after `apply(ctx)` for the same pass.\r\n *\r\n * @param device - Rendering device.\r\n * @param pass - Pass index to bind.\r\n * @returns `true` on success; `false` if state or program missing.\r\n * @internal\r\n */\r\n bind(device: AbstractDevice, pass: number) {\r\n const hash = this._currentHash[pass];\r\n const state = this._states[hash];\r\n if (!state) {\r\n console.error('Material.bind() failed: state not found');\r\n return false;\r\n }\r\n if (!state.program) {\r\n return false;\r\n }\r\n device.setProgram(state.program);\r\n if (state.bindGroup) {\r\n device.setBindGroup(2, state.bindGroup);\r\n }\r\n device.setRenderStates(state.renderStateSet);\r\n return true;\r\n }\r\n /**\r\n * Compute the global hash for the given pass and draw context.\r\n *\r\n * Includes:\r\n * - Per-pass material hash from `getHash(pass)`.\r\n * - `ctx.materialFlags` for context-dependent toggles.\r\n * - `ctx.shaderVariantHash` for shader layout/feature variants, falling back\r\n * to `ctx.renderPassHash` for passes without a shader-only hash.\r\n * @internal\r\n */\r\n private calcGlobalHash(ctx: DrawContext, pass: number) {\r\n const shaderVariantHash = ctx.shaderVariantHash ?? ctx.renderPassHash;\r\n return `${this.getHash(pass)}:${ctx.materialFlags}:${shaderVariantHash}:${\r\n ctx.drawEnvLight ? 1 : 0\r\n }:${ctx.currentShadowLight ? 1 : 0}:${ctx.lightBlending ? 1 : 0}`;\r\n }\r\n /**\r\n * Draw a primitive for all passes using this material.\r\n *\r\n * Calls `bind()` then `drawPrimitive()` per pass. If `numInstances` is zero,\r\n * and `ctx.instanceData` exists, uses `ctx.instanceData.numInstances`.\r\n *\r\n * @param primitive - Geometry to draw.\r\n * @param ctx - Draw context.\r\n * @param numInstances - Instance count; 0 means auto-detect from context.\r\n * @internal\r\n */\r\n draw(primitive: Primitive, ctx: DrawContext, numInstances = 0) {\r\n const numPasses = this.getNumPassesForRender(ctx);\r\n for (let pass = 0; pass < numPasses; pass++) {\r\n this.bind(ctx.device, pass);\r\n this.drawPrimitive(pass, primitive, ctx, numInstances);\r\n }\r\n }\r\n /**\r\n * Conditionally update uniforms/resources into the material bind group.\r\n *\r\n * Delegates to `_applyUniforms()` when `needUpdate` is true (based on `_optionTag` check).\r\n *\r\n * @param bindGroup - Material bind group at index 2 (may be `null` if program has no layout).\r\n * @param ctx - Draw context.\r\n * @param needUpdate - Whether uniforms need to be refreshed.\r\n * @param pass - Pass index.\r\n */\r\n applyUniforms(bindGroup: BindGroup, ctx: DrawContext, needUpdate: boolean, pass: number) {\r\n if (needUpdate) {\r\n this._applyUniforms(bindGroup, ctx, pass);\r\n }\r\n }\r\n /**\r\n * Notify the material that some option changed.\r\n *\r\n * - Always increments `_optionTag`. This will trigger uniforms update on next `apply()`.\r\n * - If `changeHash` is true, clears per-pass hashes so programs/states will be rebuilt,\r\n * increments `_changeTag`, and notifies `RenderBundleWrapper` to regenerate recorded bundles.\r\n *\r\n * @param changeHash - Set true if the change affects shader compilation or render states.\r\n * @internal\r\n */\r\n optionChanged(changeHash: boolean) {\r\n this._optionTag++;\r\n if (changeHash) {\r\n for (let i = 0; i < this._numPasses; i++) {\r\n this._hash[i] = null;\r\n }\r\n this._changeTag++;\r\n RenderBundleWrapper.materialChanged(this.coreMaterial);\r\n }\r\n }\r\n clearCache() {\r\n const hashes = Object.keys(this._states);\r\n if (hashes.length > 0) {\r\n this._changeTag++;\r\n RenderBundleWrapper.materialChanged(this.coreMaterial);\r\n }\r\n for (const k of hashes) {\r\n const state = this._states[k];\r\n state?.bindGroup?.dispose();\r\n const programEntry = Material._programCache[k];\r\n if (state?.program && programEntry?.program === state.program) {\r\n programEntry.refCount--;\r\n if (programEntry.refCount <= 0) {\r\n programEntry.program.dispose();\r\n delete Material._programCache[k];\r\n }\r\n }\r\n }\r\n this._states = {};\r\n }\r\n /**\r\n * Convert a pass index to a hash seed string.\r\n *\r\n * Subclasses may override to encode per-pass role (e.g., \"depth\", \"forward\", \"shadow\").\r\n *\r\n * @param pass - Pass number.\r\n * @returns String used when building full hash.\r\n */\r\n passToHash(pass: number) {\r\n return String(pass);\r\n }\r\n /**\r\n * Build the material hash for a pass (excluding context-dependent parts).\r\n *\r\n * Default formula: `${constructor.name}|${pass}|${_createHash()}`\r\n *\r\n * @param pass - Pass number.\r\n * @returns Hash string used in program caching.\r\n * @internal\r\n */\r\n createHash(pass: number) {\r\n return `${this.constructor.name}|${this.passToHash(pass)}|${this._createHash()}`;\r\n }\r\n /**\r\n * Issue the actual draw call for a pass.\r\n *\r\n * Override for custom per-pass draw behavior if necessary. The default implementation:\r\n * - Draws instanced if `numInstances > 0`.\r\n * - Else uses `ctx.instanceData.numInstances` if available.\r\n * - Else issues a non-instanced draw.\r\n *\r\n * @param pass - Pass number.\r\n * @param primitive - Primitive to draw.\r\n * @param ctx - Draw context.\r\n * @param numInstances - Explicit instance count (0 = auto).\r\n */\r\n drawPrimitive(pass: number, primitive: Primitive, ctx: DrawContext, numInstances: number) {\r\n if (numInstances > 0) {\r\n primitive.drawInstanced(numInstances);\r\n } else if (ctx.instanceData) {\r\n primitive.drawInstanced(ctx.instanceData.numInstances);\r\n } else {\r\n primitive.draw();\r\n }\r\n }\r\n /**\r\n * Dispose the material and release GPU-side resource references.\r\n *\r\n * - Unregisters from the global registry.\r\n * - Disposes the per-material bind groups kept in `_states`.\r\n */\r\n protected onDispose() {\r\n super.onDispose();\r\n this.clearCache();\r\n }\r\n /**\r\n * Build the GPU program for a pass.\r\n *\r\n * Default creates a `ProgramBuilder` and calls `_createProgram(pb, ctx, pass)`.\r\n * Subclasses should override `_createProgram` instead of this method unless\r\n * they need to replace builder instantiation.\r\n *\r\n * @param ctx - Draw context.\r\n * @param pass - Pass number.\r\n * @returns The compiled `GPUProgram`, or `null` on failure.\r\n * @internal\r\n */\r\n protected createProgram(ctx: DrawContext, pass: number) {\r\n const pb = new ProgramBuilder(ctx.device);\r\n return this._createProgram(pb, ctx, pass);\r\n }\r\n /**\r\n * Create and compile the shader program for this material/pass.\r\n *\r\n * Implement in subclasses:\r\n * - Define shader stages, entry points, macros/defines, and resource layouts.\r\n * - Return a compiled `GPUProgram`.\r\n *\r\n * @param pb - Program builder.\r\n * @param ctx - Draw context.\r\n * @param _pass - Pass number.\r\n * @returns The created program, or `null` on failure.\r\n */\r\n protected _createProgram(_pb: ProgramBuilder, _ctx: DrawContext, _pass: number): GPUProgram {\r\n throw new Error('Abstract function call');\r\n }\r\n /**\r\n * Upload uniforms and bind resources to the per-material bind group (index 2).\r\n *\r\n * Implement in subclasses to:\r\n * - Write uniform buffers/textures/samplers to the `bindGroup`.\r\n * - Respect the current `pass` and `ctx`.\r\n *\r\n * @param _bindGroup - The bind group to populate.\r\n * @param _ctx - Draw context.\r\n * @param _pass - Pass number.\r\n */\r\n protected _applyUniforms(_bindGroup: BindGroup, _ctx: DrawContext, _pass: number) {}\r\n /**\r\n * Update render states (depth/stencil, blending, rasterization) for the pass.\r\n *\r\n * Implement in subclasses based on transparency, double-sidedness, depth writes/tests,\r\n * color mask, stencil ops, etc., and any context flags in `ctx`.\r\n *\r\n * @param _pass - Current pass index.\r\n * @param _renderStates - Render state set to mutate.\r\n * @param _ctx - Draw context.\r\n */\r\n protected updateRenderStates(_pass: number, _renderStates: RenderStateSet, _ctx: DrawContext) {}\r\n /**\r\n * Compute the material-specific portion of the shader hash for the current options.\r\n *\r\n * Subclasses should override to include macro/define sets that influence program compilation.\r\n * Example return: `\"USE_NORMALMAP=1;ALPHA_MODE=BLEND;RECEIVE_SHADOWS=1\"`.\r\n *\r\n * @returns Hash fragment string (no context/pass info).\r\n */\r\n protected _createHash() {\r\n return '';\r\n }\r\n /**\r\n * Whether this is a lightweight instance of a core material.\r\n *\r\n * Instances typically share GPU programs with a core and only override instance uniforms.\r\n * @internal\r\n */\r\n get $isInstance(): boolean {\r\n return false;\r\n }\r\n /**\r\n * Instance-uniform buffer for material instances, if supported.\r\n *\r\n * Returned as a typed Float32 view over a backing ArrayBuffer.\r\n * @internal\r\n */\r\n get $instanceUniforms(): Float32Array<ArrayBuffer> {\r\n throw new Error('Abstract function call');\r\n }\r\n}\r\n"],"names":["Material","Disposable","_nextId","_programCache","_nextProgramId","_id","_states","_numPasses","_hash","_optionTag","_changeTag","_currentHash","clone","other","copyFrom","clearCache","getEngine","resourceManager","setAssetId","getAssetId","coreMaterial","changeTag","instanceId","numPasses","val","length","push","getNumPassesForRender","_ctx","getHash","pass","createHash","getQueueType","QUEUE_OPAQUE","isTransparentPass","_pass","supportLighting","supportInstancing","isBatchable","needSceneColor","needSceneDepth","createInstance","Error","apply","ctx","hash","calcGlobalHash","state","programEntry","program","createProgram","console","error","material","name","renderPassType","renderPass","type","materialFlags","queue","lightBlending","hasOIT","oit","hasShadowLight","currentShadowLight","renderPassHash","bindGroup","bindGroupTag","renderStateSet","device","createRenderStateSet","materialTag","OIT","oitUniformTag","refCount","bindGroupLayouts","createBindGroup","getVersion","uniformTag","applyUniforms","updateRenderStates","id","RenderBundleWrapper","materialChanged","bind","setProgram","setBindGroup","setRenderStates","shaderVariantHash","drawEnvLight","draw","primitive","numInstances","drawPrimitive","needUpdate","_applyUniforms","optionChanged","changeHash","i","hashes","Object","keys","k","dispose","passToHash","String","_createHash","drawInstanced","instanceData","onDispose","pb","ProgramBuilder","_createProgram","_pb","_bindGroup","_renderStates","$isInstance","$instanceUniforms"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { ProgramBuilder } from '@zephyr3d/device';
2
- import { RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_LIGHT, QUEUE_OPAQUE, MaterialVaryingFlags, RENDER_PASS_TYPE_DEPTH, QUEUE_TRANSPARENT } from '../values.js';
2
+ import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_LIGHT, QUEUE_OPAQUE, MaterialVaryingFlags, QUEUE_TRANSPARENT } from '../values.js';
3
3
  import { Material } from './material.js';
4
4
  import { encodeNormalizedFloatToRGBA } from '../shaders/misc.js';
5
+ import { interleavedGradientNoise } from '../shaders/noise.js';
5
6
  import { ShaderHelper } from './shader/helper.js';
6
7
  import { Vector4, DRef, Vector3, Vector2, applyMixins } from '@zephyr3d/base';
7
8
  import { RenderBundleWrapper } from '../render/renderbundle_wrapper.js';
8
- import { getDevice } from '../app/api.js';
9
+ import { getDevice, getEngine } from '../app/api.js';
9
10
 
10
11
  /**
11
12
  * Apply material mixins to a target material class.
@@ -30,6 +31,7 @@ let FEATURE_CULLMODE = 0;
30
31
  let FEATURE_ALPHATOCOVERAGE = 0;
31
32
  let FEATURE_ALPHADITHER = 0;
32
33
  let FEATURE_DISABLE_TAA = 0;
34
+ let FEATURE_TRANSPARENT_MOTION_VECTOR = 0;
33
35
  /**
34
36
  * Base class for mesh materials.
35
37
  *
@@ -408,6 +410,7 @@ let FEATURE_DISABLE_TAA = 0;
408
410
  }
409
411
  }
410
412
  Object.setPrototypeOf(instance, that);
413
+ getEngine().resourceManager.trackMaterialReference(instance, getEngine().resourceManager.getAssetId(that.coreMaterial));
411
414
  return instance;
412
415
  }
413
416
  /**
@@ -464,6 +467,25 @@ let FEATURE_DISABLE_TAA = 0;
464
467
  this.useFeature(FEATURE_DISABLE_TAA, !!val);
465
468
  }
466
469
  /**
470
+ * Whether a blended material contributes motion vectors.
471
+ *
472
+ * @remarks
473
+ * Off by default, and only meaningful with `blendMode !== 'none'`: blended
474
+ * geometry is normally absent from the prepass, so it writes no velocity and a
475
+ * temporal filter reprojects it with whatever is behind it. Turning this on
476
+ * adds it to a motion-vector-only pass over the transparent queue.
477
+ *
478
+ * Right for geometry that is dense enough to own its pixels - hair, foliage
479
+ * cards - where one velocity per pixel is a fair summary. Wrong for genuinely
480
+ * see-through surfaces such as glass, where the background's own velocity is
481
+ * the one that should survive, which is why this is opt-in.
482
+ */ get transparentMotionVector() {
483
+ return !!this.featureUsed(FEATURE_TRANSPARENT_MOTION_VECTOR);
484
+ }
485
+ set transparentMotionVector(val) {
486
+ this.useFeature(FEATURE_TRANSPARENT_MOTION_VECTOR, !!val);
487
+ }
488
+ /**
467
489
  * TAA strength in [0, 1].
468
490
  * - Higher values generally imply stronger accumulation.
469
491
  * - The value is mapped when writing motion-vector outputs during depth pass.
@@ -577,9 +599,22 @@ let FEATURE_DISABLE_TAA = 0;
577
599
  if (ctx && this.useTransparentShadowCasterForPass(ctx.renderPass?.type) && this.featureUsed(FEATURE_ALPHABLEND) !== 'none') {
578
600
  return 'none';
579
601
  }
602
+ // The prepass records depth and velocity, neither of which is a quantity
603
+ // that blends. Blended geometry only reaches it through the motion vector
604
+ // pass, and there the nearest fragment should replace what is under it
605
+ // rather than tint it.
606
+ if (ctx && ctx.renderPass?.type === RENDER_PASS_TYPE_DEPTH) {
607
+ return 'none';
608
+ }
580
609
  return this.featureUsed(FEATURE_ALPHABLEND);
581
610
  }
582
611
  /**
612
+ * Whether this draw is the motion-vector-only pass over the transparent queue.
613
+ * @internal
614
+ */ isMotionVectorOnlyPass(ctx) {
615
+ return ctx.renderPass?.type === RENDER_PASS_TYPE_DEPTH && !!ctx.renderPass.motionVectorOnly;
616
+ }
617
+ /**
583
618
  * Returns the alpha cutoff that should be used for the current pass.
584
619
  */ getActiveAlphaCutoff(ctx, pass = this.pass) {
585
620
  return ctx && this.useTransparentShadowCaster(ctx, pass) ? this._shadowAlphaCutoff : this._alphaCutoff;
@@ -596,7 +631,7 @@ let FEATURE_DISABLE_TAA = 0;
596
631
  }
597
632
  /**
598
633
  * Whether this pass needs an alpha-cutoff uniform bound.
599
- */ usesAlphaCutoff(ctx, pass = this.pass) {
634
+ */ usesAlphaCutoff(ctx, pass) {
600
635
  return this._alphaCutoff > 0 || !!ctx && this.useTransparentShadowCaster(ctx, pass);
601
636
  }
602
637
  /**
@@ -621,6 +656,22 @@ let FEATURE_DISABLE_TAA = 0;
621
656
  const blending = !isObjectColorPass && (blendMode !== 'none' || ctx.lightBlending);
622
657
  const a2c = !isObjectColorPass && this.featureUsed(FEATURE_ALPHATOCOVERAGE) && !this.useTransparentShadowCaster(ctx, pass);
623
658
  const ztestEq = ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT && ctx.queue === QUEUE_OPAQUE && !!ctx.depthPrepassAttachment && !ctx.sceneColorTexture;
659
+ if (this.isMotionVectorOnlyPass(ctx)) {
660
+ // Tested against the opaque depth, so anything behind the scene is
661
+ // rejected, but never written: the prepass depth is what the light pass
662
+ // and every screen-space effect read, and blended geometry does not
663
+ // belong in it.
664
+ stateSet.defaultBlendingState();
665
+ stateSet.useDepthState().enableTest(true).enableWrite(false);
666
+ if (ctx.forceCullMode || this._cullMode !== 'back') {
667
+ stateSet.useRasterizerState().cullMode = ctx.forceCullMode || this._cullMode;
668
+ } else {
669
+ stateSet.defaultRasterizerState();
670
+ }
671
+ stateSet.defaultColorState();
672
+ stateSet.defaultStencilState();
673
+ return;
674
+ }
624
675
  if (blending || a2c) {
625
676
  const blendingState = stateSet.useBlendingState();
626
677
  if (blending) {
@@ -667,6 +718,14 @@ let FEATURE_DISABLE_TAA = 0;
667
718
  if (ctx.oit) {
668
719
  ctx.oit.setRenderStates(stateSet);
669
720
  }
721
+ if (ctx.renderPass.type === RENDER_PASS_TYPE_SHADOWMAP) {
722
+ // Gives the shadow implementation the last say, the same way OIT gets it on
723
+ // the transparent pass. Single-pass techniques leave the states alone; an
724
+ // accumulating one (deep opacity maps) needs blending on and depth write
725
+ // off, which the branches above would otherwise never produce for a caster.
726
+ const shadowMapParams = ctx.shadowMapInfo?.get(ctx.renderPass.light);
727
+ shadowMapParams?.impl?.setCasterRenderStates(shadowMapParams, stateSet);
728
+ }
670
729
  }
671
730
  /**
672
731
  * Submit material uniforms/resources to the material bind group (set 2).
@@ -683,9 +742,6 @@ let FEATURE_DISABLE_TAA = 0;
683
742
  if (this.isTransparentPass(pass, ctx) && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT && !(ctx.materialFlags & MaterialVaryingFlags.INSTANCING)) {
684
743
  bindGroup.setValue('zOpacity', this._opacity);
685
744
  }
686
- if (ctx.oit) {
687
- ctx.oit.applyUniforms(ctx, bindGroup);
688
- }
689
745
  if (ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH && ctx.motionVectors) {
690
746
  bindGroup.setValue('zTAAStrength', (1 - this._taaStrength) * 50000);
691
747
  }
@@ -766,6 +822,9 @@ let FEATURE_DISABLE_TAA = 0;
766
822
  */ _applyUniforms(bindGroup, ctx, pass) {
767
823
  this.applyUniformValues(bindGroup, ctx, pass);
768
824
  }
825
+ isMaterialTextureEnabled(_name) {
826
+ return true;
827
+ }
769
828
  /**
770
829
  * Whether the fragment shader needs to compute color.
771
830
  * Returns true for LIGHT pass, or when alpha test or alpha-to-coverage is enabled.
@@ -795,7 +854,7 @@ let FEATURE_DISABLE_TAA = 0;
795
854
  scope.$inputs.zBlendWeights = pb.vec4().attrib('blendWeights');
796
855
  ShaderHelper.prepareSkinAnimation(scope);
797
856
  }
798
- if (this.drawContext.materialFlags & MaterialVaryingFlags.MORPH_ANIMATION && this.drawContext.device.type === 'webgl') {
857
+ if (this.drawContext.device.type === 'webgl' && this.drawContext.materialFlags & (MaterialVaryingFlags.SKIN_ANIMATION | MaterialVaryingFlags.MORPH_ANIMATION)) {
799
858
  scope.$inputs.zFakeVertexID = pb.float().attrib('texCoord7');
800
859
  }
801
860
  if (this.drawContext.materialFlags & MaterialVaryingFlags.INSTANCING) {
@@ -852,6 +911,12 @@ let FEATURE_DISABLE_TAA = 0;
852
911
  fragment (pb) {
853
912
  if (that.drawContext.oit) {
854
913
  that.drawContext.oit.setupFragmentOutput(this);
914
+ } else if (that.isMotionVectorOnlyPass(ctx)) {
915
+ // One attachment, so the motion vector has to be the first output
916
+ // declared rather than the second - the framebuffer this pass runs
917
+ // against carries no linear depth target to write.
918
+ this.$outputs.zMotionVector = pb.vec4();
919
+ this.zTAAStrength = pb.float().uniform(2);
855
920
  } else {
856
921
  this.$outputs.zFragmentOutput = pb.vec4();
857
922
  if (ctx.materialFlags & MaterialVaryingFlags.SCENE_STORE_ROUGHNESS) {
@@ -953,9 +1018,30 @@ let FEATURE_DISABLE_TAA = 0;
953
1018
  this.$l.noise = this.Z_shadowDither4x4.at(pb.uint(this.ditherIndex));
954
1019
  this.$return(pb.lessThan(this.coverage, this.noise));
955
1020
  } else if (that.featureUsed(FEATURE_ALPHADITHER) && !that.isTransparentPass(that.pass, that.drawContext)) {
956
- this.$l.frame = pb.float(ShaderHelper.getFramestamp(this));
957
- this.$l.phase = pb.add(this.$builtins.fragCoord.xy, pb.vec2(this.frame, pb.mul(this.frame, 0.754877666)));
958
- this.$l.noise = pb.fract(pb.mul(pb.sin(pb.dot(this.phase, pb.vec2(12.9898, 78.233))), 43758.5453));
1021
+ // Interleaved gradient noise rather than the fract(sin(dot(...)))
1022
+ // hash it replaced. That hash feeds its dot product straight into
1023
+ // sin(), and at 1080p the fragCoord term alone reaches ~1e5 - past
1024
+ // the range where GPU sin() stays accurate - so it degraded into a
1025
+ // fixed hatch pattern that TAA then faithfully preserved instead of
1026
+ // averaging away. IGN wraps its accumulator in fract() before the
1027
+ // magnifying multiply, so it stays well conditioned at any
1028
+ // resolution, and it is what UE5 uses for exactly this job.
1029
+ //
1030
+ // The frame index is wrapped for the same reason: it is unbounded
1031
+ // (device.frameInfo.frameCounter), and left raw it would walk the
1032
+ // noise input back out of that well-conditioned range within a
1033
+ // minute. 64 matches the wrap the eye material already applies.
1034
+ this.$l.frameId = pb.mod(pb.float(ShaderHelper.getFramestamp(this)), 64);
1035
+ // Decorrelate overlapping layers by depth, for the same reason the
1036
+ // shadow dither above does it: the noise is a function of the pixel,
1037
+ // so without this every fragment stacked on one texel draws the same
1038
+ // value and they all pass or all fail together. That collapses the
1039
+ // visible coverage of N layers from 1-(1-a)^N down to a - on dense
1040
+ // hair seen from far enough that each strand is thin, an order of
1041
+ // magnitude too dark. The 131.37 scale matches the shadow path.
1042
+ this.$l.layerJitter = pb.fract(pb.mul(this.$builtins.fragCoord.z, 131.37));
1043
+ this.$l.phase = pb.add(this.$builtins.fragCoord.xy, pb.add(pb.mul(pb.vec2(47, 17), 0.695, this.frameId), pb.mul(pb.vec2(13.7, 29.3), this.layerJitter)));
1044
+ this.$l.noise = interleavedGradientNoise(this, this.phase);
959
1045
  this.$l.coverage = pb.clamp(pb.div(pb.sub(this.alpha, this.cutoff), pb.max(pb.sub(1, this.cutoff), 1e-4)), 0, 1);
960
1046
  this.$return(pb.lessThan(this.coverage, this.noise));
961
1047
  } else {
@@ -1005,13 +1091,22 @@ let FEATURE_DISABLE_TAA = 0;
1005
1091
  }
1006
1092
  }
1007
1093
  const depthPass = that.drawContext.renderPass;
1008
- this.$l.depth = ShaderHelper.nonLinearDepthToLinearNormalized(this, this.$builtins.fragCoord.z);
1009
- if (depthPass.encodeDepth) {
1010
- this.$outputs.zFragmentOutput = encodeNormalizedFloatToRGBA(this, this.depth);
1011
- } else if (depthPass.renderBackface) {
1012
- this.$outputs.zFragmentOutput = pb.vec4(0, this.depth, 0, 1);
1094
+ if (depthPass.motionVectorOnly) {
1095
+ // The whole transparent queue is submitted to this pass; a material
1096
+ // that has not opted in leaves the velocity underneath it alone,
1097
+ // which for a see-through surface is the right answer.
1098
+ if (!that.featureUsed(FEATURE_TRANSPARENT_MOTION_VECTOR)) {
1099
+ pb.discard();
1100
+ }
1013
1101
  } else {
1014
- this.$outputs.zFragmentOutput = pb.vec4(this.depth, 0, 0, 1);
1102
+ this.$l.depth = ShaderHelper.nonLinearDepthToLinearNormalized(this, this.$builtins.fragCoord.z);
1103
+ if (depthPass.encodeDepth) {
1104
+ this.$outputs.zFragmentOutput = encodeNormalizedFloatToRGBA(this, this.depth);
1105
+ } else if (depthPass.renderBackface) {
1106
+ this.$outputs.zFragmentOutput = pb.vec4(0, this.depth, 0, 1);
1107
+ } else {
1108
+ this.$outputs.zFragmentOutput = pb.vec4(this.depth, 0, 0, 1);
1109
+ }
1015
1110
  }
1016
1111
  if (that.drawContext.motionVectors) {
1017
1112
  if (that.featureUsed(FEATURE_DISABLE_TAA)) {
@@ -1038,13 +1133,18 @@ let FEATURE_DISABLE_TAA = 0;
1038
1133
  this.$outputs.zDistance = pb.vec4(this.worldPos, pb.distance(ShaderHelper.getCameraPosition(this), this.worldPos));
1039
1134
  }
1040
1135
  } else {
1041
- if (color) {
1136
+ const shadowMapParams = that.drawContext.shadowMapInfo.get(that.drawContext.renderPass.light);
1137
+ // Coverage-accumulating implementations read the alpha rather than test
1138
+ // it, and clipping first would consume the very quantity they integrate.
1139
+ if (color && shadowMapParams.impl.clipsCasterAlpha(shadowMapParams)) {
1042
1140
  this.$if(pb.getGlobalScope()[alphaClipFuncName](this.outColor.a, this.zAlphaCutoff), function() {
1043
1141
  pb.discard();
1044
1142
  });
1045
1143
  }
1046
- const shadowMapParams = that.drawContext.shadowMapInfo.get(that.drawContext.renderPass.light);
1047
- this.$outputs.zFragmentOutput = shadowMapParams.impl.computeShadowMapDepth(shadowMapParams, this, this.worldPos);
1144
+ this.$outputs.zFragmentOutput = shadowMapParams.impl.computeShadowMapDepth(shadowMapParams, this, this.worldPos, // Null, not a zero alpha, when no colour was computed: `outColor` is a
1145
+ // zero vector in that case, and a technique that reads alpha as coverage
1146
+ // would take it for a fully transparent fragment.
1147
+ color ? this.outColor.a : null);
1048
1148
  }
1049
1149
  });
1050
1150
  if (color) {
@@ -1101,6 +1201,7 @@ FEATURE_CULLMODE = MeshMaterial.defineFeature();
1101
1201
  FEATURE_ALPHATOCOVERAGE = MeshMaterial.defineFeature();
1102
1202
  FEATURE_ALPHADITHER = MeshMaterial.defineFeature();
1103
1203
  FEATURE_DISABLE_TAA = MeshMaterial.defineFeature();
1204
+ FEATURE_TRANSPARENT_MOTION_VECTOR = MeshMaterial.defineFeature();
1104
1205
 
1105
1206
  export { MeshMaterial, applyMaterialMixins };
1106
1207
  //# sourceMappingURL=meshmaterial.js.map