@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.
- package/CHANGELOG.json +127 -0
- package/README.md +1 -1
- package/dist/geometry/meshConcat.js +2 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +22 -1
- package/dist/math/scalar.js +1 -1
- package/dist/render/anisotropicLobe.d.ts +7 -0
- package/dist/render/anisotropicLobe.js +33 -0
- package/dist/render/backend/api.d.ts +41 -4
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
- package/dist/render/backend/webgl2/clothTextures.js +93 -0
- package/dist/render/backend/webgl2/renderer.d.ts +159 -3
- package/dist/render/backend/webgl2/renderer.js +662 -80
- package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
- package/dist/render/backend/webgl2/skinScatter.js +256 -0
- package/dist/render/backend/webgpu/buffers.d.ts +25 -11
- package/dist/render/backend/webgpu/buffers.js +98 -49
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
- package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
- package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
- package/dist/render/backend/webgpu/clothTextures.js +145 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
- package/dist/render/backend/webgpu/compressedUpload.js +38 -0
- package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
- package/dist/render/backend/webgpu/computeHost.js +5 -0
- package/dist/render/backend/webgpu/depthPass.d.ts +29 -9
- package/dist/render/backend/webgpu/depthPass.js +43 -12
- package/dist/render/backend/webgpu/drawCommand.js +6 -2
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +43 -4
- package/dist/render/backend/webgpu/flatPass.js +111 -13
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
- package/dist/render/backend/webgpu/motionPass.js +106 -9
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +15 -3
- package/dist/render/backend/webgpu/postPass.js +25 -8
- package/dist/render/backend/webgpu/renderer.d.ts +123 -6
- package/dist/render/backend/webgpu/renderer.js +534 -58
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
- package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/clothBindingData.d.ts +41 -0
- package/dist/render/clothBindingData.js +72 -0
- package/dist/render/clothPlacement.d.ts +25 -0
- package/dist/render/clothPlacement.js +85 -0
- package/dist/render/compressedSource.d.ts +66 -0
- package/dist/render/compressedSource.js +138 -0
- package/dist/render/cutoutCaster.d.ts +5 -1
- package/dist/render/cutoutCaster.js +6 -0
- package/dist/render/cutoutDither.d.ts +51 -0
- package/dist/render/cutoutDither.js +90 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/eyeRefraction.d.ts +35 -0
- package/dist/render/eyeRefraction.js +40 -0
- package/dist/render/glCompressed.d.ts +27 -0
- package/dist/render/glCompressed.js +53 -0
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/hairLobes.d.ts +70 -0
- package/dist/render/hairLobes.js +120 -0
- package/dist/render/lightBudget.d.ts +22 -0
- package/dist/render/lightBudget.js +22 -0
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/mesh.d.ts +7 -0
- package/dist/render/mesh.js +24 -0
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +6 -2
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/recon/motionVectors.d.ts +4 -2
- package/dist/render/recon/motionVectors.js +6 -3
- package/dist/render/renderQuality.d.ts +29 -2
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sceneTarget.d.ts +12 -0
- package/dist/render/sceneTarget.js +14 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/clothBinding.d.ts +22 -0
- package/dist/render/shaders/clothBinding.js +80 -0
- package/dist/render/shaders/depth.js +45 -8
- package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
- package/dist/render/shaders/flat/anisotropic.js +68 -0
- package/dist/render/shaders/flat/eye.d.ts +1 -0
- package/dist/render/shaders/flat/eye.js +81 -0
- package/dist/render/shaders/flat/hair.d.ts +1 -0
- package/dist/render/shaders/flat/hair.js +101 -0
- package/dist/render/shaders/flat/index.js +42 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +113 -17
- package/dist/render/shaders/flat/models.d.ts +14 -0
- package/dist/render/shaders/flat/models.js +273 -0
- package/dist/render/shaders/flat/preamble.js +26 -24
- package/dist/render/shaders/flat/skin.d.ts +1 -0
- package/dist/render/shaders/flat/skin.js +114 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +66 -24
- package/dist/render/shaders/generated/depth.wgsl.js +66 -24
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1815 -1704
- package/dist/render/shaders/generated/flat.wgsl.js +1853 -1742
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
- package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +102 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/skinBlur.d.ts +1 -0
- package/dist/render/shaders/skinBlur.js +103 -0
- package/dist/render/shaders/skinning.d.ts +3 -1
- package/dist/render/shaders/skinning.js +31 -4
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/shadowCasters.d.ts +13 -1
- package/dist/render/skinBlur.d.ts +41 -0
- package/dist/render/skinBlur.js +49 -0
- package/dist/render/skinScatter.d.ts +67 -0
- package/dist/render/skinScatter.js +80 -0
- package/dist/render/skinnedClothRun.d.ts +26 -0
- package/dist/render/skinnedClothRun.js +84 -0
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceModel.d.ts +129 -0
- package/dist/render/surfaceModel.js +152 -0
- package/dist/render/surfaceTexture.d.ts +55 -4
- package/dist/render/surfaceTexture.js +60 -46
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +8 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/geometry/meshConcat.ts +2 -0
- package/src/index.ts +53 -1
- package/src/math/scalar.ts +1 -1
- package/src/render/anisotropicLobe.ts +41 -0
- package/src/render/backend/api.ts +52 -3
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/clothTextures.ts +120 -0
- package/src/render/backend/webgl2/renderer.ts +750 -80
- package/src/render/backend/webgl2/skinScatter.ts +309 -0
- package/src/render/backend/webgpu/buffers.ts +137 -63
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
- package/src/render/backend/webgpu/clothTextures.ts +194 -0
- package/src/render/backend/webgpu/compressedUpload.ts +65 -0
- package/src/render/backend/webgpu/computeHost.ts +25 -0
- package/src/render/backend/webgpu/depthPass.ts +43 -10
- package/src/render/backend/webgpu/drawCommand.ts +6 -2
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +147 -8
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/motionPass.ts +128 -9
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +25 -6
- package/src/render/backend/webgpu/renderer.ts +701 -52
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
- package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/clothBindingData.ts +89 -0
- package/src/render/clothPlacement.ts +114 -0
- package/src/render/compressedSource.ts +198 -0
- package/src/render/cutoutCaster.ts +20 -2
- package/src/render/cutoutDither.ts +99 -0
- package/src/render/decalPass.ts +2 -1
- package/src/render/eyeRefraction.ts +72 -0
- package/src/render/glCompressed.ts +86 -0
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/hairLobes.ts +188 -0
- package/src/render/lightBudget.ts +25 -0
- package/src/render/lineBatch.ts +3 -1
- package/src/render/mesh.ts +23 -0
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +6 -2
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/recon/motionVectors.ts +6 -3
- package/src/render/renderQuality.ts +37 -2
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sceneTarget.ts +21 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/clothBinding.ts +81 -0
- package/src/render/shaders/depth.ts +45 -8
- package/src/render/shaders/flat/anisotropic.ts +68 -0
- package/src/render/shaders/flat/eye.ts +82 -0
- package/src/render/shaders/flat/hair.ts +102 -0
- package/src/render/shaders/flat/index.ts +42 -3
- package/src/render/shaders/flat/main.ts +113 -17
- package/src/render/shaders/flat/models.ts +286 -0
- package/src/render/shaders/flat/preamble.ts +26 -24
- package/src/render/shaders/flat/skin.ts +116 -0
- package/src/render/shaders/generated/depth.wgsl.ts +66 -24
- package/src/render/shaders/generated/flat.wgsl.ts +1853 -1742
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/motion.wgsl.ts +102 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/skinBlur.ts +105 -0
- package/src/render/shaders/skinning.ts +31 -4
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/shadowCasters.ts +13 -0
- package/src/render/skinBlur.ts +53 -0
- package/src/render/skinScatter.ts +92 -0
- package/src/render/skinnedClothRun.ts +128 -0
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceModel.ts +274 -0
- package/src/render/surfaceTexture.ts +134 -63
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +8 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
- package/dist/math/exact.d.ts +0 -76
- package/dist/math/exact.js +0 -327
- package/src/math/exact.ts +0 -340
|
@@ -2,6 +2,7 @@ import { DEPTH_COMPARE } from '../../depthConvention.js';
|
|
|
2
2
|
import { CAUSTICS_BINDINGS, CAUSTICS_FRAG_WGSL, CAUSTICS_VERT_WGSL, } from '../../shaders/generated/caustics.wgsl.js';
|
|
3
3
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
4
4
|
import { shaderModule } from './shaderModules.js';
|
|
5
|
+
import { SCENE_ALPHA_KEEPS } from '../../sceneCoverage.js';
|
|
5
6
|
/**
|
|
6
7
|
* The caustics pass: a surface lit by nearby water.
|
|
7
8
|
*
|
|
@@ -126,7 +127,7 @@ export function causticsPipeline(cache, device, layout) {
|
|
|
126
127
|
format: cache.format,
|
|
127
128
|
blend: {
|
|
128
129
|
color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|
|
129
|
-
alpha:
|
|
130
|
+
alpha: SCENE_ALPHA_KEEPS,
|
|
130
131
|
},
|
|
131
132
|
},
|
|
132
133
|
],
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SkinnedClothSetup } from '@driftengine/physics';
|
|
2
|
+
/** Floats of statics a particle, across its sections. */
|
|
3
|
+
export declare const CLOTH_STATIC_FLOATS = 29;
|
|
4
|
+
/** Bit flags of the limits a set-up has: a max distance, a backstop, a frontstop. */
|
|
5
|
+
export declare const CLOTH_LIMIT_MAX = 1;
|
|
6
|
+
export declare const CLOTH_LIMIT_BACK = 2;
|
|
7
|
+
export declare const CLOTH_LIMIT_FRONT = 4;
|
|
8
|
+
export interface PackedCloth {
|
|
9
|
+
readonly count: number;
|
|
10
|
+
/** inverse mass, max distance, backstop ×2, frontstop ×2, thickness, rest ×3, normal ×3, influences ×16. */
|
|
11
|
+
readonly statics: Float32Array;
|
|
12
|
+
/** Distance records (a, b, rest, compliance), bending (four ids, rest, compliance), tethers (particle, anchor, length). */
|
|
13
|
+
readonly constraints: Uint32Array;
|
|
14
|
+
readonly distances: number;
|
|
15
|
+
readonly bendings: number;
|
|
16
|
+
readonly tethers: number;
|
|
17
|
+
/** Each kind's batches as offsets into its sorted records: batch b is `[b]` up to `[b + 1]`. */
|
|
18
|
+
readonly distanceBatches: Uint32Array;
|
|
19
|
+
readonly bendingBatches: Uint32Array;
|
|
20
|
+
readonly tetherBatches: Uint32Array;
|
|
21
|
+
readonly limits: number;
|
|
22
|
+
/** Skin matrices the solver uploads a pose: the rig's joints, or one for the model alone. */
|
|
23
|
+
readonly joints: number;
|
|
24
|
+
readonly rigged: boolean;
|
|
25
|
+
readonly colliders: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function packCloth(setup: SkinnedClothSetup): PackedCloth;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A skinned cloth's set-up as the buffers `shaders/clothSolve.wgsl.ts` reads: per-particle
|
|
3
|
+
* statics in sections, and every constraint as a record sorted by batch.
|
|
4
|
+
*
|
|
5
|
+
* **Sorted by batch so a batch is a range**: a dispatch runs records `begin` to `end`, and a
|
|
6
|
+
* multiplier sits at its record's index. The batches are `batchesOf`'s — the set-up's own where it
|
|
7
|
+
* gives them, coloured otherwise — the same ones the CPU solver walks, so the two solve each batch
|
|
8
|
+
* in the same order.
|
|
9
|
+
*
|
|
10
|
+
* **What a device cannot be handed is said another way here.** An infinite max distance is −1, an
|
|
11
|
+
* infinite stop distance is a stop of radius 0: shader compilers may assume no infinity, and a
|
|
12
|
+
* comparison against one is then whatever the optimiser made of it. Tethers are coloured too, with
|
|
13
|
+
* every particle counted as movable, so two tethers on one particle never run in one dispatch —
|
|
14
|
+
* which keeps the CPU's order for that particle while the rest run at once.
|
|
15
|
+
*
|
|
16
|
+
* A cloth with no rig is skinned by its model matrix alone: one joint, weight 1, whose matrix the
|
|
17
|
+
* solver uploads as the model.
|
|
18
|
+
*/
|
|
19
|
+
import { batchesOf } from '@driftengine/physics';
|
|
20
|
+
/** Floats of statics a particle, across its sections. */
|
|
21
|
+
export const CLOTH_STATIC_FLOATS = 29;
|
|
22
|
+
/** Bit flags of the limits a set-up has: a max distance, a backstop, a frontstop. */
|
|
23
|
+
export const CLOTH_LIMIT_MAX = 1;
|
|
24
|
+
export const CLOTH_LIMIT_BACK = 2;
|
|
25
|
+
export const CLOTH_LIMIT_FRONT = 4;
|
|
26
|
+
/** Whether a set-up is skinned by a rig, as `ClothTargets` decides it. */
|
|
27
|
+
function riggedOf(setup) {
|
|
28
|
+
return (setup.joints !== undefined &&
|
|
29
|
+
setup.weights !== undefined &&
|
|
30
|
+
(setup.inverseBind?.length ?? 0) >= 16);
|
|
31
|
+
}
|
|
32
|
+
export function packCloth(setup) {
|
|
33
|
+
const n = setup.positions.length / 3;
|
|
34
|
+
const inverseMass = setup.inverseMass;
|
|
35
|
+
const limits = setup.limits ?? {};
|
|
36
|
+
const statics = new Float32Array(n * CLOTH_STATIC_FLOATS);
|
|
37
|
+
statics.set(inverseMass, 0);
|
|
38
|
+
const rigged = riggedOf(setup);
|
|
39
|
+
for (let i = 0; i < n; i++) {
|
|
40
|
+
const max = limits.maxDistance?.[i] ?? Infinity;
|
|
41
|
+
statics[n + i] = max < Infinity ? max : -1;
|
|
42
|
+
for (const [stops, at] of [
|
|
43
|
+
[limits.backstop, 2 * n],
|
|
44
|
+
[limits.frontstop, 4 * n],
|
|
45
|
+
]) {
|
|
46
|
+
const distance = stops?.[i * 2] ?? Infinity;
|
|
47
|
+
const radius = stops?.[i * 2 + 1] ?? 0;
|
|
48
|
+
const real = distance < Infinity && radius > 0;
|
|
49
|
+
statics[at + i * 2] = real ? distance : 0;
|
|
50
|
+
statics[at + i * 2 + 1] = real ? radius : 0;
|
|
51
|
+
}
|
|
52
|
+
statics[6 * n + i] = limits.thickness?.[i] ?? 0;
|
|
53
|
+
for (let k = 0; k < 3; k++) {
|
|
54
|
+
statics[7 * n + i * 3 + k] = setup.positions[i * 3 + k];
|
|
55
|
+
statics[10 * n + i * 3 + k] = setup.normals?.[i * 3 + k] ?? (k === 2 ? 1 : 0);
|
|
56
|
+
}
|
|
57
|
+
const at = 13 * n + i * 16;
|
|
58
|
+
if (!rigged) {
|
|
59
|
+
statics[at + 4] = 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (let k = 0; k < 4; k++) {
|
|
63
|
+
statics[at + k] = setup.joints?.[i * 4 + k] ?? 0;
|
|
64
|
+
statics[at + 4 + k] = setup.weights?.[i * 4 + k] ?? 0;
|
|
65
|
+
statics[at + 8 + k] = setup.joints2?.[i * 4 + k] ?? 0;
|
|
66
|
+
statics[at + 12 + k] = setup.weights2?.[i * 4 + k] ?? 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const { distance, bending, tethers } = setup;
|
|
70
|
+
const distances = distance.rest.length;
|
|
71
|
+
const bendings = bending?.rest.length ?? 0;
|
|
72
|
+
const tetherCount = tethers?.lengths.length ?? 0;
|
|
73
|
+
const words = new Uint32Array(distances * 4 + bendings * 6 + tetherCount * 3);
|
|
74
|
+
const floats = new Float32Array(words.buffer);
|
|
75
|
+
const distanceBatches = batchesOf('distance', distance.pairs, 2, inverseMass, distance.batches);
|
|
76
|
+
sorted(distanceBatches, (k, r) => {
|
|
77
|
+
const at = r * 4;
|
|
78
|
+
words[at] = distance.pairs[k * 2];
|
|
79
|
+
words[at + 1] = distance.pairs[k * 2 + 1];
|
|
80
|
+
floats[at + 2] = distance.rest[k];
|
|
81
|
+
floats[at + 3] = distance.compliance[k];
|
|
82
|
+
});
|
|
83
|
+
let bendingBatches = new Uint32Array([0]);
|
|
84
|
+
if (bending !== undefined) {
|
|
85
|
+
const batches = batchesOf('bending', bending.quads, 4, inverseMass, bending.batches);
|
|
86
|
+
bendingBatches = batches.batches;
|
|
87
|
+
sorted(batches, (k, r) => {
|
|
88
|
+
const at = distances * 4 + r * 6;
|
|
89
|
+
for (let j = 0; j < 4; j++)
|
|
90
|
+
words[at + j] = bending.quads[k * 4 + j];
|
|
91
|
+
floats[at + 4] = bending.rest[k];
|
|
92
|
+
floats[at + 5] = bending.compliance[k];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
let tetherBatches = new Uint32Array([0]);
|
|
96
|
+
if (tethers !== undefined) {
|
|
97
|
+
const movable = new Float32Array(n).fill(1);
|
|
98
|
+
const batches = batchesOf('tethers', tethers.particles, 1, movable, undefined);
|
|
99
|
+
tetherBatches = batches.batches;
|
|
100
|
+
sorted(batches, (k, r) => {
|
|
101
|
+
const at = distances * 4 + bendings * 6 + r * 3;
|
|
102
|
+
words[at] = tethers.particles[k];
|
|
103
|
+
words[at + 1] = tethers.anchors[k];
|
|
104
|
+
floats[at + 2] = tethers.lengths[k];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
count: n,
|
|
109
|
+
statics,
|
|
110
|
+
constraints: words,
|
|
111
|
+
distances,
|
|
112
|
+
bendings,
|
|
113
|
+
tethers: tetherCount,
|
|
114
|
+
distanceBatches: distanceBatches.batches,
|
|
115
|
+
bendingBatches,
|
|
116
|
+
tetherBatches,
|
|
117
|
+
limits: (limits.maxDistance !== undefined ? CLOTH_LIMIT_MAX : 0) |
|
|
118
|
+
(limits.backstop !== undefined ? CLOTH_LIMIT_BACK : 0) |
|
|
119
|
+
(limits.frontstop !== undefined ? CLOTH_LIMIT_FRONT : 0),
|
|
120
|
+
joints: rigged ? (setup.inverseBind?.length ?? 0) / 16 : 1,
|
|
121
|
+
rigged,
|
|
122
|
+
colliders: setup.colliders?.length ?? 0,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Each constraint `k` with the place `r` it takes in batch order. */
|
|
126
|
+
function sorted(batches, write) {
|
|
127
|
+
for (let r = 0; r < batches.order.length; r++)
|
|
128
|
+
write(batches.order[r], r);
|
|
129
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ClothBindingData } from '../../clothBindingData.ts';
|
|
2
|
+
/** The three views a skinned bind group takes for its cloth. */
|
|
3
|
+
export interface ClothViews {
|
|
4
|
+
readonly binding: GPUTextureView;
|
|
5
|
+
readonly particles: GPUTextureView;
|
|
6
|
+
readonly rest: GPUTextureView;
|
|
7
|
+
}
|
|
8
|
+
/** A one-texel float texture, as all three views, for every draw with no cloth. */
|
|
9
|
+
export declare function clothStandIn(device: GPUDevice): {
|
|
10
|
+
texture: GPUTexture;
|
|
11
|
+
views: ClothViews;
|
|
12
|
+
};
|
|
13
|
+
/** A mesh's binding and its particles at rest. */
|
|
14
|
+
export declare class GpuClothBinding {
|
|
15
|
+
readonly binding: GPUTexture;
|
|
16
|
+
readonly rest: GPUTexture;
|
|
17
|
+
readonly bindingView: GPUTextureView;
|
|
18
|
+
readonly restView: GPUTextureView;
|
|
19
|
+
readonly particles: number;
|
|
20
|
+
constructor(device: GPUDevice, data: ClothBindingData);
|
|
21
|
+
/** Its textures, for the renderer to retire once no draw of this frame can read them. */
|
|
22
|
+
get textures(): readonly GPUTexture[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One character's particles: this frame's and the last, in two textures that swap. Written from
|
|
26
|
+
* the CPU with `update`, or — `storage` — by the device solver's publish pass, `publish` then
|
|
27
|
+
* saying which texture it writes.
|
|
28
|
+
*/
|
|
29
|
+
export declare class GpuClothParticles {
|
|
30
|
+
readonly count: number;
|
|
31
|
+
/** Texels a row, which the publish pass wraps a particle index by. */
|
|
32
|
+
readonly width: number;
|
|
33
|
+
private readonly height;
|
|
34
|
+
private readonly staging;
|
|
35
|
+
/** Both textures, for the renderer to retire once no draw of this frame can read them. */
|
|
36
|
+
readonly textures: readonly [GPUTexture, GPUTexture];
|
|
37
|
+
private readonly views;
|
|
38
|
+
private latest;
|
|
39
|
+
/** The frame each texture was last written for, so a motion knows whether the other is last frame's. */
|
|
40
|
+
private readonly written;
|
|
41
|
+
private paired;
|
|
42
|
+
private pairedViews;
|
|
43
|
+
constructor(device: GPUDevice, count: number, storage?: boolean);
|
|
44
|
+
/** This frame's particles. */
|
|
45
|
+
get current(): GPUTextureView;
|
|
46
|
+
/** Last frame's. */
|
|
47
|
+
get previous(): GPUTextureView;
|
|
48
|
+
/**
|
|
49
|
+
* Where they were last frame, for a motion vector at `frame`: the other texture where it was
|
|
50
|
+
* written the frame before this one's update, and this frame's otherwise — particles not updated
|
|
51
|
+
* this frame did not move, and a first update has no past to measure from.
|
|
52
|
+
*/
|
|
53
|
+
previousAt(frame: number): GPUTextureView;
|
|
54
|
+
/** A texture's view, by index: the publish pass binds both, one at a time. */
|
|
55
|
+
viewOf(k: number): GPUTextureView;
|
|
56
|
+
/**
|
|
57
|
+
* The device is about to write the particles of `frame`: the older texture becomes this frame's,
|
|
58
|
+
* as an `update` makes it, and its index is returned for the pass that writes it.
|
|
59
|
+
*/
|
|
60
|
+
publish(frame: number): number;
|
|
61
|
+
/** Write `positions` as the particles of `frame`; last frame's stays. */
|
|
62
|
+
update(device: GPUDevice, positions: Float32Array, frame: number): void;
|
|
63
|
+
/**
|
|
64
|
+
* The three views a draw of `binding` with these particles binds, kept so a cached bind group
|
|
65
|
+
* compares equal frame after frame. Two objects, one for each texture that is current.
|
|
66
|
+
*/
|
|
67
|
+
viewsWith(binding: GpuClothBinding): ClothViews;
|
|
68
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
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.js';
|
|
15
|
+
const USAGE = 0x4 | 0x2; // TEXTURE_BINDING | COPY_DST
|
|
16
|
+
const STORAGE_BINDING = 0x8;
|
|
17
|
+
function floatTexture(device, label, width, height, texels, storage = false) {
|
|
18
|
+
const texture = device.createTexture({
|
|
19
|
+
label,
|
|
20
|
+
size: [width, height],
|
|
21
|
+
format: 'rgba32float',
|
|
22
|
+
usage: storage ? USAGE | STORAGE_BINDING : USAGE,
|
|
23
|
+
});
|
|
24
|
+
if (texels !== null) {
|
|
25
|
+
device.queue.writeTexture({ texture }, texels, { bytesPerRow: width * 16, rowsPerImage: height }, { width, height });
|
|
26
|
+
}
|
|
27
|
+
return texture;
|
|
28
|
+
}
|
|
29
|
+
/** A one-texel float texture, as all three views, for every draw with no cloth. */
|
|
30
|
+
export function clothStandIn(device) {
|
|
31
|
+
const texture = floatTexture(device, 'cloth.standIn', 1, 1, new Float32Array(4));
|
|
32
|
+
const view = texture.createView();
|
|
33
|
+
return { texture, views: { binding: view, particles: view, rest: view } };
|
|
34
|
+
}
|
|
35
|
+
/** A mesh's binding and its particles at rest. */
|
|
36
|
+
export class GpuClothBinding {
|
|
37
|
+
binding;
|
|
38
|
+
rest;
|
|
39
|
+
bindingView;
|
|
40
|
+
restView;
|
|
41
|
+
particles;
|
|
42
|
+
constructor(device, data) {
|
|
43
|
+
const size = clothTextureSize(data.weights.length * 2);
|
|
44
|
+
this.binding = floatTexture(device, 'cloth.binding', size.width, size.height, packClothBinding(data));
|
|
45
|
+
this.particles = data.rest.length / 3;
|
|
46
|
+
const rest = clothTextureSize(this.particles);
|
|
47
|
+
const texels = new Float32Array(rest.width * rest.height * 4);
|
|
48
|
+
packClothParticles(data.rest, texels);
|
|
49
|
+
this.rest = floatTexture(device, 'cloth.rest', rest.width, rest.height, texels);
|
|
50
|
+
this.bindingView = this.binding.createView();
|
|
51
|
+
this.restView = this.rest.createView();
|
|
52
|
+
}
|
|
53
|
+
/** Its textures, for the renderer to retire once no draw of this frame can read them. */
|
|
54
|
+
get textures() {
|
|
55
|
+
return [this.binding, this.rest];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One character's particles: this frame's and the last, in two textures that swap. Written from
|
|
60
|
+
* the CPU with `update`, or — `storage` — by the device solver's publish pass, `publish` then
|
|
61
|
+
* saying which texture it writes.
|
|
62
|
+
*/
|
|
63
|
+
export class GpuClothParticles {
|
|
64
|
+
count;
|
|
65
|
+
/** Texels a row, which the publish pass wraps a particle index by. */
|
|
66
|
+
width;
|
|
67
|
+
height;
|
|
68
|
+
staging;
|
|
69
|
+
/** Both textures, for the renderer to retire once no draw of this frame can read them. */
|
|
70
|
+
textures;
|
|
71
|
+
views;
|
|
72
|
+
latest = 0;
|
|
73
|
+
/** The frame each texture was last written for, so a motion knows whether the other is last frame's. */
|
|
74
|
+
written = [-1, -1];
|
|
75
|
+
paired = null;
|
|
76
|
+
pairedViews = [];
|
|
77
|
+
constructor(device, count, storage = false) {
|
|
78
|
+
this.count = count;
|
|
79
|
+
const size = clothTextureSize(count);
|
|
80
|
+
this.width = size.width;
|
|
81
|
+
this.height = size.height;
|
|
82
|
+
this.staging = new Float32Array(size.width * size.height * 4);
|
|
83
|
+
this.textures = [
|
|
84
|
+
floatTexture(device, 'cloth.particles', size.width, size.height, this.staging, storage),
|
|
85
|
+
floatTexture(device, 'cloth.particles', size.width, size.height, this.staging, storage),
|
|
86
|
+
];
|
|
87
|
+
this.views = [this.textures[0].createView(), this.textures[1].createView()];
|
|
88
|
+
}
|
|
89
|
+
/** This frame's particles. */
|
|
90
|
+
get current() {
|
|
91
|
+
return this.views[this.latest];
|
|
92
|
+
}
|
|
93
|
+
/** Last frame's. */
|
|
94
|
+
get previous() {
|
|
95
|
+
return this.views[1 - this.latest];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Where they were last frame, for a motion vector at `frame`: the other texture where it was
|
|
99
|
+
* written the frame before this one's update, and this frame's otherwise — particles not updated
|
|
100
|
+
* this frame did not move, and a first update has no past to measure from.
|
|
101
|
+
*/
|
|
102
|
+
previousAt(frame) {
|
|
103
|
+
const fresh = this.written[this.latest] === frame && this.written[1 - this.latest] === frame - 1;
|
|
104
|
+
return fresh ? this.previous : this.current;
|
|
105
|
+
}
|
|
106
|
+
/** A texture's view, by index: the publish pass binds both, one at a time. */
|
|
107
|
+
viewOf(k) {
|
|
108
|
+
return this.views[k];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The device is about to write the particles of `frame`: the older texture becomes this frame's,
|
|
112
|
+
* as an `update` makes it, and its index is returned for the pass that writes it.
|
|
113
|
+
*/
|
|
114
|
+
publish(frame) {
|
|
115
|
+
this.latest = 1 - this.latest;
|
|
116
|
+
this.written[this.latest] = frame;
|
|
117
|
+
return this.latest;
|
|
118
|
+
}
|
|
119
|
+
/** Write `positions` as the particles of `frame`; last frame's stays. */
|
|
120
|
+
update(device, positions, frame) {
|
|
121
|
+
if (positions.length !== this.count * 3) {
|
|
122
|
+
throw new Error(`cloth: ${this.count} particles and an update of ${positions.length / 3}; a cloth's ` +
|
|
123
|
+
'particle count is fixed when its particles are created');
|
|
124
|
+
}
|
|
125
|
+
packClothParticles(positions, this.staging);
|
|
126
|
+
this.latest = 1 - this.latest;
|
|
127
|
+
this.written[this.latest] = frame;
|
|
128
|
+
device.queue.writeTexture({ texture: this.textures[this.latest] }, this.staging, { bytesPerRow: this.width * 16, rowsPerImage: this.height }, { width: this.width, height: this.height });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The three views a draw of `binding` with these particles binds, kept so a cached bind group
|
|
132
|
+
* compares equal frame after frame. Two objects, one for each texture that is current.
|
|
133
|
+
*/
|
|
134
|
+
viewsWith(binding) {
|
|
135
|
+
if (this.paired !== binding) {
|
|
136
|
+
this.paired = binding;
|
|
137
|
+
this.pairedViews = [0, 1].map((k) => ({
|
|
138
|
+
binding: binding.bindingView,
|
|
139
|
+
particles: this.views[k],
|
|
140
|
+
rest: binding.restView,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
return this.pairedViews[this.latest];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
/** Every BC format, where the device carries the feature; none where it does not. */
|
|
16
|
+
export declare function gpuCompressedFormats(device: GPUDevice): CompressedTextureFormat[];
|
|
17
|
+
/** A texture holding `layers` as `name`, `levels` levels of each, written as they are. */
|
|
18
|
+
export declare function createCompressedTexture(device: GPUDevice, name: CompressedTextureFormat, layers: readonly CompressedTextureSource[], levels: number): GPUTexture;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BLOCK_BYTES } from '../../compressedSource.js';
|
|
2
|
+
const GPU_FORMAT = {
|
|
3
|
+
bc1: 'bc1-rgba-unorm',
|
|
4
|
+
'bc1-srgb': 'bc1-rgba-unorm-srgb',
|
|
5
|
+
bc2: 'bc2-rgba-unorm',
|
|
6
|
+
'bc2-srgb': 'bc2-rgba-unorm-srgb',
|
|
7
|
+
bc3: 'bc3-rgba-unorm',
|
|
8
|
+
'bc3-srgb': 'bc3-rgba-unorm-srgb',
|
|
9
|
+
bc4: 'bc4-r-unorm',
|
|
10
|
+
bc5: 'bc5-rg-unorm',
|
|
11
|
+
bc7: 'bc7-rgba-unorm',
|
|
12
|
+
'bc7-srgb': 'bc7-rgba-unorm-srgb',
|
|
13
|
+
};
|
|
14
|
+
/** Every BC format, where the device carries the feature; none where it does not. */
|
|
15
|
+
export function gpuCompressedFormats(device) {
|
|
16
|
+
return device.features.has('texture-compression-bc')
|
|
17
|
+
? Object.keys(GPU_FORMAT)
|
|
18
|
+
: [];
|
|
19
|
+
}
|
|
20
|
+
/** A texture holding `layers` as `name`, `levels` levels of each, written as they are. */
|
|
21
|
+
export function createCompressedTexture(device, name, layers, levels) {
|
|
22
|
+
const { format, width, height } = layers[0];
|
|
23
|
+
const texture = device.createTexture({
|
|
24
|
+
label: 'surface.texture.bc',
|
|
25
|
+
size: [width, height, layers.length],
|
|
26
|
+
format: GPU_FORMAT[name],
|
|
27
|
+
mipLevelCount: levels,
|
|
28
|
+
usage: 0x2 | 0x4, // COPY_DST | TEXTURE_BINDING
|
|
29
|
+
});
|
|
30
|
+
for (let level = 0; level < levels; level++) {
|
|
31
|
+
const across = Math.ceil(Math.max(1, width >> level) / 4);
|
|
32
|
+
const down = Math.ceil(Math.max(1, height >> level) / 4);
|
|
33
|
+
for (let layer = 0; layer < layers.length; layer++) {
|
|
34
|
+
device.queue.writeTexture({ texture, mipLevel: level, origin: [0, 0, layer] }, layers[layer].levels[level], { bytesPerRow: across * BLOCK_BYTES[format], rowsPerImage: down }, { width: across * 4, height: down * 4, depthOrArrayLayers: 1 });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return texture;
|
|
38
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
/** The host of a renderer that has one: the WebGPU backend, and nothing else. */
|
|
21
|
+
export declare function computeHostOf(renderer: object): ComputeHost | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEPTH_FRAG_WGSL } from '../../shaders/generated/depth.wgsl.ts';
|
|
2
2
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
3
|
+
import type { ClothViews } from './clothTextures.ts';
|
|
3
4
|
/**
|
|
4
5
|
* The shadow pass: the world drawn from the light, into a depth map.
|
|
5
6
|
*
|
|
@@ -87,13 +88,18 @@ export declare const DEPTH_CUTOUT_VERT_FIELDS: {
|
|
|
87
88
|
readonly size: 8;
|
|
88
89
|
readonly type: "vec2";
|
|
89
90
|
};
|
|
90
|
-
readonly
|
|
91
|
+
readonly uUvOffset: {
|
|
91
92
|
readonly offset: 168;
|
|
92
|
-
readonly size:
|
|
93
|
-
readonly type: "
|
|
93
|
+
readonly size: 8;
|
|
94
|
+
readonly type: "vec2";
|
|
95
|
+
};
|
|
96
|
+
readonly uAlphaCutout: {
|
|
97
|
+
readonly offset: 176;
|
|
98
|
+
readonly size: 8;
|
|
99
|
+
readonly type: "vec2";
|
|
94
100
|
};
|
|
95
101
|
};
|
|
96
|
-
export declare const DEPTH_CUTOUT_VERT_SIZE:
|
|
102
|
+
export declare const DEPTH_CUTOUT_VERT_SIZE: 192;
|
|
97
103
|
export declare const DEPTH_INSTANCED_CUTOUT_VERT_FIELDS: {
|
|
98
104
|
readonly uLightViewProj: {
|
|
99
105
|
readonly offset: 0;
|
|
@@ -105,10 +111,15 @@ export declare const DEPTH_INSTANCED_CUTOUT_VERT_FIELDS: {
|
|
|
105
111
|
readonly size: 8;
|
|
106
112
|
readonly type: "vec2";
|
|
107
113
|
};
|
|
108
|
-
readonly
|
|
114
|
+
readonly uUvOffset: {
|
|
109
115
|
readonly offset: 72;
|
|
110
|
-
readonly size:
|
|
111
|
-
readonly type: "
|
|
116
|
+
readonly size: 8;
|
|
117
|
+
readonly type: "vec2";
|
|
118
|
+
};
|
|
119
|
+
readonly uAlphaCutout: {
|
|
120
|
+
readonly offset: 80;
|
|
121
|
+
readonly size: 8;
|
|
122
|
+
readonly type: "vec2";
|
|
112
123
|
};
|
|
113
124
|
};
|
|
114
125
|
/** Re-exported so the scatter depth pass can pair with the same fragment stage. */
|
|
@@ -157,7 +168,9 @@ palette?: GPUTextureView | null,
|
|
|
157
168
|
cutout?: {
|
|
158
169
|
readonly view: GPUTextureView;
|
|
159
170
|
readonly sampler: GPUSampler;
|
|
160
|
-
} | null
|
|
171
|
+
} | null,
|
|
172
|
+
/** A skinned group's cloth views: the caster's, or the stand-in where it has none. */
|
|
173
|
+
cloth?: ClothViews | null): GPUBindGroup;
|
|
161
174
|
/**
|
|
162
175
|
* The depth pipeline.
|
|
163
176
|
*
|
|
@@ -189,4 +202,11 @@ skinned?: boolean,
|
|
|
189
202
|
*/
|
|
190
203
|
instanced?: boolean,
|
|
191
204
|
/** Whether this pipeline casts a cutout: the variant that reads a map and discards by it. */
|
|
192
|
-
cutout?: boolean
|
|
205
|
+
cutout?: boolean,
|
|
206
|
+
/**
|
|
207
|
+
* Whether a skinned caster reads its second four influences, as its visible draw does. The key
|
|
208
|
+
* must carry it, so the shadow parts from nothing the body does. See `skinning.ts`.
|
|
209
|
+
*/
|
|
210
|
+
skinEight?: boolean,
|
|
211
|
+
/** Whether a skinned caster is placed by a cloth binding, as its visible draw is. The key carries it. */
|
|
212
|
+
cloth?: boolean): GPURenderPipeline;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEPTH_BINDINGS, DEPTH_CUTOUT_FRAG_WGSL, DEPTH_CUTOUT_VERT_WGSL, DEPTH_FRAG_WGSL, DEPTH_INSTANCED_CUTOUT_VERT_WGSL, DEPTH_INSTANCED_VERT_WGSL, DEPTH_SKINNED_VERT_WGSL, DEPTH_VERT_WGSL, } from '../../shaders/generated/depth.wgsl.js';
|
|
2
2
|
import { vertexBufferLayouts } from './buffers.js';
|
|
3
|
+
import { vertexConstants } from './flatPass.js';
|
|
3
4
|
import { shaderModule } from './shaderModules.js';
|
|
4
5
|
/**
|
|
5
6
|
* The shadow pass: the world drawn from the light, into a depth map.
|
|
@@ -12,9 +13,10 @@ export const DEPTH_VERT_FIELDS = DEPTH_BINDINGS.DEPTH_VERT.fields;
|
|
|
12
13
|
export const DEPTH_VERT_SIZE = DEPTH_BINDINGS.DEPTH_VERT.uniformSize;
|
|
13
14
|
export const DEPTH_FRAG_SIZE = DEPTH_BINDINGS.DEPTH_FRAG.uniformSize;
|
|
14
15
|
/*
|
|
15
|
-
* The cutout variants' vertex blocks. The rigid one is the plain block with `uUvScale
|
|
16
|
-
* `uAlphaCutout` after every other field, so a slot written for it is written with
|
|
17
|
-
* offsets plus these
|
|
16
|
+
* The cutout variants' vertex blocks. The rigid one is the plain block with `uUvScale`,
|
|
17
|
+
* `uUvOffset` and `uAlphaCutout` after every other field, so a slot written for it is written with
|
|
18
|
+
* the plain offsets plus these three; the instanced one has no model and no wind, so its three sit
|
|
19
|
+
* earlier.
|
|
18
20
|
*/
|
|
19
21
|
export const DEPTH_CUTOUT_VERT_FIELDS = DEPTH_BINDINGS.DEPTH_CUTOUT_VERT.fields;
|
|
20
22
|
export const DEPTH_CUTOUT_VERT_SIZE = DEPTH_BINDINGS.DEPTH_CUTOUT_VERT.uniformSize;
|
|
@@ -29,6 +31,12 @@ const FRAG_BINDING = DEPTH_BINDINGS.DEPTH_FRAG.uniforms;
|
|
|
29
31
|
const PREVIOUS = DEPTH_BINDINGS.DEPTH_FRAG.textures.uPreviousShadowMap;
|
|
30
32
|
/** Where the skinned vertex stage reads the joint palette. Generated, not chosen here. */
|
|
31
33
|
export const DEPTH_PALETTE_BINDING = DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uJointPalette;
|
|
34
|
+
/** And the cloth binding's three textures, which every skinned caster declares. */
|
|
35
|
+
const CLOTH_TEXTURES = [
|
|
36
|
+
DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothBinding,
|
|
37
|
+
DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothParticles,
|
|
38
|
+
DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothRest,
|
|
39
|
+
];
|
|
32
40
|
const VISIBILITY_VERTEX = 0x1;
|
|
33
41
|
const VISIBILITY_FRAGMENT = 0x2;
|
|
34
42
|
/**
|
|
@@ -63,13 +71,11 @@ cutout = false) {
|
|
|
63
71
|
coordinate with nothing to filter, which is what `flatPass.ts` binds for the same
|
|
64
72
|
palette. naga declares a sampler for it and nothing ever reads one. */
|
|
65
73
|
...(skinned
|
|
66
|
-
? [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
},
|
|
72
|
-
]
|
|
74
|
+
? [DEPTH_PALETTE_BINDING, ...CLOTH_TEXTURES].map((binding) => ({
|
|
75
|
+
binding: binding.texture,
|
|
76
|
+
visibility: VISIBILITY_VERTEX,
|
|
77
|
+
texture: { sampleType: 'unfilterable-float' },
|
|
78
|
+
}))
|
|
73
79
|
: []),
|
|
74
80
|
{
|
|
75
81
|
binding: VERT_BINDING,
|
|
@@ -139,7 +145,9 @@ export function createDepthBindGroup(device, layout, perDraw, perPass, previous,
|
|
|
139
145
|
/** The joint palette, for a layout built with `skinned`. Omit for the rigid layout. */
|
|
140
146
|
palette = null,
|
|
141
147
|
/** The cutout map and its sampler, for a layout built with `cutout`. */
|
|
142
|
-
cutout = null
|
|
148
|
+
cutout = null,
|
|
149
|
+
/** A skinned group's cloth views: the caster's, or the stand-in where it has none. */
|
|
150
|
+
cloth = null) {
|
|
143
151
|
return device.createBindGroup({
|
|
144
152
|
label: palette !== null
|
|
145
153
|
? 'depth.bindGroup.skinned'
|
|
@@ -159,6 +167,13 @@ cutout = null) {
|
|
|
159
167
|
{ binding: PREVIOUS.texture, resource: previous },
|
|
160
168
|
{ binding: PREVIOUS.sampler, resource: sampler },
|
|
161
169
|
...(palette !== null ? [{ binding: DEPTH_PALETTE_BINDING.texture, resource: palette }] : []),
|
|
170
|
+
...(palette !== null
|
|
171
|
+
? [
|
|
172
|
+
{ binding: CLOTH_TEXTURES[0].texture, resource: clothOf(cloth).binding },
|
|
173
|
+
{ binding: CLOTH_TEXTURES[1].texture, resource: clothOf(cloth).particles },
|
|
174
|
+
{ binding: CLOTH_TEXTURES[2].texture, resource: clothOf(cloth).rest },
|
|
175
|
+
]
|
|
176
|
+
: []),
|
|
162
177
|
...(cutout !== null
|
|
163
178
|
? [
|
|
164
179
|
{ binding: CUTOUT_MAP.texture, resource: cutout.view },
|
|
@@ -199,7 +214,14 @@ skinned = false,
|
|
|
199
214
|
*/
|
|
200
215
|
instanced = false,
|
|
201
216
|
/** Whether this pipeline casts a cutout: the variant that reads a map and discards by it. */
|
|
202
|
-
cutout = false
|
|
217
|
+
cutout = false,
|
|
218
|
+
/**
|
|
219
|
+
* Whether a skinned caster reads its second four influences, as its visible draw does. The key
|
|
220
|
+
* must carry it, so the shadow parts from nothing the body does. See `skinning.ts`.
|
|
221
|
+
*/
|
|
222
|
+
skinEight = false,
|
|
223
|
+
/** Whether a skinned caster is placed by a cloth binding, as its visible draw is. The key carries it. */
|
|
224
|
+
cloth = false) {
|
|
203
225
|
return cache.get(key, () => ({
|
|
204
226
|
label: key,
|
|
205
227
|
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
|
|
@@ -215,6 +237,9 @@ cutout = false) {
|
|
|
215
237
|
: { label: 'depth.vert', code: DEPTH_VERT_WGSL }),
|
|
216
238
|
entryPoint: 'main',
|
|
217
239
|
buffers: vertexBufferLayouts(present, instanced),
|
|
240
|
+
...(skinned && !instanced && !cutout
|
|
241
|
+
? { constants: vertexConstants(DEPTH_BINDINGS.DEPTH_SKINNED_VERT, skinEight, cloth) }
|
|
242
|
+
: {}),
|
|
218
243
|
},
|
|
219
244
|
fragment: {
|
|
220
245
|
module: shaderModule(device, cutout
|
|
@@ -292,3 +317,9 @@ cutout = false) {
|
|
|
292
317
|
},
|
|
293
318
|
}));
|
|
294
319
|
}
|
|
320
|
+
function clothOf(cloth) {
|
|
321
|
+
if (cloth === null) {
|
|
322
|
+
throw new Error('depthPass: a skinned bind group needs cloth views, the stand-in at the least');
|
|
323
|
+
}
|
|
324
|
+
return cloth;
|
|
325
|
+
}
|