@driftengine/core 4.8.2 → 4.8.4

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 (259) hide show
  1. package/CHANGELOG.json +127 -0
  2. package/README.md +1 -1
  3. package/dist/geometry/meshConcat.js +2 -0
  4. package/dist/index.d.ts +11 -4
  5. package/dist/index.js +22 -1
  6. package/dist/math/scalar.js +1 -1
  7. package/dist/render/anisotropicLobe.d.ts +7 -0
  8. package/dist/render/anisotropicLobe.js +33 -0
  9. package/dist/render/backend/api.d.ts +41 -4
  10. package/dist/render/backend/materialChanges.d.ts +7 -2
  11. package/dist/render/backend/materialChanges.js +8 -3
  12. package/dist/render/backend/select.js +8 -0
  13. package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
  14. package/dist/render/backend/webgl2/clothTextures.js +93 -0
  15. package/dist/render/backend/webgl2/renderer.d.ts +159 -3
  16. package/dist/render/backend/webgl2/renderer.js +662 -80
  17. package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
  18. package/dist/render/backend/webgl2/skinScatter.js +256 -0
  19. package/dist/render/backend/webgpu/buffers.d.ts +25 -11
  20. package/dist/render/backend/webgpu/buffers.js +98 -49
  21. package/dist/render/backend/webgpu/causticsPass.js +2 -1
  22. package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
  23. package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
  24. package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
  25. package/dist/render/backend/webgpu/clothTextures.js +145 -0
  26. package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
  27. package/dist/render/backend/webgpu/compressedUpload.js +38 -0
  28. package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
  29. package/dist/render/backend/webgpu/computeHost.js +5 -0
  30. package/dist/render/backend/webgpu/depthPass.d.ts +29 -9
  31. package/dist/render/backend/webgpu/depthPass.js +43 -12
  32. package/dist/render/backend/webgpu/drawCommand.js +6 -2
  33. package/dist/render/backend/webgpu/filmPass.js +2 -1
  34. package/dist/render/backend/webgpu/flatPass.d.ts +43 -4
  35. package/dist/render/backend/webgpu/flatPass.js +111 -13
  36. package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
  37. package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
  38. package/dist/render/backend/webgpu/linePass.js +2 -1
  39. package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
  40. package/dist/render/backend/webgpu/motionPass.js +106 -9
  41. package/dist/render/backend/webgpu/panelPass.js +2 -1
  42. package/dist/render/backend/webgpu/particlePass.js +2 -1
  43. package/dist/render/backend/webgpu/plumePass.js +2 -1
  44. package/dist/render/backend/webgpu/postPass.d.ts +15 -3
  45. package/dist/render/backend/webgpu/postPass.js +25 -8
  46. package/dist/render/backend/webgpu/renderer.d.ts +123 -6
  47. package/dist/render/backend/webgpu/renderer.js +534 -58
  48. package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
  49. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
  50. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
  51. package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
  52. package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
  53. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
  54. package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
  55. package/dist/render/backend/webgpu/textPass.js +2 -1
  56. package/dist/render/backend/webgpu/waterPass.js +2 -1
  57. package/dist/render/backend/webgpu/windStreakPass.js +2 -1
  58. package/dist/render/boltBatch.js +2 -1
  59. package/dist/render/causticsRenderer.js +2 -1
  60. package/dist/render/clothBindingData.d.ts +41 -0
  61. package/dist/render/clothBindingData.js +72 -0
  62. package/dist/render/clothPlacement.d.ts +25 -0
  63. package/dist/render/clothPlacement.js +85 -0
  64. package/dist/render/compressedSource.d.ts +66 -0
  65. package/dist/render/compressedSource.js +138 -0
  66. package/dist/render/cutoutCaster.d.ts +5 -1
  67. package/dist/render/cutoutCaster.js +6 -0
  68. package/dist/render/cutoutDither.d.ts +51 -0
  69. package/dist/render/cutoutDither.js +90 -0
  70. package/dist/render/decalPass.js +2 -1
  71. package/dist/render/eyeRefraction.d.ts +35 -0
  72. package/dist/render/eyeRefraction.js +40 -0
  73. package/dist/render/glCompressed.d.ts +27 -0
  74. package/dist/render/glCompressed.js +53 -0
  75. package/dist/render/globalMediumPass.js +3 -2
  76. package/dist/render/hairLobes.d.ts +70 -0
  77. package/dist/render/hairLobes.js +120 -0
  78. package/dist/render/lightBudget.d.ts +22 -0
  79. package/dist/render/lightBudget.js +22 -0
  80. package/dist/render/lineBatch.js +5 -1
  81. package/dist/render/mesh.d.ts +7 -0
  82. package/dist/render/mesh.js +24 -0
  83. package/dist/render/oitPass.js +2 -1
  84. package/dist/render/particleBatch.js +3 -2
  85. package/dist/render/particlePool.d.ts +6 -2
  86. package/dist/render/plumeRenderer.js +3 -2
  87. package/dist/render/recon/motionVectors.d.ts +4 -2
  88. package/dist/render/recon/motionVectors.js +6 -3
  89. package/dist/render/renderQuality.d.ts +29 -2
  90. package/dist/render/renderQuality.js +5 -0
  91. package/dist/render/rendererHarness.js +4 -0
  92. package/dist/render/sceneCoverage.d.ts +54 -0
  93. package/dist/render/sceneCoverage.js +72 -0
  94. package/dist/render/sceneTarget.d.ts +12 -0
  95. package/dist/render/sceneTarget.js +14 -0
  96. package/dist/render/sdfTextRenderer.js +2 -1
  97. package/dist/render/shaders/clothBinding.d.ts +22 -0
  98. package/dist/render/shaders/clothBinding.js +80 -0
  99. package/dist/render/shaders/depth.js +45 -8
  100. package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
  101. package/dist/render/shaders/flat/anisotropic.js +68 -0
  102. package/dist/render/shaders/flat/eye.d.ts +1 -0
  103. package/dist/render/shaders/flat/eye.js +81 -0
  104. package/dist/render/shaders/flat/hair.d.ts +1 -0
  105. package/dist/render/shaders/flat/hair.js +101 -0
  106. package/dist/render/shaders/flat/index.js +42 -3
  107. package/dist/render/shaders/flat/main.d.ts +1 -1
  108. package/dist/render/shaders/flat/main.js +113 -17
  109. package/dist/render/shaders/flat/models.d.ts +14 -0
  110. package/dist/render/shaders/flat/models.js +273 -0
  111. package/dist/render/shaders/flat/preamble.js +26 -24
  112. package/dist/render/shaders/flat/skin.d.ts +1 -0
  113. package/dist/render/shaders/flat/skin.js +114 -0
  114. package/dist/render/shaders/generated/depth.wgsl.d.ts +66 -24
  115. package/dist/render/shaders/generated/depth.wgsl.js +66 -24
  116. package/dist/render/shaders/generated/flat.wgsl.d.ts +1815 -1704
  117. package/dist/render/shaders/generated/flat.wgsl.js +1853 -1742
  118. package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
  119. package/dist/render/shaders/generated/line.wgsl.js +1 -1
  120. package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
  121. package/dist/render/shaders/generated/particle.wgsl.js +3 -3
  122. package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
  123. package/dist/render/shaders/generated/rush.wgsl.js +1 -1
  124. package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
  125. package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
  126. package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
  127. package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
  128. package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
  129. package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
  130. package/dist/render/shaders/line.d.ts +1 -1
  131. package/dist/render/shaders/outputTransform.d.ts +14 -1
  132. package/dist/render/shaders/outputTransform.js +23 -1
  133. package/dist/render/shaders/particle.d.ts +3 -3
  134. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  135. package/dist/render/shaders/recon/motion.wgsl.js +102 -0
  136. package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
  137. package/dist/render/shaders/rush.js +13 -2
  138. package/dist/render/shaders/sdfText.d.ts +1 -1
  139. package/dist/render/shaders/skinBlur.d.ts +1 -0
  140. package/dist/render/shaders/skinBlur.js +103 -0
  141. package/dist/render/shaders/skinning.d.ts +3 -1
  142. package/dist/render/shaders/skinning.js +31 -4
  143. package/dist/render/shaders/temporalResolve.js +12 -5
  144. package/dist/render/shadowCasters.d.ts +13 -1
  145. package/dist/render/skinBlur.d.ts +41 -0
  146. package/dist/render/skinBlur.js +49 -0
  147. package/dist/render/skinScatter.d.ts +67 -0
  148. package/dist/render/skinScatter.js +80 -0
  149. package/dist/render/skinnedClothRun.d.ts +26 -0
  150. package/dist/render/skinnedClothRun.js +84 -0
  151. package/dist/render/ssrPass.js +2 -1
  152. package/dist/render/surfaceModel.d.ts +129 -0
  153. package/dist/render/surfaceModel.js +152 -0
  154. package/dist/render/surfaceTexture.d.ts +55 -4
  155. package/dist/render/surfaceTexture.js +60 -46
  156. package/dist/render/textRenderer.js +2 -1
  157. package/dist/render/vertexDefaults.js +8 -0
  158. package/dist/render/waterRenderer.js +2 -1
  159. package/dist/render/windStreakRenderer.js +2 -1
  160. package/package.json +6 -6
  161. package/src/geometry/meshConcat.ts +2 -0
  162. package/src/index.ts +53 -1
  163. package/src/math/scalar.ts +1 -1
  164. package/src/render/anisotropicLobe.ts +41 -0
  165. package/src/render/backend/api.ts +52 -3
  166. package/src/render/backend/materialChanges.ts +8 -3
  167. package/src/render/backend/select.ts +8 -0
  168. package/src/render/backend/webgl2/clothTextures.ts +120 -0
  169. package/src/render/backend/webgl2/renderer.ts +750 -80
  170. package/src/render/backend/webgl2/skinScatter.ts +309 -0
  171. package/src/render/backend/webgpu/buffers.ts +137 -63
  172. package/src/render/backend/webgpu/causticsPass.ts +2 -1
  173. package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
  174. package/src/render/backend/webgpu/clothTextures.ts +194 -0
  175. package/src/render/backend/webgpu/compressedUpload.ts +65 -0
  176. package/src/render/backend/webgpu/computeHost.ts +25 -0
  177. package/src/render/backend/webgpu/depthPass.ts +43 -10
  178. package/src/render/backend/webgpu/drawCommand.ts +6 -2
  179. package/src/render/backend/webgpu/filmPass.ts +2 -1
  180. package/src/render/backend/webgpu/flatPass.ts +147 -8
  181. package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
  182. package/src/render/backend/webgpu/linePass.ts +2 -1
  183. package/src/render/backend/webgpu/motionPass.ts +128 -9
  184. package/src/render/backend/webgpu/panelPass.ts +2 -1
  185. package/src/render/backend/webgpu/particlePass.ts +2 -1
  186. package/src/render/backend/webgpu/plumePass.ts +2 -1
  187. package/src/render/backend/webgpu/postPass.ts +25 -6
  188. package/src/render/backend/webgpu/renderer.ts +701 -52
  189. package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
  190. package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
  191. package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
  192. package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
  193. package/src/render/backend/webgpu/textPass.ts +2 -1
  194. package/src/render/backend/webgpu/waterPass.ts +2 -1
  195. package/src/render/backend/webgpu/windStreakPass.ts +2 -1
  196. package/src/render/boltBatch.ts +2 -1
  197. package/src/render/causticsRenderer.ts +2 -1
  198. package/src/render/clothBindingData.ts +89 -0
  199. package/src/render/clothPlacement.ts +114 -0
  200. package/src/render/compressedSource.ts +198 -0
  201. package/src/render/cutoutCaster.ts +20 -2
  202. package/src/render/cutoutDither.ts +99 -0
  203. package/src/render/decalPass.ts +2 -1
  204. package/src/render/eyeRefraction.ts +72 -0
  205. package/src/render/glCompressed.ts +86 -0
  206. package/src/render/globalMediumPass.ts +3 -2
  207. package/src/render/hairLobes.ts +188 -0
  208. package/src/render/lightBudget.ts +25 -0
  209. package/src/render/lineBatch.ts +3 -1
  210. package/src/render/mesh.ts +23 -0
  211. package/src/render/oitPass.ts +2 -1
  212. package/src/render/particleBatch.ts +3 -2
  213. package/src/render/particlePool.ts +6 -2
  214. package/src/render/plumeRenderer.ts +3 -2
  215. package/src/render/recon/motionVectors.ts +6 -3
  216. package/src/render/renderQuality.ts +37 -2
  217. package/src/render/rendererHarness.ts +4 -0
  218. package/src/render/sceneCoverage.ts +78 -0
  219. package/src/render/sceneTarget.ts +21 -0
  220. package/src/render/sdfTextRenderer.ts +2 -1
  221. package/src/render/shaders/clothBinding.ts +81 -0
  222. package/src/render/shaders/depth.ts +45 -8
  223. package/src/render/shaders/flat/anisotropic.ts +68 -0
  224. package/src/render/shaders/flat/eye.ts +82 -0
  225. package/src/render/shaders/flat/hair.ts +102 -0
  226. package/src/render/shaders/flat/index.ts +42 -3
  227. package/src/render/shaders/flat/main.ts +113 -17
  228. package/src/render/shaders/flat/models.ts +286 -0
  229. package/src/render/shaders/flat/preamble.ts +26 -24
  230. package/src/render/shaders/flat/skin.ts +116 -0
  231. package/src/render/shaders/generated/depth.wgsl.ts +66 -24
  232. package/src/render/shaders/generated/flat.wgsl.ts +1853 -1742
  233. package/src/render/shaders/generated/line.wgsl.ts +1 -1
  234. package/src/render/shaders/generated/particle.wgsl.ts +3 -3
  235. package/src/render/shaders/generated/rush.wgsl.ts +1 -1
  236. package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
  237. package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
  238. package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
  239. package/src/render/shaders/outputTransform.ts +24 -1
  240. package/src/render/shaders/recon/motion.wgsl.ts +102 -0
  241. package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
  242. package/src/render/shaders/rush.ts +13 -2
  243. package/src/render/shaders/skinBlur.ts +105 -0
  244. package/src/render/shaders/skinning.ts +31 -4
  245. package/src/render/shaders/temporalResolve.ts +12 -5
  246. package/src/render/shadowCasters.ts +13 -0
  247. package/src/render/skinBlur.ts +53 -0
  248. package/src/render/skinScatter.ts +92 -0
  249. package/src/render/skinnedClothRun.ts +128 -0
  250. package/src/render/ssrPass.ts +2 -1
  251. package/src/render/surfaceModel.ts +274 -0
  252. package/src/render/surfaceTexture.ts +134 -63
  253. package/src/render/textRenderer.ts +2 -1
  254. package/src/render/vertexDefaults.ts +8 -0
  255. package/src/render/waterRenderer.ts +2 -1
  256. package/src/render/windStreakRenderer.ts +2 -1
  257. package/dist/math/exact.d.ts +0 -76
  258. package/dist/math/exact.js +0 -327
  259. package/src/math/exact.ts +0 -340
