@driftengine/core 4.9.0 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/CHANGELOG.json +133 -11
  2. package/README.md +43 -38
  3. package/dist/index.d.ts +7 -4
  4. package/dist/index.js +5 -4
  5. package/dist/render/ambientHarmonics.d.ts +1 -1
  6. package/dist/render/ambientHarmonics.js +1 -1
  7. package/dist/render/areaLights.d.ts +4 -4
  8. package/dist/render/areaLights.js +1 -1
  9. package/dist/render/backend/api.d.ts +10 -3
  10. package/dist/render/backend/select.js +11 -5
  11. package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
  12. package/dist/render/backend/webgl2/boneAnimations.js +80 -0
  13. package/dist/render/backend/webgl2/instanced.d.ts +5 -1
  14. package/dist/render/backend/webgl2/instanced.js +13 -1
  15. package/dist/render/backend/webgl2/renderer.d.ts +189 -4
  16. package/dist/render/backend/webgl2/renderer.js +614 -66
  17. package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
  18. package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
  19. package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
  20. package/dist/render/backend/webgpu/boneAnimations.js +76 -0
  21. package/dist/render/backend/webgpu/buffers.d.ts +24 -4
  22. package/dist/render/backend/webgpu/buffers.js +116 -59
  23. package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
  24. package/dist/render/backend/webgpu/clothKernels.js +76 -0
  25. package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
  26. package/dist/render/backend/webgpu/clothOps.js +78 -0
  27. package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
  28. package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
  29. package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
  30. package/dist/render/backend/webgpu/clothSetPack.js +144 -0
  31. package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
  32. package/dist/render/backend/webgpu/compressedUpload.js +53 -8
  33. package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
  34. package/dist/render/backend/webgpu/depthPass.js +49 -14
  35. package/dist/render/backend/webgpu/drawCommand.d.ts +20 -4
  36. package/dist/render/backend/webgpu/drawCommand.js +11 -3
  37. package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
  38. package/dist/render/backend/webgpu/flatPass.js +137 -20
  39. package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
  40. package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
  41. package/dist/render/backend/webgpu/instanced.d.ts +14 -1
  42. package/dist/render/backend/webgpu/instanced.js +24 -1
  43. package/dist/render/backend/webgpu/latePass.js +1 -0
  44. package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
  45. package/dist/render/backend/webgpu/materialStore.js +265 -0
  46. package/dist/render/backend/webgpu/motionPass.js +15 -6
  47. package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
  48. package/dist/render/backend/webgpu/pipelineCache.js +46 -1
  49. package/dist/render/backend/webgpu/postPass.d.ts +1 -1
  50. package/dist/render/backend/webgpu/renderer.d.ts +200 -35
  51. package/dist/render/backend/webgpu/renderer.js +993 -168
  52. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
  53. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
  54. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
  55. package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
  56. package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
  57. package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
  58. package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
  59. package/dist/render/backend/webgpu/staticBundles.js +242 -0
  60. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
  61. package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
  62. package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
  63. package/dist/render/backend/webgpu/uniformRing.js +9 -1
  64. package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
  65. package/dist/render/backend/webgpu/vertexFormats.js +97 -0
  66. package/dist/render/bloomChain.d.ts +3 -3
  67. package/dist/render/boneAnimation.d.ts +92 -0
  68. package/dist/render/boneAnimation.js +133 -0
  69. package/dist/render/compressedSource.d.ts +33 -13
  70. package/dist/render/compressedSource.js +70 -25
  71. package/dist/render/glCompressed.js +83 -0
  72. package/dist/render/globalMediumPass.d.ts +2 -0
  73. package/dist/render/globalMediumPass.js +1 -0
  74. package/dist/render/indexWidth.d.ts +17 -0
  75. package/dist/render/indexWidth.js +23 -0
  76. package/dist/render/instances.d.ts +26 -0
  77. package/dist/render/lightBudget.d.ts +10 -0
  78. package/dist/render/lightBudget.js +10 -0
  79. package/dist/render/lightMatrix.d.ts +11 -0
  80. package/dist/render/lightMatrix.js +14 -0
  81. package/dist/render/lightmap.d.ts +1 -1
  82. package/dist/render/mesh.d.ts +14 -0
  83. package/dist/render/mesh.js +44 -14
  84. package/dist/render/pointLightSelection.d.ts +4 -4
  85. package/dist/render/renderQuality.d.ts +30 -0
  86. package/dist/render/renderQuality.js +5 -0
  87. package/dist/render/rendererHarness.d.ts +7 -0
  88. package/dist/render/rendererHarness.js +12 -0
  89. package/dist/render/screenSpaceReflection.d.ts +2 -1
  90. package/dist/render/shader.d.ts +15 -0
  91. package/dist/render/shader.js +41 -0
  92. package/dist/render/shaders/boneAnimation.d.ts +19 -0
  93. package/dist/render/shaders/boneAnimation.js +63 -0
  94. package/dist/render/shaders/depth.d.ts +7 -0
  95. package/dist/render/shaders/depth.js +35 -10
  96. package/dist/render/shaders/flat/directionalShadow.js +84 -10
  97. package/dist/render/shaders/flat/glassTint.d.ts +1 -1
  98. package/dist/render/shaders/flat/glassTint.js +3 -0
  99. package/dist/render/shaders/flat/index.d.ts +21 -0
  100. package/dist/render/shaders/flat/index.js +42 -7
  101. package/dist/render/shaders/flat/layered.d.ts +19 -0
  102. package/dist/render/shaders/flat/layered.js +115 -0
  103. package/dist/render/shaders/flat/main.d.ts +1 -1
  104. package/dist/render/shaders/flat/main.js +58 -21
  105. package/dist/render/shaders/flat/mapReads.d.ts +10 -0
  106. package/dist/render/shaders/flat/mapReads.js +38 -0
  107. package/dist/render/shaders/flat/models.js +1 -1
  108. package/dist/render/shaders/flat/movingSun.d.ts +21 -0
  109. package/dist/render/shaders/flat/movingSun.js +46 -0
  110. package/dist/render/shaders/flat/normalMap.d.ts +6 -2
  111. package/dist/render/shaders/flat/normalMap.js +14 -3
  112. package/dist/render/shaders/flat/overlay.d.ts +3 -2
  113. package/dist/render/shaders/flat/overlay.js +42 -20
  114. package/dist/render/shaders/flat/pointShadow.js +2 -2
  115. package/dist/render/shaders/flat/preamble.js +59 -27
  116. package/dist/render/shaders/flat/worldUv.d.ts +22 -0
  117. package/dist/render/shaders/flat/worldUv.js +113 -0
  118. package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
  119. package/dist/render/shaders/generated/depth.wgsl.js +93 -0
  120. package/dist/render/shaders/generated/flat.wgsl.d.ts +3616 -3042
  121. package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
  122. package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
  123. package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
  124. package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
  125. package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
  126. package/dist/render/shaders/globalMedium.js +9 -1
  127. package/dist/render/shaders/lightVolume.js +9 -1
  128. package/dist/render/shaders/outputTransform.d.ts +7 -0
  129. package/dist/render/shaders/outputTransform.js +28 -14
  130. package/dist/render/shaders/tangentFrame.d.ts +1 -1
  131. package/dist/render/shaders/tangentFrame.js +16 -3
  132. package/dist/render/shaders/vertexChannel.d.ts +7 -0
  133. package/dist/render/shaders/vertexChannel.js +26 -0
  134. package/dist/render/skinnedClothRun.d.ts +28 -0
  135. package/dist/render/skinnedClothRun.js +103 -35
  136. package/dist/render/staticDraws.d.ts +47 -0
  137. package/dist/render/staticDraws.js +43 -0
  138. package/dist/render/surfaceLayers.d.ts +39 -0
  139. package/dist/render/surfaceLayers.js +26 -0
  140. package/dist/render/surfaceOverlay.d.ts +16 -3
  141. package/dist/render/surfaceOverlay.js +11 -6
  142. package/dist/render/surfaceProjection.d.ts +34 -0
  143. package/dist/render/surfaceProjection.js +30 -0
  144. package/dist/render/surfaceTexture.d.ts +68 -7
  145. package/dist/render/surfaceTexture.js +56 -17
  146. package/dist/render/vertexPacking.d.ts +48 -0
  147. package/dist/render/vertexPacking.js +68 -0
  148. package/package.json +6 -6
  149. package/src/index.ts +12 -4
  150. package/src/render/ambientHarmonics.ts +1 -1
  151. package/src/render/areaLights.ts +4 -4
  152. package/src/render/backend/api.ts +13 -3
  153. package/src/render/backend/select.ts +10 -5
  154. package/src/render/backend/webgl2/boneAnimations.ts +105 -0
  155. package/src/render/backend/webgl2/instanced.ts +20 -1
  156. package/src/render/backend/webgl2/renderer.ts +723 -79
  157. package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
  158. package/src/render/backend/webgpu/boneAnimations.ts +97 -0
  159. package/src/render/backend/webgpu/buffers.ts +195 -70
  160. package/src/render/backend/webgpu/clothKernels.ts +86 -0
  161. package/src/render/backend/webgpu/clothOps.ts +86 -0
  162. package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
  163. package/src/render/backend/webgpu/clothSetPack.ts +197 -0
  164. package/src/render/backend/webgpu/compressedUpload.ts +59 -9
  165. package/src/render/backend/webgpu/depthPass.ts +51 -10
  166. package/src/render/backend/webgpu/drawCommand.ts +25 -4
  167. package/src/render/backend/webgpu/flatPass.ts +212 -41
  168. package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
  169. package/src/render/backend/webgpu/instanced.ts +32 -1
  170. package/src/render/backend/webgpu/latePass.ts +1 -0
  171. package/src/render/backend/webgpu/materialStore.ts +276 -0
  172. package/src/render/backend/webgpu/motionPass.ts +18 -7
  173. package/src/render/backend/webgpu/pipelineCache.ts +53 -7
  174. package/src/render/backend/webgpu/renderer.ts +1201 -202
  175. package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
  176. package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
  177. package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
  178. package/src/render/backend/webgpu/staticBundles.ts +364 -0
  179. package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
  180. package/src/render/backend/webgpu/uniformRing.ts +10 -1
  181. package/src/render/backend/webgpu/vertexFormats.ts +113 -0
  182. package/src/render/bloomChain.ts +3 -3
  183. package/src/render/boneAnimation.ts +223 -0
  184. package/src/render/compressedSource.ts +121 -29
  185. package/src/render/glCompressed.ts +83 -0
  186. package/src/render/globalMediumPass.ts +7 -0
  187. package/src/render/indexWidth.ts +27 -0
  188. package/src/render/instances.ts +27 -0
  189. package/src/render/lightBudget.ts +11 -0
  190. package/src/render/lightMatrix.ts +15 -0
  191. package/src/render/lightmap.ts +1 -1
  192. package/src/render/mesh.ts +72 -14
  193. package/src/render/pointLightSelection.ts +4 -4
  194. package/src/render/renderQuality.ts +38 -0
  195. package/src/render/rendererHarness.ts +15 -0
  196. package/src/render/screenSpaceReflection.ts +2 -1
  197. package/src/render/shader.ts +59 -0
  198. package/src/render/shaders/boneAnimation.ts +63 -0
  199. package/src/render/shaders/depth.ts +45 -10
  200. package/src/render/shaders/flat/directionalShadow.ts +84 -10
  201. package/src/render/shaders/flat/glassTint.ts +7 -1
  202. package/src/render/shaders/flat/index.ts +63 -7
  203. package/src/render/shaders/flat/layered.ts +115 -0
  204. package/src/render/shaders/flat/main.ts +58 -21
  205. package/src/render/shaders/flat/mapReads.ts +38 -0
  206. package/src/render/shaders/flat/models.ts +1 -1
  207. package/src/render/shaders/flat/movingSun.ts +46 -0
  208. package/src/render/shaders/flat/normalMap.ts +14 -3
  209. package/src/render/shaders/flat/overlay.ts +42 -20
  210. package/src/render/shaders/flat/pointShadow.ts +2 -2
  211. package/src/render/shaders/flat/preamble.ts +59 -27
  212. package/src/render/shaders/flat/worldUv.ts +113 -0
  213. package/src/render/shaders/generated/depth.wgsl.ts +95 -0
  214. package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
  215. package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
  216. package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
  217. package/src/render/shaders/globalMedium.ts +9 -1
  218. package/src/render/shaders/lightVolume.ts +9 -1
  219. package/src/render/shaders/outputTransform.ts +30 -14
  220. package/src/render/shaders/tangentFrame.ts +16 -3
  221. package/src/render/shaders/vertexChannel.ts +27 -0
  222. package/src/render/skinnedClothRun.ts +138 -38
  223. package/src/render/staticDraws.ts +95 -0
  224. package/src/render/surfaceLayers.ts +57 -0
  225. package/src/render/surfaceOverlay.ts +24 -7
  226. package/src/render/surfaceProjection.ts +57 -0
  227. package/src/render/surfaceTexture.ts +124 -27
  228. package/src/render/vertexPacking.ts +109 -0
  229. package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
  230. package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
  231. package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The cloth solver's layouts and one compute pipeline an entry point of `shaders/clothSolve.wgsl.ts`,
