@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
@@ -21,6 +21,7 @@ const RIGID = 0;
21
21
  const SKINNED = 1;
22
22
  const DYNAMIC = 2;
23
23
  const INSTANCED = 3;
24
+ const CLOTH = 4;
24
25
 
25
26
  /** An instanced record's per-draw matrices are not read; this fills their slot. */
26
27
  const IDENTITY = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
@@ -37,6 +38,22 @@ export class MotionPass {
37
38
  /** A skinned record's two palettes, this frame's and last frame's, by record index. */
38
39
  private readonly paletteNow: (GPUTextureView | null)[] = [];
39
40
  private readonly paletteWas: (GPUTextureView | null)[] = [];
41
+ /** A cloth record's binding and its particles now and last frame, by record index. */
42
+ private readonly clothBinding: (GPUTextureView | null)[] = [];
43
+ private readonly clothNow: (GPUTextureView | null)[] = [];
44
+ private readonly clothWas: (GPUTextureView | null)[] = [];
45
+ private clothLayout: GPUBindGroupLayout | null = null;
46
+ private clothPipelineLayout: GPUPipelineLayout | null = null;
47
+ /** Keyed as `skinnedPipelines` is. */
48
+ private readonly clothPipelines = new Map<number, GPURenderPipeline>();
49
+ /**
50
+ * A bind group per particles now, particles then and binding. Weak at the top, by this frame's
51
+ * particles, so a character's groups go when its particles do; a steady garment settles on three.
52
+ */
53
+ private clothGroups = new WeakMap<
54
+ GPUTextureView,
55
+ Map<GPUTextureView, Map<GPUTextureView, GPUBindGroup>>
56
+ >();
40
57
  /** A rewritten record's last-frame rows, by record index. */
41
58
  private readonly previousRows: (GPUBuffer | null)[] = [];
42
59
  /** One pipeline per stride for rewritten meshes: current and previous rows at that stride. */
@@ -95,6 +112,28 @@ export class MotionPass {
95
112
  this.paletteWas[at] = previousPalette;
96
113
  }
97
114
 
115
+ /**
116
+ * A cloth-bound skinned draw: its skinned positions under both poses, each carried toward the
117
+ * cloth by its painted weight — this frame's `particles` and last frame's `previousParticles`.
118
+ */
119
+ recordCloth(
120
+ mesh: GpuMesh,
121
+ model: ArrayLike<number>,
122
+ previousModel: ArrayLike<number>,
123
+ palette: GPUTextureView,
124
+ previousPalette: GPUTextureView,
125
+ binding: GPUTextureView,
126
+ particles: GPUTextureView,
127
+ previousParticles: GPUTextureView,
128
+ ): void {
129
+ const at = this.take(mesh, model, previousModel, CLOTH);
130
+ this.paletteNow[at] = palette;
131
+ this.paletteWas[at] = previousPalette;
132
+ this.clothBinding[at] = binding;
133
+ this.clothNow[at] = particles;
134
+ this.clothWas[at] = previousParticles;
135
+ }
136
+
98
137
  /** A rewritten mesh: its vertices as they were, read from `previous`, under last frame's model. */
99
138
  recordDynamic(
100
139
  mesh: GpuMesh,
@@ -188,6 +227,18 @@ export class MotionPass {
188
227
  this.skinnedLayout = this.device.createPipelineLayout({
189
228
  bindGroupLayouts: [this.layout, this.paletteLayout],
190
229
  });
230
+ /* The binding and the two particle textures, read with `textureLoad` as the palettes are. */
231
+ this.clothLayout = this.device.createBindGroupLayout({
232
+ label: 'recon.motion.cloth',
233
+ entries: [0, 1, 2].map((binding) => ({
234
+ binding,
235
+ visibility: 0x1,
236
+ texture: { sampleType: 'unfilterable-float' as const },
237
+ })),
238
+ });
239
+ this.clothPipelineLayout = this.device.createPipelineLayout({
240
+ bindGroupLayouts: [this.layout, this.paletteLayout, this.clothLayout],
241
+ });
191
242
  }
192
243
 
193
244
  /**
@@ -198,20 +249,38 @@ export class MotionPass {
198
249
  */
199
250
  private skinnedPipelineFor(
200
251
  stride: number,
201
- offsets: { readonly joints: number; readonly weights: number },
252
+ offsets: {
253
+ readonly joints: number;
254
+ readonly weights: number;
255
+ readonly joints2: number;
256
+ readonly weights2: number;
257
+ },
258
+ cloth = false,
202
259
  ): GPURenderPipeline | null {
203
- const key = (stride * 4096 + offsets.joints) * 4096 + offsets.weights;
204
- const held = this.skinnedPipelines.get(key);
260
+ /* A rig of eight takes the stage that sums the second four as well. See motion.wgsl.ts. */
261
+ const eight = offsets.joints2 >= 0 && offsets.weights2 >= 0;
262
+ /*
263
+ * Five byte counts, each under 256 because a vertex is at most 172 bytes, as five base-256
264
+ * digits: exact below 2^40, so two layouts can never share a key.
265
+ */
266
+ const key =
267
+ (((stride * 256 + offsets.joints) * 256 + offsets.weights) * 256 + offsets.joints2 + 1) *
268
+ 256 +
269
+ offsets.weights2 +
270
+ 1;
271
+ const pipelines = cloth ? this.clothPipelines : this.skinnedPipelines;
272
+ const held = pipelines.get(key);
205
273
  if (held !== undefined) return held;
206
274
  const module = this.module;
207
- const layout = this.skinnedLayout;
275
+ const layout = cloth ? this.clothPipelineLayout : this.skinnedLayout;
208
276
  if (module === null || layout === null) return null;
277
+ const stage = cloth ? 'Cloth' : 'Skinned';
209
278
  const pipeline = this.device.createRenderPipeline({
210
- label: `recon.motion.skinned.${String(stride)}`,
279
+ label: `recon.motion.${cloth ? 'cloth' : 'skinned'}.${String(stride)}`,
211
280
  layout,
212
281
  vertex: {
213
282
  module,
214
- entryPoint: 'motionSkinnedVert',
283
+ entryPoint: eight ? `motion${stage}EightVert` : `motion${stage}Vert`,
215
284
  buffers: [
216
285
  {
217
286
  arrayStride: stride,
@@ -219,6 +288,12 @@ export class MotionPass {
219
288
  { shaderLocation: 0, offset: 0, format: 'float32x3' },
220
289
  { shaderLocation: 11, offset: offsets.joints, format: 'float32x4' },
221
290
  { shaderLocation: 12, offset: offsets.weights, format: 'float32x4' },
291
+ ...(eight
292
+ ? ([
293
+ { shaderLocation: 14, offset: offsets.joints2, format: 'float32x4' },
294
+ { shaderLocation: 15, offset: offsets.weights2, format: 'float32x4' },
295
+ ] as const)
296
+ : []),
222
297
  ],
223
298
  },
224
299
  ],
@@ -231,7 +306,7 @@ export class MotionPass {
231
306
  depthCompare: DEPTH_COMPARE_EQUAL,
232
307
  },
233
308
  });
234
- this.skinnedPipelines.set(key, pipeline);
309
+ pipelines.set(key, pipeline);
235
310
  return pipeline;
236
311
  }
237
312
 
@@ -342,6 +417,40 @@ export class MotionPass {
342
417
  return group;
343
418
  }
344
419
 
420
+ /** The bind group holding a cloth's binding and its two particle textures, built on first sight. */
421
+ private clothGroup(
422
+ binding: GPUTextureView,
423
+ now: GPUTextureView,
424
+ was: GPUTextureView,
425
+ ): GPUBindGroup | null {
426
+ const layout = this.clothLayout;
427
+ if (layout === null) return null;
428
+ let byWas = this.clothGroups.get(now);
429
+ if (byWas === undefined) {
430
+ byWas = new Map();
431
+ this.clothGroups.set(now, byWas);
432
+ }
433
+ let byBinding = byWas.get(was);
434
+ if (byBinding === undefined) {
435
+ byBinding = new Map();
436
+ byWas.set(was, byBinding);
437
+ }
438
+ let group = byBinding.get(binding);
439
+ if (group === undefined) {
440
+ group = this.device.createBindGroup({
441
+ label: 'recon.motion.cloth',
442
+ layout,
443
+ entries: [
444
+ { binding: 0, resource: binding },
445
+ { binding: 1, resource: now },
446
+ { binding: 2, resource: was },
447
+ ],
448
+ });
449
+ byBinding.set(binding, group);
450
+ }
451
+ return group;
452
+ }
453
+
345
454
  /** The pipeline for one vertex stride, built on first sight of it. */
346
455
  private pipelineFor(stride: number): GPURenderPipeline | null {
347
456
  const held = this.pipelines.get(stride);
@@ -497,14 +606,23 @@ export class MotionPass {
497
606
  if (previous === null) continue;
498
607
  pipeline = this.dynamicPipelineFor(mesh.vertexStride);
499
608
  pass.setVertexBuffer(1, previous);
500
- } else if (kind === SKINNED) {
609
+ } else if (kind === SKINNED || kind === CLOTH) {
501
610
  const offsets = mesh.skinOffsets;
502
611
  const now = this.paletteNow[i] ?? null;
503
612
  const was = this.paletteWas[i] ?? null;
504
613
  if (offsets === null || now === null || was === null) continue;
505
- pipeline = this.skinnedPipelineFor(mesh.vertexStride, offsets);
614
+ pipeline = this.skinnedPipelineFor(mesh.vertexStride, offsets, kind === CLOTH);
506
615
  const palettes = this.paletteGroup(now, was);
507
616
  if (pipeline === null || palettes === null) continue;
617
+ if (kind === CLOTH) {
618
+ const binding = this.clothBinding[i] ?? null;
619
+ const particles = this.clothNow[i] ?? null;
620
+ const previous = this.clothWas[i] ?? null;
621
+ if (binding === null || particles === null || previous === null) continue;
622
+ const cloth = this.clothGroup(binding, particles, previous);
623
+ if (cloth === null) continue;
624
+ pass.setBindGroup(2, cloth);
625
+ }
508
626
  pass.setBindGroup(1, palettes);
509
627
  } else {
510
628
  pipeline = this.pipelineFor(mesh.vertexStride);
@@ -526,6 +644,7 @@ export class MotionPass {
526
644
 
527
645
  dispose(): void {
528
646
  this.paletteGroups.clear();
647
+ this.clothGroups = new WeakMap();
529
648
  this.frameBuffer?.destroy();
530
649
  this.drawBuffer?.destroy();
531
650
  this.frameBuffer = null;
@@ -7,6 +7,7 @@ import type { UniformFields } from './scatterPass.ts';
7
7
  import { DEPTH_FORMAT } from './flatPass.ts';
8
8
  import type { PipelineCache } from './pipelineCache.ts';
9
9
  import { shaderModule } from './shaderModules.ts';
10
+ import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
10
11
 
11
12
  /**
12
13
  * The panel pass: two triangles in screen space, one colour, one alpha.
@@ -121,7 +122,7 @@ export function panelPipeline(
121
122
  format: cache.format,
122
123
  blend: {
123
124
  color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
124
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
125
+ alpha: SCENE_ALPHA_COVERS,
125
126
  },
126
127
  },
127
128
  ],
@@ -8,6 +8,7 @@ import { DEPTH_FORMAT } from './flatPass.ts';
8
8
  import type { PipelineCache } from './pipelineCache.ts';
9
9
  import type { UniformFields } from './scatterPass.ts';
10
10
  import { shaderModule } from './shaderModules.ts';
11
+ import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
11
12
 
12
13
  /**
13
14
  * The particle pass: a crossed pair of quads per live particle, rebuilt every frame.
@@ -367,7 +368,7 @@ export function particlePipeline(
367
368
  dstFactor: 'one-minus-src-alpha',
368
369
  operation: 'add',
369
370
  },
370
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
371
+ alpha: SCENE_ALPHA_COVERS,
371
372
  },
372
373
  },
373
374
  ],
@@ -9,6 +9,7 @@ import { DEPTH_FORMAT } from './flatPass.ts';
9
9
  import type { PipelineCache } from './pipelineCache.ts';
10
10
  import type { UniformFields } from './scatterPass.ts';
11
11
  import { shaderModule } from './shaderModules.ts';
12
+ import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
12
13
 
13
14
  /**
14
15
  * The plume pass: crossed quads per plume, displaced and faded in the shaders.
@@ -251,7 +252,7 @@ function blendState(blend: 'additive' | 'alpha'): GPUBlendState {
251
252
  }
252
253
  : {
253
254
  color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
254
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
255
+ alpha: SCENE_ALPHA_COVERS,
255
256
  };
256
257
  }
257
258
 
@@ -39,6 +39,7 @@ import {
39
39
  import type { UniformFields } from './scatterPass.ts';
40
40
  import type { PipelineCache } from './pipelineCache.ts';
41
41
  import { shaderModule } from './shaderModules.ts';
42
+ import { SCENE_ALPHA_KEEPS, SCENE_ALPHA_TRANSMITS } from '../../sceneCoverage.ts';
42
43
 
43
44
  /**
44
45
  * The composite: everything that happens to the frame after the world is drawn.
@@ -271,7 +272,7 @@ export function createPostStageLayout(
271
272
  */
272
273
  export const OIT_COMPOSITE_BLEND: GPUBlendState = {
273
274
  color: { srcFactor: 'one-minus-src-alpha', dstFactor: 'src-alpha', operation: 'add' },
274
- alpha: { srcFactor: 'one-minus-src-alpha', dstFactor: 'src-alpha', operation: 'add' },
275
+ alpha: SCENE_ALPHA_TRANSMITS,
275
276
  };
276
277
 
277
278
  /**
@@ -290,17 +291,19 @@ export const OIT_COMPOSITE_BLEND: GPUBlendState = {
290
291
  * exactly the integral — and the composite therefore never samples the colour it is modifying,
291
292
  * which is the read both backends are entitled to call undefined.
292
293
  *
293
- * Alpha is `(zero, one)`: the frame's own alpha is not the medium's to spend. The scene target
294
- * carries it to the canvas, and a medium that wrote there would make a foggy frame translucent.
294
+ * Alpha is the transmittance again, `share * a`: the scene's alpha is how much of the pixel is
295
+ * still the opaque surface, which is what the composite lets ambient occlusion darken, and a
296
+ * medium thick enough to hide a corner hides its occlusion too (`sceneCoverage.ts`). It reaches
297
+ * no canvas: both are opaque, and the composite writes 1.
295
298
  */
296
299
  export const MEDIUM_TRANSMIT_BLEND: GPUBlendState = {
297
300
  color: { srcFactor: 'one', dstFactor: 'src-alpha', operation: 'add' },
298
- alpha: { srcFactor: 'zero', dstFactor: 'one', operation: 'add' },
301
+ alpha: SCENE_ALPHA_TRANSMITS,
299
302
  };
300
303
 
301
304
  export const DECAL_MULTIPLY_BLEND: GPUBlendState = {
302
305
  color: { srcFactor: 'zero', dstFactor: 'src', operation: 'add' },
303
- alpha: { srcFactor: 'zero', dstFactor: 'src', operation: 'add' },
306
+ alpha: SCENE_ALPHA_KEEPS,
304
307
  };
305
308
 
306
309
  /**
@@ -316,6 +319,18 @@ export const PREMULTIPLIED_OVER_BLEND: GPUBlendState = {
316
319
  alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
317
320
  };
318
321
 
322
+ /**
323
+ * The same `over` for the resolve that lands a reflection on the scene, keeping the scene's alpha.
324
+ *
325
+ * A reflection is a layer of the surface it is seen in, so the surface's share of the pixel is
326
+ * unchanged (`sceneCoverage.ts`). The trace keeps `PREMULTIPLIED_OVER_BLEND`, because its target is
327
+ * the reflection's own and its alpha is the coverage this resolve reads.
328
+ */
329
+ export const REFLECTION_OVER_BLEND: GPUBlendState = {
330
+ color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
331
+ alpha: SCENE_ALPHA_KEEPS,
332
+ };
333
+
319
334
  export const ADDITIVE_BLEND: GPUBlendState = {
320
335
  color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
321
336
  alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
@@ -350,8 +365,10 @@ export function postPipelineTargets(
350
365
  key: string,
351
366
  fragment: string,
352
367
  targets: GPUColorTargetState[],
368
+ /** For a pass drawn into the frame's own multisampled colour, which must match its count. */
369
+ sampleCount = 1,
353
370
  ): GPURenderPipeline {
354
- return cache.get(key, () => describePost(device, layout, key, fragment, targets));
371
+ return cache.get(key, () => describePost(device, layout, key, fragment, targets, sampleCount));
355
372
  }
356
373
 
357
374
  function describePost(
@@ -360,8 +377,10 @@ function describePost(
360
377
  key: string,
361
378
  fragment: string,
362
379
  targets: GPUColorTargetState[],
380
+ sampleCount = 1,
363
381
  ): GPURenderPipelineDescriptor {
364
382
  return {
383
+ ...(sampleCount > 1 ? { multisample: { count: sampleCount } } : {}),
365
384
  label: key,
366
385
  layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
367
386
  vertex: {