@@ -13,6 +13,7 @@ const RIGID = 0;
13
13
  const SKINNED = 1;
14
14
  const DYNAMIC = 2;
15
15
  const INSTANCED = 3;
16
+ const CLOTH = 4;
16
17
  /** An instanced record's per-draw matrices are not read; this fills their slot. */
17
18
  const IDENTITY = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
18
19
  export class MotionPass {
@@ -27,6 +28,19 @@ export class MotionPass {
27
28
  /** A skinned record's two palettes, this frame's and last frame's, by record index. */
28
29
  paletteNow = [];
29
30
  paletteWas = [];
31
+ /** A cloth record's binding and its particles now and last frame, by record index. */
32
+ clothBinding = [];
33
+ clothNow = [];
34
+ clothWas = [];
35
+ clothLayout = null;
36
+ clothPipelineLayout = null;
37
+ /** Keyed as `skinnedPipelines` is. */
38
+ clothPipelines = new Map();
39
+ /**
40
+ * A bind group per particles now, particles then and binding. Weak at the top, by this frame's
41
+ * particles, so a character's groups go when its particles do; a steady garment settles on three.
42
+ */
43
+ clothGroups = new WeakMap();
30
44
  /** A rewritten record's last-frame rows, by record index. */
31
45
  previousRows = [];
32
46
  /** One pipeline per stride for rewritten meshes: current and previous rows at that stride. */
@@ -74,6 +88,18 @@ export class MotionPass {
74
88
  this.paletteNow[at] = palette;
75
89
  this.paletteWas[at] = previousPalette;
76
90
  }
91
+ /**
92
+ * A cloth-bound skinned draw: its skinned positions under both poses, each carried toward the
93
+ * cloth by its painted weight — this frame's `particles` and last frame's `previousParticles`.
94
+ */
95
+ recordCloth(mesh, model, previousModel, palette, previousPalette, binding, particles, previousParticles) {
96
+ const at = this.take(mesh, model, previousModel, CLOTH);
97
+ this.paletteNow[at] = palette;
98
+ this.paletteWas[at] = previousPalette;
99
+ this.clothBinding[at] = binding;
100
+ this.clothNow[at] = particles;
101
+ this.clothWas[at] = previousParticles;
102
+ }
77
103
  /** A rewritten mesh: its vertices as they were, read from `previous`, under last frame's model. */
78
104
  recordDynamic(mesh, model, previousModel, previous) {
79
105
  const at = this.take(mesh, model, previousModel, DYNAMIC);
@@ -147,6 +173,18 @@ export class MotionPass {
147
173
  this.skinnedLayout = this.device.createPipelineLayout({
148
174
  bindGroupLayouts: [this.layout, this.paletteLayout],
149
175
  });
176
+ /* The binding and the two particle textures, read with `textureLoad` as the palettes are. */
177
+ this.clothLayout = this.device.createBindGroupLayout({
178
+ label: 'recon.motion.cloth',
179
+ entries: [0, 1, 2].map((binding) => ({
180
+ binding,
181
+ visibility: 0x1,
182
+ texture: { sampleType: 'unfilterable-float' },
183
+ })),
184
+ });
185
+ this.clothPipelineLayout = this.device.createPipelineLayout({
186
+ bindGroupLayouts: [this.layout, this.paletteLayout, this.clothLayout],
187
+ });
150
188
  }
151
189
  /**
152
190
  * The skinned pipeline for one stride and one placement of the joints and weights in it.
@@ -154,21 +192,32 @@ export class MotionPass {
154
192
  * Same fragment stage, winding and depth test as the rigid one — see `pipelineFor` — with the two
155
193
  * skinning attributes read from the interleaved rows at the offsets the mesh records.
156
194
  */
157
- skinnedPipelineFor(stride, offsets) {
158
- const key = (stride * 4096 + offsets.joints) * 4096 + offsets.weights;
159
- const held = this.skinnedPipelines.get(key);
195
+ skinnedPipelineFor(stride, offsets, cloth = false) {
196
+ /* A rig of eight takes the stage that sums the second four as well. See motion.wgsl.ts. */
197
+ const eight = offsets.joints2 >= 0 && offsets.weights2 >= 0;
198
+ /*
199
+ * Five byte counts, each under 256 because a vertex is at most 172 bytes, as five base-256
200
+ * digits: exact below 2^40, so two layouts can never share a key.
201
+ */
202
+ const key = (((stride * 256 + offsets.joints) * 256 + offsets.weights) * 256 + offsets.joints2 + 1) *
203
+ 256 +
204
+ offsets.weights2 +
205
+ 1;
206
+ const pipelines = cloth ? this.clothPipelines : this.skinnedPipelines;
207
+ const held = pipelines.get(key);
160
208
  if (held !== undefined)
161
209
  return held;
162
210
  const module = this.module;
163
- const layout = this.skinnedLayout;
211
+ const layout = cloth ? this.clothPipelineLayout : this.skinnedLayout;
164
212
  if (module === null || layout === null)
165
213
  return null;
214
+ const stage = cloth ? 'Cloth' : 'Skinned';
166
215
  const pipeline = this.device.createRenderPipeline({
167
- label: `recon.motion.skinned.${String(stride)}`,
216
+ label: `recon.motion.${cloth ? 'cloth' : 'skinned'}.${String(stride)}`,
168
217
  layout,
169
218
  vertex: {
170
219
  module,
171
- entryPoint: 'motionSkinnedVert',
220
+ entryPoint: eight ? `motion${stage}EightVert` : `motion${stage}Vert`,
172
221
  buffers: [
173
222
  {
174
223
  arrayStride: stride,
@@ -176,6 +225,12 @@ export class MotionPass {
176
225
  { shaderLocation: 0, offset: 0, format: 'float32x3' },
177
226
  { shaderLocation: 11, offset: offsets.joints, format: 'float32x4' },
178
227
  { shaderLocation: 12, offset: offsets.weights, format: 'float32x4' },
228
+ ...(eight
229
+ ? [
230
+ { shaderLocation: 14, offset: offsets.joints2, format: 'float32x4' },
231
+ { shaderLocation: 15, offset: offsets.weights2, format: 'float32x4' },
232
+ ]
233
+ : []),
179
234
  ],
180
235
  },
181
236
  ],
@@ -188,7 +243,7 @@ export class MotionPass {
188
243
  depthCompare: DEPTH_COMPARE_EQUAL,
189
244
  },
190
245
  });
191
- this.skinnedPipelines.set(key, pipeline);
246
+ pipelines.set(key, pipeline);
192
247
  return pipeline;
193
248
  }
194
249
  /** The rewritten pipeline for one stride: position from each of two buffers of that stride. */
@@ -300,6 +355,36 @@ export class MotionPass {
300
355
  }
301
356
  return group;
302
357
  }
358
+ /** The bind group holding a cloth's binding and its two particle textures, built on first sight. */
359
+ clothGroup(binding, now, was) {
360
+ const layout = this.clothLayout;
361
+ if (layout === null)
362
+ return null;
363
+ let byWas = this.clothGroups.get(now);
364
+ if (byWas === undefined) {
365
+ byWas = new Map();
366
+ this.clothGroups.set(now, byWas);
367
+ }
368
+ let byBinding = byWas.get(was);
369
+ if (byBinding === undefined) {
370
+ byBinding = new Map();
371
+ byWas.set(was, byBinding);
372
+ }
373
+ let group = byBinding.get(binding);
374
+ if (group === undefined) {
375
+ group = this.device.createBindGroup({
376
+ label: 'recon.motion.cloth',
377
+ layout,
378
+ entries: [
379
+ { binding: 0, resource: binding },
380
+ { binding: 1, resource: now },
381
+ { binding: 2, resource: was },
382
+ ],
383
+ });
384
+ byBinding.set(binding, group);
385
+ }
386
+ return group;
387
+ }
303
388
  /** The pipeline for one vertex stride, built on first sight of it. */
304
389
  pipelineFor(stride) {
305
390
  const held = this.pipelines.get(stride);
@@ -450,16 +535,27 @@ export class MotionPass {
450
535
  pipeline = this.dynamicPipelineFor(mesh.vertexStride);
451
536
  pass.setVertexBuffer(1, previous);
452
537
  }
453
- else if (kind === SKINNED) {
538
+ else if (kind === SKINNED || kind === CLOTH) {
454
539
  const offsets = mesh.skinOffsets;
455
540
  const now = this.paletteNow[i] ?? null;
456
541
  const was = this.paletteWas[i] ?? null;
457
542
  if (offsets === null || now === null || was === null)
458
543
  continue;
459
- pipeline = this.skinnedPipelineFor(mesh.vertexStride, offsets);
544
+ pipeline = this.skinnedPipelineFor(mesh.vertexStride, offsets, kind === CLOTH);
460
545
  const palettes = this.paletteGroup(now, was);
461
546
  if (pipeline === null || palettes === null)
462
547
  continue;
548
+ if (kind === CLOTH) {
549
+ const binding = this.clothBinding[i] ?? null;
550
+ const particles = this.clothNow[i] ?? null;
551
+ const previous = this.clothWas[i] ?? null;
552
+ if (binding === null || particles === null || previous === null)
553
+ continue;
554
+ const cloth = this.clothGroup(binding, particles, previous);
555
+ if (cloth === null)
556
+ continue;
557
+ pass.setBindGroup(2, cloth);
558
+ }
463
559
  pass.setBindGroup(1, palettes);
464
560
  }
465
561
  else {
@@ -482,6 +578,7 @@ export class MotionPass {
482
578
  }
483
579
  dispose() {
484
580
  this.paletteGroups.clear();
581
+ this.clothGroups = new WeakMap();
485
582
  this.frameBuffer?.destroy();
486
583
  this.drawBuffer?.destroy();
487
584
  this.frameBuffer = null;
@@ -1,6 +1,7 @@
1
1
  import { PANEL_BINDINGS, PANEL_FRAG_WGSL, PANEL_VERT_WGSL, } from '../../shaders/generated/panel.wgsl.js';
2
2
  import { DEPTH_FORMAT } from './flatPass.js';
3
3
  import { shaderModule } from './shaderModules.js';
4
+ import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
4
5
  /**
5
6
  * The panel pass: two triangles in screen space, one colour, one alpha.
6
7
  *
@@ -97,7 +98,7 @@ export function panelPipeline(cache, device, layout) {
97
98
  format: cache.format,
98
99
  blend: {
99
100
  color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
100
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
101
+ alpha: SCENE_ALPHA_COVERS,
101
102
  },
102
103
  },
103
104
  ],
@@ -4,6 +4,7 @@ import { PARTICLE_BINDINGS } from '../../shaders/generated/particle.wgsl.js';
4
4
  import { PARTICLE_BLADES, PARTICLE_INDICES, PARTICLE_VERTS } from '../../shaders/particle.js';
5
5
  import { DEPTH_FORMAT } from './flatPass.js';
6
6
  import { shaderModule } from './shaderModules.js';
7
+ import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
7
8
  /**
8
9
  * The particle pass: a crossed pair of quads per live particle, rebuilt every frame.
9
10
  *
@@ -269,7 +270,7 @@ export function particlePipeline(cache, device, layout, material, blend) {
269
270
  dstFactor: 'one-minus-src-alpha',
270
271
  operation: 'add',
271
272
  },
272
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
273
+ alpha: SCENE_ALPHA_COVERS,
273
274
  },
274
275
  },
275
276
  ],
@@ -6,6 +6,7 @@ import { FIRE_BINDINGS } from '../../shaders/generated/fire.wgsl.js';
6
6
  import { SMOKE_BINDINGS } from '../../shaders/generated/smoke.wgsl.js';
7
7
  import { DEPTH_FORMAT } from './flatPass.js';
8
8
  import { shaderModule } from './shaderModules.js';
9
+ import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
9
10
  /**
10
11
  * The plume pass: crossed quads per plume, displaced and faded in the shaders.
11
12
  *
@@ -179,7 +180,7 @@ function blendState(blend) {
179
180
  }
180
181
  : {
181
182
  color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
182
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
183
+ alpha: SCENE_ALPHA_COVERS,
183
184
  };
184
185
  }
185
186
  /**
@@ -156,8 +156,10 @@ export declare const OIT_COMPOSITE_BLEND: GPUBlendState;
156
156
  * exactly the integral — and the composite therefore never samples the colour it is modifying,
157
157
  * which is the read both backends are entitled to call undefined.
158
158
  *
159
- * Alpha is `(zero, one)`: the frame's own alpha is not the medium's to spend. The scene target
160
- * carries it to the canvas, and a medium that wrote there would make a foggy frame translucent.
159
+ * Alpha is the transmittance again, `share * a`: the scene's alpha is how much of the pixel is
160
+ * still the opaque surface, which is what the composite lets ambient occlusion darken, and a
161
+ * medium thick enough to hide a corner hides its occlusion too (`sceneCoverage.ts`). It reaches
162
+ * no canvas: both are opaque, and the composite writes 1.
161
163
  */
162
164
  export declare const MEDIUM_TRANSMIT_BLEND: GPUBlendState;
163
165
  export declare const DECAL_MULTIPLY_BLEND: GPUBlendState;
@@ -170,6 +172,14 @@ export declare const DECAL_MULTIPLY_BLEND: GPUBlendState;
170
172
  * sample the colour it is compositing onto.
171
173
  */
172
174
  export declare const PREMULTIPLIED_OVER_BLEND: GPUBlendState;
175
+ /**
176
+ * The same `over` for the resolve that lands a reflection on the scene, keeping the scene's alpha.
177
+ *
178
+ * A reflection is a layer of the surface it is seen in, so the surface's share of the pixel is
179
+ * unchanged (`sceneCoverage.ts`). The trace keeps `PREMULTIPLIED_OVER_BLEND`, because its target is
180
+ * the reflection's own and its alpha is the coverage this resolve reads.
181
+ */
182
+ export declare const REFLECTION_OVER_BLEND: GPUBlendState;
173
183
  export declare const ADDITIVE_BLEND: GPUBlendState;
174
184
  /** A fullscreen pass over one or more inputs, writing to one colour target. */
175
185
  export declare function postPipeline(cache: PipelineCache, device: GPUDevice, layout: GPUBindGroupLayout, key: string, fragment: string, format: GPUTextureFormat, blend?: GPUBlendState): GPURenderPipeline;
@@ -178,7 +188,9 @@ export declare function postPipeline(cache: PipelineCache, device: GPUDevice, la
178
188
  * writes its picture and each pixel's flicker record in one pass. `targets` is the caller's, held
179
189
  * rather than built per call, since this is asked for every frame.
180
190
  */
181
- export declare function postPipelineTargets(cache: PipelineCache, device: GPUDevice, layout: GPUBindGroupLayout, key: string, fragment: string, targets: GPUColorTargetState[]): GPURenderPipeline;
191
+ export declare function postPipelineTargets(cache: PipelineCache, device: GPUDevice, layout: GPUBindGroupLayout, key: string, fragment: string, targets: GPUColorTargetState[],
192
+ /** For a pass drawn into the frame's own multisampled colour, which must match its count. */
193
+ sampleCount?: number): GPURenderPipeline;
182
194
  /**
183
195
  * The temporal resolve's block and its four inputs.
184
196
  *
@@ -9,6 +9,7 @@ import { SSRTRACE_BINDINGS as SSR_BINDINGS, SSR_TRACE_FRAG_WGSL, } from '../../s
9
9
  import { SSRRESOLVE_BINDINGS as SSR_RESOLVE_BINDINGS, SSR_RESOLVE_FRAG_WGSL, } from '../../shaders/generated/ssrResolve.wgsl.js';
10
10
  import { GLOBALMEDIUM_BINDINGS as MEDIUM_BINDINGS, MEDIUM_FRAG_WGSL, MEDIUM_UPSAMPLE_FRAG_WGSL, } from '../../shaders/generated/globalMedium.wgsl.js';
11
11
  import { shaderModule } from './shaderModules.js';
12
+ import { SCENE_ALPHA_KEEPS, SCENE_ALPHA_TRANSMITS } from '../../sceneCoverage.js';
12
13
  /**
13
14
  * The composite: everything that happens to the frame after the world is drawn.
14
15
  *
@@ -197,7 +198,7 @@ export function createPostStageLayout(device, uniformBinding, uniformSize, textu
197
198
  */
198
199
  export const OIT_COMPOSITE_BLEND = {
199
200
  color: { srcFactor: 'one-minus-src-alpha', dstFactor: 'src-alpha', operation: 'add' },
200
- alpha: { srcFactor: 'one-minus-src-alpha', dstFactor: 'src-alpha', operation: 'add' },
201
+ alpha: SCENE_ALPHA_TRANSMITS,
201
202
  };
202
203
  /**
203
204
  * What a drawn decal does to the pixel under it: `dst * src`.
@@ -215,16 +216,18 @@ export const OIT_COMPOSITE_BLEND = {
215
216
  * exactly the integral — and the composite therefore never samples the colour it is modifying,
216
217
  * which is the read both backends are entitled to call undefined.
217
218
  *
218
- * Alpha is `(zero, one)`: the frame's own alpha is not the medium's to spend. The scene target
219
- * carries it to the canvas, and a medium that wrote there would make a foggy frame translucent.
219
+ * Alpha is the transmittance again, `share * a`: the scene's alpha is how much of the pixel is
220
+ * still the opaque surface, which is what the composite lets ambient occlusion darken, and a
221
+ * medium thick enough to hide a corner hides its occlusion too (`sceneCoverage.ts`). It reaches
222
+ * no canvas: both are opaque, and the composite writes 1.
220
223
  */
221
224
  export const MEDIUM_TRANSMIT_BLEND = {
222
225
  color: { srcFactor: 'one', dstFactor: 'src-alpha', operation: 'add' },
223
- alpha: { srcFactor: 'zero', dstFactor: 'one', operation: 'add' },
226
+ alpha: SCENE_ALPHA_TRANSMITS,
224
227
  };
225
228
  export const DECAL_MULTIPLY_BLEND = {
226
229
  color: { srcFactor: 'zero', dstFactor: 'src', operation: 'add' },
227
- alpha: { srcFactor: 'zero', dstFactor: 'src', operation: 'add' },
230
+ alpha: SCENE_ALPHA_KEEPS,
228
231
  };
229
232
  /**
230
233
  * `over`, against colour a pass has already multiplied by its own coverage.
@@ -238,6 +241,17 @@ export const PREMULTIPLIED_OVER_BLEND = {
238
241
  color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
239
242
  alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
240
243
  };
244
+ /**
245
+ * The same `over` for the resolve that lands a reflection on the scene, keeping the scene's alpha.
246
+ *
247
+ * A reflection is a layer of the surface it is seen in, so the surface's share of the pixel is
248
+ * unchanged (`sceneCoverage.ts`). The trace keeps `PREMULTIPLIED_OVER_BLEND`, because its target is
249
+ * the reflection's own and its alpha is the coverage this resolve reads.
250
+ */
251
+ export const REFLECTION_OVER_BLEND = {
252
+ color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
253
+ alpha: SCENE_ALPHA_KEEPS,
254
+ };
241
255
  export const ADDITIVE_BLEND = {
242
256
  color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
243
257
  alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
@@ -253,11 +267,14 @@ export function postPipeline(cache, device, layout, key, fragment, format, blend
253
267
  * writes its picture and each pixel's flicker record in one pass. `targets` is the caller's, held
254
268
  * rather than built per call, since this is asked for every frame.
255
269
  */
256
- export function postPipelineTargets(cache, device, layout, key, fragment, targets) {
257
- return cache.get(key, () => describePost(device, layout, key, fragment, targets));
270
+ export function postPipelineTargets(cache, device, layout, key, fragment, targets,
271
+ /** For a pass drawn into the frame's own multisampled colour, which must match its count. */
272
+ sampleCount = 1) {
273
+ return cache.get(key, () => describePost(device, layout, key, fragment, targets, sampleCount));
258
274
  }
259
- function describePost(device, layout, key, fragment, targets) {
275
+ function describePost(device, layout, key, fragment, targets, sampleCount = 1) {
260
276
  return {
277
+ ...(sampleCount > 1 ? { multisample: { count: sampleCount } } : {}),
261
278
  label: key,
262
279
  layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
263
280
  vertex: {
@@ -19,6 +19,10 @@ import { type ShadowLight } from '../../pointShadowSystem.ts';
19
19
  import { GpuText } from './textPass.ts';
20
20
  import type { TextStyle } from '../../textLayout.ts';
21
21
  import { GpuSurfaceTexture } from './surfaceTexturePass.ts';
22
+ import { GpuClothBinding, GpuClothParticles } from './clothTextures.ts';
23
+ import type { ComputeHost } from './computeHost.ts';
24
+ import type { ClothBindingData } from '../../clothBindingData.ts';
25
+ import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
22
26
  import { GpuSdfText } from './sdfTextPass.ts';
23
27
  import type { SdfFont } from '../../sdfFont.ts';
24
28
  import type { SdfTextStyle } from '../../sdfTextLayout.ts';
@@ -151,6 +155,12 @@ export declare class WebGPURenderer implements RendererApi {
151
155
  private readonly skinnedGroupsBeside;
152
156
  private readonly skinnedGroupsPalette;
153
157
  private readonly skinnedGroupsDeltas;
158
+ /** The cloth views each skinned twin was built with, compared by identity like the palette. */
159
+ private readonly skinnedGroupsCloth;
160
+ private readonly skinnedGroupsClothBinding;
161
+ /** The cloth the next skinned draws are placed by, or null. See `setCloth`. */
162
+ private cloth;
163
+ private clothStandCache;
154
164
  /**
155
165
  * Which palette slot the following draws skin by, or -1 for none.
156
166
  *
@@ -498,6 +508,16 @@ export declare class WebGPURenderer implements RendererApi {
498
508
  private refractSnapshotView;
499
509
  /** Whether this frame has taken it. One snapshot serves every refracting draw. */
500
510
  private refractSnapshotTaken;
511
+ /**
512
+ * Skin's screen-space scattering, made where the profile asks for it and has a composite to add
513
+ * into, and null otherwise — which is every profile by default. See `SkinScatterPass`.
514
+ */
515
+ private readonly skinScatter;
516
+ /** The pipelines skin's diffuse half is built for: the blur target's format, the frame's samples. */
517
+ private readonly skinPipelines;
518
+ /** Whether this frame has spread its skin: once, at its first blended draw or before its end. */
519
+ private skinScattered;
520
+ private readonly skinInverseProjection;
501
521
  private sceneColorView;
502
522
  /** The multisampled twin, when the profile asks for samples. Resolved into `sceneColor`. */
503
523
  private sceneColorMsaa;
@@ -676,6 +696,17 @@ export declare class WebGPURenderer implements RendererApi {
676
696
  private temporalJitterX;
677
697
  private temporalJitterY;
678
698
  private temporalJittering;
699
+ /**
700
+ * The frame a dithered cutout's pattern is offset by, advanced at `beginFrame` and wrapped at 64.
701
+ * See `cutoutDither.ts`.
702
+ */
703
+ private cutoutFrame;
704
+ /**
705
+ * How the current material's cutout edge is drawn this frame, decided when the material is staged
706
+ * — after `bindMeshPass` has settled whether the frame is temporal — and read by every draw: a
707
+ * coverage edge is pipeline state here (`|a2c`), and a blended draw tests hard.
708
+ */
709
+ private cutoutResolveStaged;
679
710
  /**
680
711
  * Whether this frame is being reconstructed, settled once in `bindMeshPass`.
681
712
  *
@@ -1082,6 +1113,12 @@ export declare class WebGPURenderer implements RendererApi {
1082
1113
  private ormMap;
1083
1114
  /** The caller's emissive map, or null for the stand-in. See `setMaterial`. */
1084
1115
  private emissiveMap;
1116
+ /** The material's model map, or null for the stand-in. See `setMaterial` and `surfaceModel.ts`. */
1117
+ private modelMap;
1118
+ /** The material's shading model, which picks the lit pipeline: null the standard. */
1119
+ private materialModel;
1120
+ /** Its numbers, `uModelParams`, packed by `packModel`. */
1121
+ private readonly modelParams;
1085
1122
  /**
1086
1123
  * The flat group for a material, keyed on **all four** of its maps.
1087
1124
  *
@@ -1113,13 +1150,27 @@ export declare class WebGPURenderer implements RendererApi {
1113
1150
  private blankAlbedoBindGroup;
1114
1151
  /** Repeats across the mesh's own UV range. Per draw, because `uUvScale` is a vertex uniform. */
1115
1152
  private readonly uvScale;
1153
+ /** Where the texture starts, after the scale. Per draw, beside `uvScale`. */
1154
+ private readonly uvOffset;
1155
+ /**
1156
+ * How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
1157
+ * each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
1158
+ */
1159
+ private surfaceFog;
1116
1160
  /** Whether the bound material is seen from both faces. See `SurfaceMaterial.doubleSided`. */
1117
1161
  private materialDoubleSided;
1118
1162
  /** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
1119
1163
  private materialHasMaps;
1120
- createSurfaceTexture(source: TexImageSource, options?: SurfaceTextureOptions): GpuSurfaceTexture;
1164
+ createSurfaceTexture(source: SurfaceSource, options?: SurfaceTextureOptions): GpuSurfaceTexture;
1165
+ private compressedCache;
1166
+ /**
1167
+ * The BC formats this device takes as blocks: every one where the device carries
1168
+ * `texture-compression-bc`, which `select.ts` asks for wherever the adapter offers it, and none
1169
+ * where it does not. See `Renderer.compressedFormats`.
1170
+ */
1171
+ get compressedFormats(): readonly CompressedTextureFormat[];
1121
1172
  /** Several images of one size as an array's layers. See `RendererApi.createSurfaceTextureArray`. */
1122
- createSurfaceTextureArray(sources: readonly TexImageSource[], options?: SurfaceTextureOptions): GpuSurfaceTexture;
1173
+ createSurfaceTextureArray(sources: readonly SurfaceSource[], options?: SurfaceTextureOptions): GpuSurfaceTexture;
1123
1174
  /**
1124
1175
  * GPU textures an `updateSurfaceTexture` replaced, destroyed when the next frame begins.
1125
1176
  *
@@ -1152,6 +1203,40 @@ export declare class WebGPURenderer implements RendererApi {
1152
1203
  * surface must be answered by every backend, and answered honestly, per the 2026-08-13 rule.
1153
1204
  */
1154
1205
  setSkinPalette(palette: Float32Array | null): void;
1206
+ /**
1207
+ * A mesh's cloth binding. See `RendererApi.createClothBinding`. The pipelines that draw it bound
1208
+ * are warmed here, every way this mesh can be drawn, so none compiles inside the first frame
1209
+ * that draws a garment.
1210
+ */
1211
+ createClothBinding(mesh: GpuMesh, data: ClothBindingData): GpuClothBinding;
1212
+ /** One character's particles. See `RendererApi.createClothParticles`. */
1213
+ createClothParticles(count: number): GpuClothParticles;
1214
+ /**
1215
+ * This frame's particles. **Once a frame, before the character's draws**: a texture write lands
1216
+ * ahead of every draw of the frame, so a second update between two draws would reach both.
1217
+ */
1218
+ updateClothParticles(particles: GpuClothParticles, positions: Float32Array): void;
1219
+ /** Place the following skinned draws by a cloth, or by none. See `RendererApi.setCloth`. */
1220
+ setCloth(binding: GpuClothBinding | null, particles?: GpuClothParticles | null): void;
1221
+ /** Retired rather than destroyed: a draw recorded earlier in this frame may still read them. */
1222
+ disposeClothBinding(binding: GpuClothBinding): void;
1223
+ disposeClothParticles(particles: GpuClothParticles): void;
1224
+ /**
1225
+ * The device and the frame's bookkeeping, for compute a consumer opts into by importing it —
1226
+ * `createSkinnedCloth` — rather than for anything this renderer runs itself. See `computeHost.ts`.
1227
+ */
1228
+ computeHost(): ComputeHost;
1229
+ private readonly computeHostCache;
1230
+ /**
1231
+ * An eye's axis in the world for this draw, into the open material's \`uModelParams\`, and a new
1232
+ * material slot when it moved: the axis is per draw, and a slot already taken by an earlier draw
1233
+ * of the same eye must not be written under it. See \`eyeAxisInWorld\`.
1234
+ */
1235
+ private writeEyeAxis;
1236
+ /** The views the draw's cloth binds, or the stand-in's where no cloth is set. */
1237
+ private clothViews;
1238
+ /** The one-texel stand-in a skinned group binds where there is no cloth, made on first need. */
1239
+ private clothStand;
1155
1240
  /**
1156
1241
  * Choose the morph weights the following `drawMesh` calls deform by, or null for none.
1157
1242
  *
@@ -1198,6 +1283,8 @@ export declare class WebGPURenderer implements RendererApi {
1198
1283
  /** How bright the environment the surfaces drawn next reflect is. See `renderer.ts`. */
1199
1284
  setEnvironmentGain(gain: number): void;
1200
1285
  setSurfaceGrain(amount: number): void;
1286
+ /** Whether the surfaces drawn next meet the medium. See `renderer.ts`. */
1287
+ setSurfaceFog(enabled: boolean): void;
1201
1288
  /**
1202
1289
  * How much of the surfaces drawn next survives a screen-door dither. See `renderer.ts`.
1203
1290
  *
@@ -1716,6 +1803,8 @@ export declare class WebGPURenderer implements RendererApi {
1716
1803
  private readonly shadowSkinnedGroups;
1717
1804
  private readonly shadowSkinnedGroupsBeside;
1718
1805
  private readonly shadowSkinnedGroupsPalette;
1806
+ private readonly shadowSkinnedGroupsCloth;
1807
+ private readonly shadowSkinnedGroupsClothBinding;
1719
1808
  /** Held from construction so the skinned group can be rebuilt without them being threaded. */
1720
1809
  private shadowBlankView;
1721
1810
  private shadowBlankSampler;
@@ -1816,7 +1905,7 @@ export declare class WebGPURenderer implements RendererApi {
1816
1905
  */
1817
1906
  /** The cutout bind group for `texture` in the open layer, built the first time it casts there. */
1818
1907
  private cutoutGroup;
1819
- /** The cutoff and UV scale into a cutout draw's slot, at the offsets its own variant declares. */
1908
+ /** The cutoff and UV scale and offset into a cutout draw's slot, at its own variant's offsets. */
1820
1909
  private writeCutout;
1821
1910
  private shadowGroup;
1822
1911
  /**
@@ -2084,9 +2173,9 @@ export declare class WebGPURenderer implements RendererApi {
2084
2173
  /**
2085
2174
  * Rewrite a mesh's positions, and its normals where the caller has them.
2086
2175
  *
2087
- * Refused in words on a mesh that did not declare itself dynamic, because the thing that makes
2088
- * an update possible on this backend — the interleaved array the vertex data was built from —
2089
- * is only kept when a consumer asked for it. See `GpuMesh.update`.
2176
+ * Refused in words on a mesh that did not declare itself dynamic, because what makes an update
2177
+ * cheap on this backend — positions and normals in buffers of their own — is a layout chosen at
2178
+ * creation. See `GpuMesh.update`.
2090
2179
  */
2091
2180
  updateMesh(mesh: GpuMesh, positions: Float32Array, normals?: Float32Array): void;
2092
2181
  /**
@@ -2666,6 +2755,13 @@ export declare class WebGPURenderer implements RendererApi {
2666
2755
  * it before the motion pass reads it. See `SkinPaletteRing`.
2667
2756
  */
2668
2757
  private recordSkinnedMotion;
2758
+ /**
2759
+ * A cloth-bound draw's motion. **Recorded even where the caller named no previous place**, as a
2760
+ * rewritten mesh's is: the particles carry their own past, and a garment swinging on a character
2761
+ * standing still is motion nobody else can see. Where the rig is a `Mover` its last pose is taken
2762
+ * as a skinned mover's is; otherwise the rig is taken not to have moved.
2763
+ */
2764
+ private recordClothMotion;
2669
2765
  private warnMoverTwice;
2670
2766
  /**
2671
2767
  * Where every mover was last frame, into the motion target. See `MotionPass`.
@@ -2840,6 +2936,27 @@ export declare class WebGPURenderer implements RendererApi {
2840
2936
  * glass, and a frame that refracts nothing breaks no pass at all.
2841
2937
  */
2842
2938
  private takeRefractSnapshot;
2939
+ /**
2940
+ * Whether a skin draw now draws its two halves: the profile asked, the frame has not spread its
2941
+ * skin yet, and this is the frame's own picture — not a mirror's, a probe's, an overlay after
2942
+ * the frame, or an order-independent set, none of which the spread reads.
2943
+ */
2944
+ private skinSplits;
2945
+ /**
2946
+ * A skin draw's diffuse half, kept for the spread: the frame's draw again, with the same slots,
2947
+ * buffers and bind group, through the pipeline that writes the diffuse alone into the blur's
2948
+ * target and tests for the depth its frame half wrote.
2949
+ */
2950
+ private keepSkinDiffuse;
2951
+ /**
2952
+ * Spread the frame's skin and add it back: once, at the first blended draw or before the frame's
2953
+ * last flush. **A fifth instance of the boundary `takeRefractSnapshot` describes** — flush, end
2954
+ * the pass, work between passes, reopen with `loadOp: 'load'` — and refused where that one is,
2955
+ * for its reasons. Between the passes: the kept diffuse halves into their target against the
2956
+ * frame's depth, the depth resolved, and `SkinScatterPass.spread` across and then down into the
2957
+ * frame's colour.
2958
+ */
2959
+ private runSkinScatter;
2843
2960
  private takeVolumeDepth;
2844
2961
  /**
2845
2962
  * The pass to record into, reopening one on the canvas if the frame has already gone.