@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
@@ -0,0 +1,194 @@
1
+ /**
2
+ * A cloth binding's textures on WebGPU: the binding and the rest particles once, a mesh's; the
3
+ * particles every frame, a character's — the twin of `webgl2/clothTextures.ts`.
4
+ *
5
+ * `rgba32float`, read by `textureLoad` in the vertex stage, so bound as `unfilterable-float` and
6
+ * never sampled. Two particle textures that swap on each update, so last frame's positions stay for
7
+ * a motion vector with no copy. **Written with `writeTexture`, whose queue order is the trap the
8
+ * per-draw rings exist for**: an update lands before every draw of the frame, so a character's
9
+ * particles are updated once a frame, before its draws, never between two draws of it.
10
+ *
11
+ * `clothStandIn` is what a skinned pipeline binds where no cloth is set: its layout declares the
12
+ * three textures whatever the draw, and WebGPU binds every declared texture.
13
+ */
14
+ import { clothTextureSize, packClothBinding, packClothParticles } from '../../clothBindingData.ts';
15
+ import type { ClothBindingData } from '../../clothBindingData.ts';
16
+
17
+ const USAGE = 0x4 | 0x2; // TEXTURE_BINDING | COPY_DST
18
+ const STORAGE_BINDING = 0x8;
19
+
20
+ function floatTexture(
21
+ device: GPUDevice,
22
+ label: string,
23
+ width: number,
24
+ height: number,
25
+ texels: Float32Array | null,
26
+ storage = false,
27
+ ): GPUTexture {
28
+ const texture = device.createTexture({
29
+ label,
30
+ size: [width, height],
31
+ format: 'rgba32float',
32
+ usage: storage ? USAGE | STORAGE_BINDING : USAGE,
33
+ });
34
+ if (texels !== null) {
35
+ device.queue.writeTexture(
36
+ { texture },
37
+ texels as Float32Array<ArrayBuffer>,
38
+ { bytesPerRow: width * 16, rowsPerImage: height },
39
+ { width, height },
40
+ );
41
+ }
42
+ return texture;
43
+ }
44
+
45
+ /** The three views a skinned bind group takes for its cloth. */
46
+ export interface ClothViews {
47
+ readonly binding: GPUTextureView;
48
+ readonly particles: GPUTextureView;
49
+ readonly rest: GPUTextureView;
50
+ }
51
+
52
+ /** A one-texel float texture, as all three views, for every draw with no cloth. */
53
+ export function clothStandIn(device: GPUDevice): { texture: GPUTexture; views: ClothViews } {
54
+ const texture = floatTexture(device, 'cloth.standIn', 1, 1, new Float32Array(4));
55
+ const view = texture.createView();
56
+ return { texture, views: { binding: view, particles: view, rest: view } };
57
+ }
58
+
59
+ /** A mesh's binding and its particles at rest. */
60
+ export class GpuClothBinding {
61
+ readonly binding: GPUTexture;
62
+ readonly rest: GPUTexture;
63
+ readonly bindingView: GPUTextureView;
64
+ readonly restView: GPUTextureView;
65
+ readonly particles: number;
66
+
67
+ constructor(device: GPUDevice, data: ClothBindingData) {
68
+ const size = clothTextureSize(data.weights.length * 2);
69
+ this.binding = floatTexture(
70
+ device,
71
+ 'cloth.binding',
72
+ size.width,
73
+ size.height,
74
+ packClothBinding(data),
75
+ );
76
+ this.particles = data.rest.length / 3;
77
+ const rest = clothTextureSize(this.particles);
78
+ const texels = new Float32Array(rest.width * rest.height * 4);
79
+ packClothParticles(data.rest, texels);
80
+ this.rest = floatTexture(device, 'cloth.rest', rest.width, rest.height, texels);
81
+ this.bindingView = this.binding.createView();
82
+ this.restView = this.rest.createView();
83
+ }
84
+
85
+ /** Its textures, for the renderer to retire once no draw of this frame can read them. */
86
+ get textures(): readonly GPUTexture[] {
87
+ return [this.binding, this.rest];
88
+ }
89
+ }
90
+
91
+ /**
92
+ * One character's particles: this frame's and the last, in two textures that swap. Written from
93
+ * the CPU with `update`, or — `storage` — by the device solver's publish pass, `publish` then
94
+ * saying which texture it writes.
95
+ */
96
+ export class GpuClothParticles {
97
+ readonly count: number;
98
+ /** Texels a row, which the publish pass wraps a particle index by. */
99
+ readonly width: number;
100
+ private readonly height: number;
101
+ private readonly staging: Float32Array<ArrayBuffer>;
102
+ /** Both textures, for the renderer to retire once no draw of this frame can read them. */
103
+ readonly textures: readonly [GPUTexture, GPUTexture];
104
+ private readonly views: [GPUTextureView, GPUTextureView];
105
+ private latest = 0;
106
+ /** The frame each texture was last written for, so a motion knows whether the other is last frame's. */
107
+ private readonly written = [-1, -1];
108
+ private paired: GpuClothBinding | null = null;
109
+ private pairedViews: ClothViews[] = [];
110
+
111
+ constructor(device: GPUDevice, count: number, storage = false) {
112
+ this.count = count;
113
+ const size = clothTextureSize(count);
114
+ this.width = size.width;
115
+ this.height = size.height;
116
+ this.staging = new Float32Array(size.width * size.height * 4);
117
+ this.textures = [
118
+ floatTexture(device, 'cloth.particles', size.width, size.height, this.staging, storage),
119
+ floatTexture(device, 'cloth.particles', size.width, size.height, this.staging, storage),
120
+ ];
121
+ this.views = [this.textures[0].createView(), this.textures[1].createView()];
122
+ }
123
+
124
+ /** This frame's particles. */
125
+ get current(): GPUTextureView {
126
+ return this.views[this.latest] as GPUTextureView;
127
+ }
128
+
129
+ /** Last frame's. */
130
+ get previous(): GPUTextureView {
131
+ return this.views[1 - this.latest] as GPUTextureView;
132
+ }
133
+
134
+ /**
135
+ * Where they were last frame, for a motion vector at `frame`: the other texture where it was
136
+ * written the frame before this one's update, and this frame's otherwise — particles not updated
137
+ * this frame did not move, and a first update has no past to measure from.
138
+ */
139
+ previousAt(frame: number): GPUTextureView {
140
+ const fresh =
141
+ this.written[this.latest] === frame && this.written[1 - this.latest] === frame - 1;
142
+ return fresh ? this.previous : this.current;
143
+ }
144
+
145
+ /** A texture's view, by index: the publish pass binds both, one at a time. */
146
+ viewOf(k: number): GPUTextureView {
147
+ return this.views[k] as GPUTextureView;
148
+ }
149
+
150
+ /**
151
+ * The device is about to write the particles of `frame`: the older texture becomes this frame's,
152
+ * as an `update` makes it, and its index is returned for the pass that writes it.
153
+ */
154
+ publish(frame: number): number {
155
+ this.latest = 1 - this.latest;
156
+ this.written[this.latest] = frame;
157
+ return this.latest;
158
+ }
159
+
160
+ /** Write `positions` as the particles of `frame`; last frame's stays. */
161
+ update(device: GPUDevice, positions: Float32Array, frame: number): void {
162
+ if (positions.length !== this.count * 3) {
163
+ throw new Error(
164
+ `cloth: ${this.count} particles and an update of ${positions.length / 3}; a cloth's ` +
165
+ 'particle count is fixed when its particles are created',
166
+ );
167
+ }
168
+ packClothParticles(positions, this.staging);
169
+ this.latest = 1 - this.latest;
170
+ this.written[this.latest] = frame;
171
+ device.queue.writeTexture(
172
+ { texture: this.textures[this.latest] as GPUTexture },
173
+ this.staging,
174
+ { bytesPerRow: this.width * 16, rowsPerImage: this.height },
175
+ { width: this.width, height: this.height },
176
+ );
177
+ }
178
+
179
+ /**
180
+ * The three views a draw of `binding` with these particles binds, kept so a cached bind group
181
+ * compares equal frame after frame. Two objects, one for each texture that is current.
182
+ */
183
+ viewsWith(binding: GpuClothBinding): ClothViews {
184
+ if (this.paired !== binding) {
185
+ this.paired = binding;
186
+ this.pairedViews = [0, 1].map((k) => ({
187
+ binding: binding.bindingView,
188
+ particles: this.views[k] as GPUTextureView,
189
+ rest: binding.restView,
190
+ }));
191
+ }
192
+ return this.pairedViews[this.latest] as ClothViews;
193
+ }
194
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * BC on WebGPU: which formats this device samples, and the upload of a stored chain.
3
+ *
4
+ * **One feature covers every BC format**, `texture-compression-bc`, which `select.ts` asks for
5
+ * wherever the adapter offers it and never demands: a device without it is still a device, and the
6
+ * loader decodes for it. Desktop adapters have it; phones have ASTC and ETC2 instead.
7
+ *
8
+ * **Written with `writeTexture`, level by level**, because `copyExternalImageToTexture` takes images
9
+ * and a compressed format cannot be a render attachment, so neither the image upload nor the mip
10
+ * blit applies. Each level's copy is whole blocks: a level smaller than a block is still one, and
11
+ * its copy extent is the block's four texels — WebGPU's physical size — rather than the one or two
12
+ * the level nominally has.
13
+ */
14
+ import type { CompressedTextureFormat, CompressedTextureSource } from '../../compressedSource.ts';
15
+ import { BLOCK_BYTES } from '../../compressedSource.ts';
16
+
17
+ const GPU_FORMAT: Readonly<Record<CompressedTextureFormat, GPUTextureFormat>> = {
18
+ bc1: 'bc1-rgba-unorm',
19
+ 'bc1-srgb': 'bc1-rgba-unorm-srgb',
20
+ bc2: 'bc2-rgba-unorm',
21
+ 'bc2-srgb': 'bc2-rgba-unorm-srgb',
22
+ bc3: 'bc3-rgba-unorm',
23
+ 'bc3-srgb': 'bc3-rgba-unorm-srgb',
24
+ bc4: 'bc4-r-unorm',
25
+ bc5: 'bc5-rg-unorm',
26
+ bc7: 'bc7-rgba-unorm',
27
+ 'bc7-srgb': 'bc7-rgba-unorm-srgb',
28
+ };
29
+
30
+ /** Every BC format, where the device carries the feature; none where it does not. */
31
+ export function gpuCompressedFormats(device: GPUDevice): CompressedTextureFormat[] {
32
+ return device.features.has('texture-compression-bc')
33
+ ? (Object.keys(GPU_FORMAT) as CompressedTextureFormat[])
34
+ : [];
35
+ }
36
+
37
+ /** A texture holding `layers` as `name`, `levels` levels of each, written as they are. */
38
+ export function createCompressedTexture(
39
+ device: GPUDevice,
40
+ name: CompressedTextureFormat,
41
+ layers: readonly CompressedTextureSource[],
42
+ levels: number,
43
+ ): GPUTexture {
44
+ const { format, width, height } = layers[0] as CompressedTextureSource;
45
+ const texture = device.createTexture({
46
+ label: 'surface.texture.bc',
47
+ size: [width, height, layers.length],
48
+ format: GPU_FORMAT[name],
49
+ mipLevelCount: levels,
50
+ usage: 0x2 | 0x4, // COPY_DST | TEXTURE_BINDING
51
+ });
52
+ for (let level = 0; level < levels; level++) {
53
+ const across = Math.ceil(Math.max(1, width >> level) / 4);
54
+ const down = Math.ceil(Math.max(1, height >> level) / 4);
55
+ for (let layer = 0; layer < layers.length; layer++) {
56
+ device.queue.writeTexture(
57
+ { texture, mipLevel: level, origin: [0, 0, layer] },
58
+ (layers[layer] as CompressedTextureSource).levels[level] as Uint8Array<ArrayBuffer>,
59
+ { bytesPerRow: across * BLOCK_BYTES[format], rowsPerImage: down },
60
+ { width: across * 4, height: down * 4, depthOrArrayLayers: 1 },
61
+ );
62
+ }
63
+ }
64
+ return texture;
65
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * What compute a consumer opts into needs from the WebGPU renderer: its device, whether the device
3
+ * is gone, which frame a change made now belongs to, and a place to retire textures the frame being
4
+ * recorded may still read.
5
+ *
6
+ * **Why a door rather than renderer methods.** A skinned cloth's solver is fifteen kilobytes
7
+ * gzipped of compute, WGSL and the CPU solver beneath it, and a renderer method that constructs it
8
+ * is a static reference no bundler can shake — every game would ship a garment solver whether or
9
+ * not it draws a garment. Behind this, `createSkinnedCloth` is a barrel function, and only a game
10
+ * that imports it carries it. **What it gives up**: a function reaching the renderer through a door
11
+ * the public surface does not show; the door stays this narrow so that is all it reaches.
12
+ */
13
+ export interface ComputeHost {
14
+ device(): GPUDevice;
15
+ lost(): boolean;
16
+ /** The frame a change made now is drawn in: this one inside a frame, the next between frames. */
17
+ changeFrame(): number;
18
+ retire(textures: readonly GPUTexture[]): void;
19
+ }
20
+
21
+ /** The host of a renderer that has one: the WebGPU backend, and nothing else. */
22
+ export function computeHostOf(renderer: object): ComputeHost | null {
23
+ const asked = (renderer as { computeHost?: () => ComputeHost }).computeHost;
24
+ return typeof asked === 'function' ? asked.call(renderer) : null;
25
+ }
@@ -9,8 +9,10 @@ import {
9
9
  DEPTH_VERT_WGSL,
10
10
  } from '../../shaders/generated/depth.wgsl.ts';
11
11
  import { vertexBufferLayouts } from './buffers.ts';
12
+ import { vertexConstants } from './flatPass.ts';
12
13
  import type { PipelineCache } from './pipelineCache.ts';
13
14
  import { shaderModule } from './shaderModules.ts';
15
+ import type { ClothViews } from './clothTextures.ts';
14
16
 
15
17
  /**
16
18
  * The shadow pass: the world drawn from the light, into a depth map.
@@ -24,9 +26,10 @@ export const DEPTH_VERT_FIELDS = DEPTH_BINDINGS.DEPTH_VERT.fields;
24
26
  export const DEPTH_VERT_SIZE = DEPTH_BINDINGS.DEPTH_VERT.uniformSize;
25
27
  export const DEPTH_FRAG_SIZE = DEPTH_BINDINGS.DEPTH_FRAG.uniformSize;
26
28
  /*
27
- * The cutout variants' vertex blocks. The rigid one is the plain block with `uUvScale` and
28
- * `uAlphaCutout` after every other field, so a slot written for it is written with the plain
29
- * offsets plus these two; the instanced one has no model and no wind, so its two sit earlier.
29
+ * The cutout variants' vertex blocks. The rigid one is the plain block with `uUvScale`,
30
+ * `uUvOffset` and `uAlphaCutout` after every other field, so a slot written for it is written with
31
+ * the plain offsets plus these three; the instanced one has no model and no wind, so its three sit
32
+ * earlier.
30
33
  */
31
34
  export const DEPTH_CUTOUT_VERT_FIELDS = DEPTH_BINDINGS.DEPTH_CUTOUT_VERT.fields;
32
35
  export const DEPTH_CUTOUT_VERT_SIZE = DEPTH_BINDINGS.DEPTH_CUTOUT_VERT.uniformSize;
@@ -42,6 +45,12 @@ const FRAG_BINDING = DEPTH_BINDINGS.DEPTH_FRAG.uniforms;
42
45
  const PREVIOUS = DEPTH_BINDINGS.DEPTH_FRAG.textures.uPreviousShadowMap;
43
46
  /** Where the skinned vertex stage reads the joint palette. Generated, not chosen here. */
44
47
  export const DEPTH_PALETTE_BINDING = DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uJointPalette;
48
+ /** And the cloth binding's three textures, which every skinned caster declares. */
49
+ const CLOTH_TEXTURES = [
50
+ DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothBinding,
51
+ DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothParticles,
52
+ DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothRest,
53
+ ] as const;
45
54
 
46
55
  const VISIBILITY_VERTEX = 0x1;
47
56
  const VISIBILITY_FRAGMENT = 0x2;
@@ -81,13 +90,11 @@ export function createDepthBindGroupLayout(
81
90
  coordinate with nothing to filter, which is what `flatPass.ts` binds for the same
82
91
  palette. naga declares a sampler for it and nothing ever reads one. */
83
92
  ...(skinned
84
- ? [
85
- {
86
- binding: DEPTH_PALETTE_BINDING.texture,
87
- visibility: VISIBILITY_VERTEX,
88
- texture: { sampleType: 'unfilterable-float' as GPUTextureSampleType },
89
- },
90
- ]
93
+ ? [DEPTH_PALETTE_BINDING, ...CLOTH_TEXTURES].map((binding) => ({
94
+ binding: binding.texture,
95
+ visibility: VISIBILITY_VERTEX,
96
+ texture: { sampleType: 'unfilterable-float' as GPUTextureSampleType },
97
+ }))
91
98
  : []),
92
99
  {
93
100
  binding: VERT_BINDING,
@@ -165,6 +172,8 @@ export function createDepthBindGroup(
165
172
  palette: GPUTextureView | null = null,
166
173
  /** The cutout map and its sampler, for a layout built with `cutout`. */
167
174
  cutout: { readonly view: GPUTextureView; readonly sampler: GPUSampler } | null = null,
175
+ /** A skinned group's cloth views: the caster's, or the stand-in where it has none. */
176
+ cloth: ClothViews | null = null,
168
177
  ): GPUBindGroup {
169
178
  return device.createBindGroup({
170
179
  label:
@@ -186,6 +195,13 @@ export function createDepthBindGroup(
186
195
  { binding: PREVIOUS.texture, resource: previous },
187
196
  { binding: PREVIOUS.sampler, resource: sampler },
188
197
  ...(palette !== null ? [{ binding: DEPTH_PALETTE_BINDING.texture, resource: palette }] : []),
198
+ ...(palette !== null
199
+ ? [
200
+ { binding: CLOTH_TEXTURES[0].texture, resource: clothOf(cloth).binding },
201
+ { binding: CLOTH_TEXTURES[1].texture, resource: clothOf(cloth).particles },
202
+ { binding: CLOTH_TEXTURES[2].texture, resource: clothOf(cloth).rest },
203
+ ]
204
+ : []),
189
205
  ...(cutout !== null
190
206
  ? [
191
207
  { binding: CUTOUT_MAP.texture, resource: cutout.view },
@@ -233,6 +249,13 @@ export function depthPipeline(
233
249
  instanced = false,
234
250
  /** Whether this pipeline casts a cutout: the variant that reads a map and discards by it. */
235
251
  cutout = false,
252
+ /**
253
+ * Whether a skinned caster reads its second four influences, as its visible draw does. The key
254
+ * must carry it, so the shadow parts from nothing the body does. See `skinning.ts`.
255
+ */
256
+ skinEight = false,
257
+ /** Whether a skinned caster is placed by a cloth binding, as its visible draw is. The key carries it. */
258
+ cloth = false,
236
259
  ): GPURenderPipeline {
237
260
  return cache.get(key, () => ({
238
261
  label: key,
@@ -252,6 +275,9 @@ export function depthPipeline(
252
275
  ),
253
276
  entryPoint: 'main',
254
277
  buffers: vertexBufferLayouts(present, instanced),
278
+ ...(skinned && !instanced && !cutout
279
+ ? { constants: vertexConstants(DEPTH_BINDINGS.DEPTH_SKINNED_VERT, skinEight, cloth) }
280
+ : {}),
255
281
  },
256
282
  fragment: {
257
283
  module: shaderModule(
@@ -332,3 +358,10 @@ export function depthPipeline(
332
358
  },
333
359
  }));
334
360
  }
361
+
362
+ function clothOf(cloth: ClothViews | null): ClothViews {
363
+ if (cloth === null) {
364
+ throw new Error('depthPass: a skinned bind group needs cloth views, the stand-in at the least');
365
+ }
366
+ return cloth;
367
+ }
@@ -11,8 +11,12 @@
11
11
  * edits rather than twenty-three implementations.
12
12
  */
13
13
 
14
- /** The most vertex buffers any verb on this backend binds is three. Four leaves a margin. */
15
- const VERTEX_BUFFER_SLOTS = 4;
14
+ /**
15
+ * WebGPU's guaranteed `maxVertexBuffers`, so no verb can outgrow it. The most any binds is five — a
16
+ * dynamic mesh's positions, normals, rows and constants, and an instanced batch's placements — and
17
+ * this said three, then four, while text and bolts already bound four.
18
+ */
19
+ const VERTEX_BUFFER_SLOTS = 8;
16
20
 
17
21
  export interface DrawCommand {
18
22
  pipeline: GPURenderPipeline | null;
@@ -9,6 +9,7 @@ import type { UniformFields } from './scatterPass.ts';
9
9
  import { DEPTH_FORMAT } from './flatPass.ts';
10
10
  import type { PipelineCache } from './pipelineCache.ts';
11
11
  import { shaderModule } from './shaderModules.ts';
12
+ import { SCENE_ALPHA_KEEPS } from '../../sceneCoverage.ts';
12
13
 
13
14
  /**
14
15
  * The film pass: a thin wet layer — an oil slick, a puddle — lying over the world.
@@ -111,7 +112,7 @@ export function filmPipeline(
111
112
  format: cache.format,
112
113
  blend: {
113
114
  color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
114
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
115
+ alpha: SCENE_ALPHA_KEEPS,
115
116
  },
116
117
  },
117
118
  ],