3
+ * built once a device and shared by every set of garments on it.
4
+ */
5
+ import { CLOTH_SOLVE_WGSL } from './shaders/clothSolve.wgsl.ts';
6
+ import { shaderModule } from './shaderModules.ts';
7
+
8
+ const COMPUTE = 0x4;
9
+
10
+ export const CLOTH_ENTRIES = [
11
+ 'clothPose',
12
+ 'clothRest',
13
+ 'clothBegin',
14
+ 'clothPredict',
15
+ 'clothDistance',
16
+ 'clothBending',
17
+ 'clothTether',
18
+ 'clothLimit',
19
+ 'clothFinish',
20
+ 'clothBlend',
21
+ 'clothPublish',
22
+ ] as const;
23
+ export type ClothEntry = (typeof CLOTH_ENTRIES)[number];
24
+
25
+ export interface ClothKernels {
26
+ readonly layout: GPUBindGroupLayout;
27
+ readonly outputLayout: GPUBindGroupLayout;
28
+ readonly pipelines: Readonly<Record<ClothEntry, GPUComputePipeline>>;
29
+ }
30
+
31
+ const kernelsByDevice = new WeakMap<GPUDevice, ClothKernels>();
32
+
33
+ export function clothKernelsFor(device: GPUDevice): ClothKernels {
34
+ const held = kernelsByDevice.get(device);
35
+ if (held !== undefined) return held;
36
+ const uniform = (binding: number, dynamic: boolean): GPUBindGroupLayoutEntry => ({
37
+ binding,
38
+ visibility: COMPUTE,
39
+ buffer: { type: 'uniform', hasDynamicOffset: dynamic },
40
+ });
41
+ const storage = (binding: number, type: GPUBufferBindingType): GPUBindGroupLayoutEntry => ({
42
+ binding,
43
+ visibility: COMPUTE,
44
+ buffer: { type },
45
+ });
46
+ /* Seven storage buffers of the eight a stage is granted: the shader's header says why so few. */
47
+ const layout = device.createBindGroupLayout({
48
+ label: 'cloth.solve',
49
+ entries: [
50
+ uniform(0, false),
51
+ uniform(1, true),
52
+ uniform(2, true),
53
+ storage(3, 'storage'),
54
+ storage(4, 'storage'),
55
+ storage(5, 'read-only-storage'),
56
+ storage(6, 'read-only-storage'),
57
+ storage(7, 'storage'),
58
+ storage(8, 'read-only-storage'),
59
+ storage(9, 'read-only-storage'),
60
+ ],
61
+ });
62
+ const outputLayout = device.createBindGroupLayout({
63
+ label: 'cloth.publish',
64
+ entries: [
65
+ {
66
+ binding: 0,
67
+ visibility: COMPUTE,
68
+ storageTexture: { access: 'write-only', format: 'rgba32float' },
69
+ },
70
+ ],
71
+ });
72
+ const module = shaderModule(device, { label: 'cloth.solve', code: CLOTH_SOLVE_WGSL });
73
+ const solve = device.createPipelineLayout({ bindGroupLayouts: [layout] });
74
+ const publish = device.createPipelineLayout({ bindGroupLayouts: [layout, outputLayout] });
75
+ const pipelines = {} as Record<ClothEntry, GPUComputePipeline>;
76
+ for (const entry of CLOTH_ENTRIES) {
77
+ pipelines[entry] = device.createComputePipeline({
78
+ label: `cloth.${entry}`,
79
+ layout: entry === 'clothPublish' ? publish : solve,
80
+ compute: { module, entryPoint: entry },
81
+ });
82
+ }
83
+ const kernels = { layout, outputLayout, pipelines };
84
+ kernelsByDevice.set(device, kernels);
85
+ return kernels;
86
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * What one garment of a set has asked the device for and the device has not yet recorded: a pose, a
3
+ * reset to rest, steps — each with the numbers its round reads, taken when it was asked for.
4
+ *
5
+ * **Taken when asked for, because they change after.** `ClothControl` moves its targets to a step's
6
+ * fraction and calls the device; by the time the set records the step, the next step has moved them
7
+ * again. So each op keeps the head of its record — fraction, blend, the poses it reads, the carry and
8
+ * the wind — as it stood, and the set copies it into the round that runs it.
9
+ *
10
+ * Grows by doubling and never shrinks, so a settled set allocates nothing a frame.
11
+ */
12
+
13
+ /** Words of a garment's record in a round: the shader's `Pace`. */
14
+ export const PACE_WORDS = 44;
15
+ /** The head of it, which an op carries: everything before the garment's own constants. */
16
+ export const PACE_HEAD_WORDS = 32;
17
+
18
+ export const CLOTH_OP_POSE = 0;
19
+ export const CLOTH_OP_REST = 1;
20
+ export const CLOTH_OP_STEP = 2;
21
+
22
+ export class ClothOps {
23
+ private kinds = new Uint8Array(8);
24
+ private heads = new ArrayBuffer(8 * PACE_HEAD_WORDS * 4);
25
+ private floats = new Float32Array(this.heads);
26
+ private words = new Uint32Array(this.heads);
27
+ private first = 0;
28
+ private end = 0;
29
+
30
+ get pending(): number {
31
+ return this.end - this.first;
32
+ }
33
+
34
+ /** The kind of the op to run next, or -1 where there is none. */
35
+ get next(): number {
36
+ return this.first < this.end ? (this.kinds[this.first] as number) : -1;
37
+ }
38
+
39
+ /** A new op of `kind`, its head zeroed, for the caller to fill: the float and word offset of it. */
40
+ push(kind: number): number {
41
+ if (this.end === this.kinds.length) this.grow();
42
+ const at = this.end * PACE_HEAD_WORDS;
43
+ this.floats.fill(0, at, at + PACE_HEAD_WORDS);
44
+ this.kinds[this.end] = kind;
45
+ this.end++;
46
+ return at;
47
+ }
48
+
49
+ get headFloats(): Float32Array {
50
+ return this.floats;
51
+ }
52
+
53
+ get headWords(): Uint32Array {
54
+ return this.words;
55
+ }
56
+
57
+ /**
58
+ * The next op's head into `out` at `at`, and the op gone. Copied as words, since a head holds
59
+ * integers too, and a word at a time, since a view a call would be an allocation a round.
60
+ */
61
+ take(out: Uint32Array, at: number): void {
62
+ const from = this.first * PACE_HEAD_WORDS;
63
+ for (let w = 0; w < PACE_HEAD_WORDS; w++) out[at + w] = this.words[from + w] as number;
64
+ this.first++;
65
+ if (this.first === this.end) {
66
+ this.first = 0;
67
+ this.end = 0;
68
+ }
69
+ }
70
+
71
+ private grow(): void {
72
+ const live = this.end - this.first;
73
+ const capacity = this.kinds.length * 2;
74
+ const kinds = new Uint8Array(capacity);
75
+ kinds.set(this.kinds.subarray(this.first, this.end));
76
+ const heads = new ArrayBuffer(capacity * PACE_HEAD_WORDS * 4);
77
+ const floats = new Float32Array(heads);
78
+ floats.set(this.floats.subarray(this.first * PACE_HEAD_WORDS, this.end * PACE_HEAD_WORDS));
79
+ this.kinds = kinds;
80
+ this.heads = heads;
81
+ this.floats = floats;
82
+ this.words = new Uint32Array(heads);
83
+ this.first = 0;
84
+ this.end = live;
85
+ }
86
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * What a set of garments writes once into the buffers its rounds read, and the one per-step number
3
+ * worked out on the CPU: the constants, each round slot's first record, each colour's and each
4
+ * garment's batch slot, and the carry a step hands a garment that keeps less than all of its
5
+ * character's motion. Plain functions over the packed set, apart from the solver that schedules.
6
+ */
7
+ import type { ResolvedClothParameters } from '@driftengine/physics';
8
+
9
+ import type { PackedClothSet } from './clothSetPack.ts';
10
+ import { DYNAMIC_ALIGNMENT } from './uniformRing.ts';
11
+
12
+ /** Bytes of the shader's `Constants`. */
13
+ export const CLOTH_CONSTANT_BYTES = 48;
14
+
15
+ /** The set's `Constants`: its totals, and the substep length every garment of it shares. */
16
+ export function clothConstants(packed: PackedClothSet, h: number): ArrayBuffer {
17
+ const bytes = new ArrayBuffer(CLOTH_CONSTANT_BYTES);
18
+ const u = new Uint32Array(bytes);
19
+ const f = new Float32Array(bytes);
20
+ u[0] = packed.count;
21
+ u[1] = packed.distances;
22
+ u[2] = packed.bendings;
23
+ u[3] = packed.joints;
24
+ u[4] = packed.colliders;
25
+ f[8] = h;
26
+ f[9] = 1 / h;
27
+ f[10] = 1 / (h * h);
28
+ return bytes;
29
+ }
30
+
31
+ /** Each of `rounds` round slots' first record: the round's index times the garments. */
32
+ export function clothRoundOffsets(rounds: number, garments: number): Uint32Array<ArrayBuffer> {
33
+ const words = new Uint32Array((rounds * DYNAMIC_ALIGNMENT) / 4);
34
+ for (let r = 0; r < rounds; r++) words[(r * DYNAMIC_ALIGNMENT) / 4] = r * garments;
35
+ return words;
36
+ }
37
+
38
+ /**
39
+ * Each colour's range, twice — opening a substep's iterations and not — at a slot each, kind by
40
+ * kind; then one slot a garment for publishing, its particles' range and its index.
41
+ */
42
+ export function clothBatchSlots(packed: PackedClothSet): Uint32Array<ArrayBuffer> {
43
+ const { distanceBatches, bendingBatches, tetherBatches, garments } = packed;
44
+ const kinds = [distanceBatches, bendingBatches, tetherBatches];
45
+ let slots = garments.length;
46
+ for (const batches of kinds) slots += (batches.length - 1) * 2;
47
+ const words = new Uint32Array((slots * DYNAMIC_ALIGNMENT) / 4);
48
+ let slot = 0;
49
+ for (const batches of kinds) {
50
+ for (let b = 0; b + 1 < batches.length; b++) {
51
+ for (let first = 0; first < 2; first++) {
52
+ const at = (slot * DYNAMIC_ALIGNMENT) / 4;
53
+ words[at] = batches[b] as number;
54
+ words[at + 1] = batches[b + 1] as number;
55
+ words[at + 2] = first;
56
+ slot++;
57
+ }
58
+ }
59
+ }
60
+ garments.forEach((range, g) => {
61
+ const at = (slot * DYNAMIC_ALIGNMENT) / 4;
62
+ words[at] = range.particleBase;
63
+ words[at + 1] = range.particleBase + range.count;
64
+ words[at + 2] = g;
65
+ slot++;
66
+ });
67
+ return words;
68
+ }
69
+
70
+ /**
71
+ * `carryParticles`' rotation, origin and move, into a record's head at `at`, between the model
72
+ * matrices before and after the step; nothing where the garment keeps all of its character's
73
+ * motion, which leaves the head's carry at 0.
74
+ */
75
+ export function writeClothCarry(
76
+ parameters: ResolvedClothParameters,
77
+ previous: Float32Array,
78
+ next: Float32Array,
79
+ words: Uint32Array,
80
+ f: Float32Array,
81
+ at: number,
82
+ ): void {
83
+ const turn = 1 - parameters.angularInertia;
84
+ const move = 1 - parameters.linearInertia;
85
+ if (turn === 0 && move === 0) return;
86
+ words[at + 3] = 1;
87
+ /* Column c of R = R_next × R_previousᵀ, at turn0 … turn2. */
88
+ for (let col = 0; col < 3; col++) {
89
+ for (let row = 0; row < 3; row++) {
90
+ let sum = 0;
91
+ for (let k = 0; k < 3; k++) {
92
+ sum += (next[k * 4 + row] as number) * (previous[k * 4 + col] as number);
93
+ }
94
+ f[at + 8 + col * 4 + row] = sum;
95
+ }
96
+ }
97
+ for (let k = 0; k < 3; k++) {
98
+ f[at + 20 + k] = previous[12 + k] as number;
99
+ f[at + 24 + k] = ((next[12 + k] as number) - (previous[12 + k] as number)) * move;
100
+ }
101
+ f[at + 23] = turn;
102
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Several garments' packs as one set of buffers, so that one dispatch solves a colour of one kind of
3
+ * constraint for every garment at once: `packCloth`'s sections laid end to end, and each kind's
4
+ * records regrouped so a colour of all of them is one range.
5
+ *
6
+ * **Why regrouping by colour keeps each garment's solve.** A batch is a set of constraints that share
7
+ * no movable particle, and two garments share no particle at all, so colour `b` of every garment is
8
+ * still such a set. Each garment's own colours still run in its own order, 0 then 1 then 2, so it is
9
+ * solved exactly as it would be alone: the CPU solver, which steps garments one at a time, is the
10
+ * reference for every one of them.
11
+ *
12
+ * **Every index a record or a static holds is the set's**: a particle is offset by where its garment
13
+ * starts, a joint by where its garment's skin matrices start. A section `packCloth` lays at `s · n`
14
+ * lies at `s · N` here, with a garment's particles at its base inside it, and one more section says
15
+ * which garment each particle is — what a kernel reads its garment's numbers by.
16
+ *
17
+ * **What it gives up**: a colour is dispatched over the most records any garment has in it, so a set
18
+ * of one garment of nine colours and fourteen of three runs nine colours' dispatches for all of them;
19
+ * and a garment joins a set when the set is made, so changing one rebuilds the set.
20
+ */
21
+ import { CLOTH_STATIC_FLOATS } from './clothSolvePack.ts';
22
+ import type { PackedCloth } from './clothSolvePack.ts';
23
+
24
+ /** Floats of statics a particle in a set: a garment's own, and which garment it is. */
25
+ export const CLOTH_SET_STATIC_FLOATS = CLOTH_STATIC_FLOATS + 1;
26
+
27
+ /** `packCloth`'s sections: where each starts, in particle counts, and its floats a particle. */
28
+ const SECTIONS: readonly (readonly [number, number])[] = [
29
+ [0, 1],
30
+ [1, 1],
31
+ [2, 2],
32
+ [4, 2],
33
+ [6, 1],
34
+ [7, 3],
35
+ [10, 3],
36
+ [13, 16],
37
+ ];
38
+ /** Where the influences start, and where the garment's index sits, in particle counts. */
39
+ const INFLUENCES = 13;
40
+ const GARMENT = CLOTH_STATIC_FLOATS;
41
+
42
+ /** Where one garment sits in a set. */
43
+ export interface ClothGarmentRange {
44
+ readonly particleBase: number;
45
+ readonly count: number;
46
+ readonly jointBase: number;
47
+ readonly joints: number;
48
+ readonly colliderBase: number;
49
+ readonly colliders: number;
50
+ /** `packCloth`'s limit flags, the garment's own. */
51
+ readonly limits: number;
52
+ readonly rigged: boolean;
53
+ }
54
+
55
+ export interface PackedClothSet {
56
+ /** Particles across every garment. */
57
+ readonly count: number;
58
+ readonly statics: Float32Array;
59
+ readonly constraints: Uint32Array;
60
+ readonly distances: number;
61
+ readonly bendings: number;
62
+ readonly tethers: number;
63
+ /** Each kind's colours across the set: colour `b` of every garment is `[b]` up to `[b + 1]`. */
64
+ readonly distanceBatches: Uint32Array;
65
+ readonly bendingBatches: Uint32Array;
66
+ readonly tetherBatches: Uint32Array;
67
+ /** Skin matrices and colliders across every garment. */
68
+ readonly joints: number;
69
+ readonly colliders: number;
70
+ readonly garments: readonly ClothGarmentRange[];
71
+ }
72
+
73
+ export function packClothSet(packs: readonly PackedCloth[]): PackedClothSet {
74
+ const garments: ClothGarmentRange[] = [];
75
+ let count = 0;
76
+ let joints = 0;
77
+ let colliders = 0;
78
+ for (const pack of packs) {
79
+ garments.push({
80
+ particleBase: count,
81
+ count: pack.count,
82
+ jointBase: joints,
83
+ joints: pack.joints,
84
+ colliderBase: colliders,
85
+ colliders: pack.colliders,
86
+ limits: pack.limits,
87
+ rigged: pack.rigged,
88
+ });
89
+ count += pack.count;
90
+ joints += pack.joints;
91
+ colliders += pack.colliders;
92
+ }
93
+
94
+ const statics = new Float32Array(count * CLOTH_SET_STATIC_FLOATS);
95
+ packs.forEach((pack, g) => {
96
+ const { particleBase: base, count: n, jointBase } = garments[g] as ClothGarmentRange;
97
+ for (const [start, stride] of SECTIONS) {
98
+ const from = pack.statics.subarray(start * n, (start + stride) * n);
99
+ statics.set(from, start * count + base * stride);
100
+ }
101
+ /* Each influence's joint, the set's: four joints, four weights, four joints, four weights. */
102
+ for (let i = 0; i < n; i++) {
103
+ const at = INFLUENCES * count + (base + i) * 16;
104
+ for (let k = 0; k < 4; k++) {
105
+ statics[at + k] = (statics[at + k] as number) + jointBase;
106
+ statics[at + 8 + k] = (statics[at + 8 + k] as number) + jointBase;
107
+ }
108
+ }
109
+ statics.fill(g, GARMENT * count + base, GARMENT * count + base + n);
110
+ });
111
+
112
+ const distances = sum(packs, (pack) => pack.distances);
113
+ const bendings = sum(packs, (pack) => pack.bendings);
114
+ const tethers = sum(packs, (pack) => pack.tethers);
115
+ const constraints = new Uint32Array(distances * 4 + bendings * 6 + tethers * 3);
116
+ /* Each kind: where its records start, a record's words, how many of them are particles. */
117
+ const distanceBatches = regroup(packs, garments, constraints, 0, 4, 2, 'distanceBatches');
118
+ const bendingStart = distances * 4;
119
+ const bendingBatches = regroup(
120
+ packs,
121
+ garments,
122
+ constraints,
123
+ bendingStart,
124
+ 6,
125
+ 4,
126
+ 'bendingBatches',
127
+ );
128
+ const tetherStart = bendingStart + bendings * 6;
129
+ const tetherBatches = regroup(packs, garments, constraints, tetherStart, 3, 2, 'tetherBatches');
130
+ return {
131
+ count,
132
+ statics,
133
+ constraints,
134
+ distances,
135
+ bendings,
136
+ tethers,
137
+ distanceBatches,
138
+ bendingBatches,
139
+ tetherBatches,
140
+ joints,
141
+ colliders,
142
+ garments,
143
+ };
144
+ }
145
+
146
+ function sum(packs: readonly PackedCloth[], of: (pack: PackedCloth) => number): number {
147
+ let total = 0;
148
+ for (const pack of packs) total += of(pack);
149
+ return total;
150
+ }
151
+
152
+ /** Where one kind's records start in a garment's own pack, in words. */
153
+ function kindStart(pack: PackedCloth, kind: BatchKind): number {
154
+ if (kind === 'distanceBatches') return 0;
155
+ if (kind === 'bendingBatches') return pack.distances * 4;
156
+ return pack.distances * 4 + pack.bendings * 6;
157
+ }
158
+
159
+ type BatchKind = 'distanceBatches' | 'bendingBatches' | 'tetherBatches';
160
+
161
+ /**
162
+ * One kind's records, colour by colour and within a colour garment by garment, into `out` from
163
+ * `start`; each record's first `ids` words offset by its garment's first particle. The set's colours.
164
+ */
165
+ function regroup(
166
+ packs: readonly PackedCloth[],
167
+ garments: readonly ClothGarmentRange[],
168
+ out: Uint32Array,
169
+ start: number,
170
+ words: number,
171
+ ids: number,
172
+ kind: BatchKind,
173
+ ): Uint32Array {
174
+ let colours = 0;
175
+ for (const pack of packs) colours = Math.max(colours, pack[kind].length - 1);
176
+ const batches = new Uint32Array(colours + 1);
177
+ let record = 0;
178
+ for (let b = 0; b < colours; b++) {
179
+ packs.forEach((pack, g) => {
180
+ const own = pack[kind];
181
+ if (b + 1 >= own.length) return;
182
+ const base = (garments[g] as ClothGarmentRange).particleBase;
183
+ const from = kindStart(pack, kind);
184
+ for (let r = own[b] as number; r < (own[b + 1] as number); r++) {
185
+ const src = from + r * words;
186
+ const dst = start + record * words;
187
+ for (let w = 0; w < words; w++) {
188
+ const word = pack.constraints[src + w] as number;
189
+ out[dst + w] = w < ids ? word + base : word;
190
+ }
191
+ record++;
192
+ }
193
+ });
194
+ batches[b + 1] = record;
195
+ }
196
+ return batches;
197
+ }
@@ -12,7 +12,8 @@
12
12
  * the level nominally has.
13
13
  */
14
14
  import type { CompressedTextureFormat, CompressedTextureSource } from '../../compressedSource.ts';
15
- import { BLOCK_BYTES } from '../../compressedSource.ts';
15
+ import { BLOCK_SHAPES, blockFamily } from '../../compressedSource.ts';
16
+ import type { BlockFormat } from '../../compressedSource.ts';
16
17
 
17
18
  const GPU_FORMAT: Readonly<Record<CompressedTextureFormat, GPUTextureFormat>> = {
18
19
  bc1: 'bc1-rgba-unorm',
@@ -25,13 +26,61 @@ const GPU_FORMAT: Readonly<Record<CompressedTextureFormat, GPUTextureFormat>> =
25
26
  bc5: 'bc5-rg-unorm',
26
27
  bc7: 'bc7-rgba-unorm',
27
28
  'bc7-srgb': 'bc7-rgba-unorm-srgb',
29
+ 'etc2-rgb8': 'etc2-rgb8unorm',
30
+ 'etc2-rgb8-srgb': 'etc2-rgb8unorm-srgb',
31
+ 'etc2-rgb8a1': 'etc2-rgb8a1unorm',
32
+ 'etc2-rgb8a1-srgb': 'etc2-rgb8a1unorm-srgb',
33
+ 'etc2-rgba8': 'etc2-rgba8unorm',
34
+ 'etc2-rgba8-srgb': 'etc2-rgba8unorm-srgb',
35
+ 'eac-r11': 'eac-r11unorm',
36
+ 'eac-rg11': 'eac-rg11unorm',
37
+ 'astc-4x4': 'astc-4x4-unorm',
38
+ 'astc-4x4-srgb': 'astc-4x4-unorm-srgb',
39
+ 'astc-5x4': 'astc-5x4-unorm',
40
+ 'astc-5x4-srgb': 'astc-5x4-unorm-srgb',
41
+ 'astc-5x5': 'astc-5x5-unorm',
42
+ 'astc-5x5-srgb': 'astc-5x5-unorm-srgb',
43
+ 'astc-6x5': 'astc-6x5-unorm',
44
+ 'astc-6x5-srgb': 'astc-6x5-unorm-srgb',
45
+ 'astc-6x6': 'astc-6x6-unorm',
46
+ 'astc-6x6-srgb': 'astc-6x6-unorm-srgb',
47
+ 'astc-8x5': 'astc-8x5-unorm',
48
+ 'astc-8x5-srgb': 'astc-8x5-unorm-srgb',
49
+ 'astc-8x6': 'astc-8x6-unorm',
50
+ 'astc-8x6-srgb': 'astc-8x6-unorm-srgb',
51
+ 'astc-8x8': 'astc-8x8-unorm',
52
+ 'astc-8x8-srgb': 'astc-8x8-unorm-srgb',
53
+ 'astc-10x5': 'astc-10x5-unorm',
54
+ 'astc-10x5-srgb': 'astc-10x5-unorm-srgb',
55
+ 'astc-10x6': 'astc-10x6-unorm',
56
+ 'astc-10x6-srgb': 'astc-10x6-unorm-srgb',
57
+ 'astc-10x8': 'astc-10x8-unorm',
58
+ 'astc-10x8-srgb': 'astc-10x8-unorm-srgb',
59
+ 'astc-10x10': 'astc-10x10-unorm',
60
+ 'astc-10x10-srgb': 'astc-10x10-unorm-srgb',
61
+ 'astc-12x10': 'astc-12x10-unorm',
62
+ 'astc-12x10-srgb': 'astc-12x10-unorm-srgb',
63
+ 'astc-12x12': 'astc-12x12-unorm',
64
+ 'astc-12x12-srgb': 'astc-12x12-unorm-srgb',
28
65
  };
29
66
 
30
67
  /** Every BC format, where the device carries the feature; none where it does not. */
68
+ /** The feature that grants each family, which `select.ts` asks for wherever the adapter offers it. */
69
+ const FEATURE: Readonly<Record<'bc' | 'etc2' | 'astc', string>> = {
70
+ bc: 'texture-compression-bc',
71
+ etc2: 'texture-compression-etc2',
72
+ astc: 'texture-compression-astc',
73
+ };
74
+
31
75
  export function gpuCompressedFormats(device: GPUDevice): CompressedTextureFormat[] {
32
- return device.features.has('texture-compression-bc')
33
- ? (Object.keys(GPU_FORMAT) as CompressedTextureFormat[])
34
- : [];
76
+ return (Object.keys(GPU_FORMAT) as CompressedTextureFormat[]).filter((name) =>
77
+ device.features.has(FEATURE[blockFamily(baseOf(name))]),
78
+ );
79
+ }
80
+
81
+ /** The format a name is of, without its colour space. */
82
+ function baseOf(name: CompressedTextureFormat): BlockFormat {
83
+ return (name.endsWith('-srgb') ? name.slice(0, -'-srgb'.length) : name) as BlockFormat;
35
84
  }
36
85
 
37
86
  /** A texture holding `layers` as `name`, `levels` levels of each, written as they are. */
@@ -43,21 +92,22 @@ export function createCompressedTexture(
43
92
  ): GPUTexture {
44
93
  const { format, width, height } = layers[0] as CompressedTextureSource;
45
94
  const texture = device.createTexture({
46
- label: 'surface.texture.bc',
95
+ label: `surface.texture.${format}`,
47
96
  size: [width, height, layers.length],
48
97
  format: GPU_FORMAT[name],
49
98
  mipLevelCount: levels,
50
99
  usage: 0x2 | 0x4, // COPY_DST | TEXTURE_BINDING
51
100
  });
101
+ const block = BLOCK_SHAPES[format];
52
102
  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);
103
+ const across = Math.ceil(Math.max(1, width >> level) / block.width);
104
+ const down = Math.ceil(Math.max(1, height >> level) / block.height);
55
105
  for (let layer = 0; layer < layers.length; layer++) {
56
106
  device.queue.writeTexture(
57
107
  { texture, mipLevel: level, origin: [0, 0, layer] },
58
108
  (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 },
109
+ { bytesPerRow: across * block.bytes, rowsPerImage: down },
110
+ { width: across * block.width, height: down * block.height, depthOrArrayLayers: 1 },
61
111
  );
62
112
  }
63
113
  }