@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.
- package/CHANGELOG.json +133 -11
- package/README.md +43 -38
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -4
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/backend/api.d.ts +10 -3
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +189 -4
- package/dist/render/backend/webgl2/renderer.js +614 -66
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +24 -4
- package/dist/render/backend/webgpu/buffers.js +116 -59
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/drawCommand.d.ts +20 -4
- package/dist/render/backend/webgpu/drawCommand.js +11 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +137 -20
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
- package/dist/render/backend/webgpu/pipelineCache.js +46 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +200 -35
- package/dist/render/backend/webgpu/renderer.js +993 -168
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +242 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +14 -0
- package/dist/render/mesh.js +44 -14
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +21 -0
- package/dist/render/shaders/flat/index.js +42 -7
- package/dist/render/shaders/flat/layered.d.ts +19 -0
- package/dist/render/shaders/flat/layered.js +115 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +58 -21
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +14 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +42 -20
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +59 -27
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3616 -3042
- package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceLayers.d.ts +39 -0
- package/dist/render/surfaceLayers.js +26 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +68 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/package.json +6 -6
- package/src/index.ts +12 -4
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +723 -79
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +195 -70
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +25 -4
- package/src/render/backend/webgpu/flatPass.ts +212 -41
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +53 -7
- package/src/render/backend/webgpu/renderer.ts +1201 -202
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +364 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +72 -14
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +63 -7
- package/src/render/shaders/flat/layered.ts +115 -0
- package/src/render/shaders/flat/main.ts +58 -21
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +14 -3
- package/src/render/shaders/flat/overlay.ts +42 -20
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +59 -27
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceLayers.ts +57 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +124 -27
- package/src/render/vertexPacking.ts +109 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -66,6 +66,14 @@ export interface MeshInstances {
|
|
|
66
66
|
*/
|
|
67
67
|
readonly uvRegions?: Float32Array;
|
|
68
68
|
/** How many instances the arrays hold. Fixed at creation; the buffers are sized from it. */
|
|
69
|
+
/**
|
|
70
|
+
* Two floats each, the instance's clock for a batch that plays a bone animation
|
|
71
|
+
* (`InstancedOptions.animation`): its phase in seconds, added to the scene's time once the rate has
|
|
72
|
+
* scaled it, and its rate, 1 for the clip's own speed. **Absent, or an instance past its end, is
|
|
73
|
+
* `(0, 1)`** — every instance at the clip's speed and in step. Read by `uploadInstanced`, as the
|
|
74
|
+
* matrices are; a batch with no animation ignores it.
|
|
75
|
+
*/
|
|
76
|
+
readonly clocks?: Float32Array;
|
|
69
77
|
readonly capacity: number;
|
|
70
78
|
/** How many are live. The rest of the buffer is neither uploaded nor drawn. */
|
|
71
79
|
count: number;
|
|
@@ -103,6 +111,7 @@ export declare const INSTANCE_STRIDE: number;
|
|
|
103
111
|
* Writes nothing beyond `count`, and allocates nothing: `out` is the caller's staging array.
|
|
104
112
|
*/
|
|
105
113
|
export declare function packInstances(instances: MeshInstances, out: Float32Array): void;
|
|
114
|
+
import type { BoneAnimationHandle } from './boneAnimation.ts';
|
|
106
115
|
/** How a batch made by `createInstanced` behaves. */
|
|
107
116
|
export interface InstancedOptions {
|
|
108
117
|
/**
|
|
@@ -117,4 +126,21 @@ export interface InstancedOptions {
|
|
|
117
126
|
* place gains only the whole-batch test, which it could as well do itself.
|
|
118
127
|
*/
|
|
119
128
|
readonly cull?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* A bone animation every instance plays (`createBoneAnimation`): each vertex follows the bone its
|
|
131
|
+
* mesh's second coordinates name, at the instance's own moment of the clip (`MeshInstances.clocks`
|
|
132
|
+
* and the clock `setAnimationTime` sets), in the colour pass and in every shadow. The mesh must
|
|
133
|
+
* carry second coordinates. See `boneAnimation.ts`.
|
|
134
|
+
*
|
|
135
|
+
* **An animated batch is culled whole, never instance by instance**: its instances read their
|
|
136
|
+
* clocks by index, which a cull that compacts the survivors would reorder. And its bounds are the
|
|
137
|
+
* rest pose's, so a clip that carries vertices far from it wants a mesh whose bounds hold them.
|
|
138
|
+
*
|
|
139
|
+
* **What it gives up**: under reconstruction or the temporal resolve, a crowd's own movement is not
|
|
140
|
+
* in the frame's motion — the motion pass places a batch by its matrices, not by its clip — so its
|
|
141
|
+
* moving parts are reprojected as though still, which softens a fast clip a little; and a glass
|
|
142
|
+
* batch's coloured shadow is cast from rest. What would make the first wrong is a clip fast enough
|
|
143
|
+
* at the size it is seen to smear, which is then the motion pass's to learn.
|
|
144
|
+
*/
|
|
145
|
+
readonly animation?: BoneAnimationHandle;
|
|
120
146
|
}
|
|
@@ -243,6 +243,16 @@ export declare const SKIN_BLUR_ALBEDO_UNIT: number;
|
|
|
243
243
|
*/
|
|
244
244
|
export declare const PARTICLE_SPRITE_UNIT: number;
|
|
245
245
|
export declare const PARTICLE_DEPTH_UNIT: number;
|
|
246
|
+
/**
|
|
247
|
+
* A bone animation's places and turns and its batch's clocks (`shaders/boneAnimation.ts`): vertex
|
|
248
|
+
* samplers of the animated instanced program, so they count against the vertex stage's sixteen, not
|
|
249
|
+
* the lit stage's. Numbered past every unit before them rather than sharing the cloth's, which no
|
|
250
|
+
* animated program declares: a unit two programs both write is a binding one draw can leave for the
|
|
251
|
+
* other to read. Twenty-six to twenty-eight, inside the combined pool of thirty-two.
|
|
252
|
+
*/
|
|
253
|
+
export declare const BONE_PLACES_TEXTURE_UNIT: number;
|
|
254
|
+
export declare const BONE_TURNS_TEXTURE_UNIT: number;
|
|
255
|
+
export declare const INSTANCE_CLOCKS_TEXTURE_UNIT: number;
|
|
246
256
|
/**
|
|
247
257
|
* A cookie's tile, in texels a side.
|
|
248
258
|
*
|
|
@@ -244,6 +244,16 @@ export const SKIN_BLUR_ALBEDO_UNIT = SKIN_BLUR_DEPTH_UNIT + 1;
|
|
|
244
244
|
*/
|
|
245
245
|
export const PARTICLE_SPRITE_UNIT = SKIN_BLUR_ALBEDO_UNIT + 1;
|
|
246
246
|
export const PARTICLE_DEPTH_UNIT = PARTICLE_SPRITE_UNIT + 1;
|
|
247
|
+
/**
|
|
248
|
+
* A bone animation's places and turns and its batch's clocks (`shaders/boneAnimation.ts`): vertex
|
|
249
|
+
* samplers of the animated instanced program, so they count against the vertex stage's sixteen, not
|
|
250
|
+
* the lit stage's. Numbered past every unit before them rather than sharing the cloth's, which no
|
|
251
|
+
* animated program declares: a unit two programs both write is a binding one draw can leave for the
|
|
252
|
+
* other to read. Twenty-six to twenty-eight, inside the combined pool of thirty-two.
|
|
253
|
+
*/
|
|
254
|
+
export const BONE_PLACES_TEXTURE_UNIT = PARTICLE_DEPTH_UNIT + 1;
|
|
255
|
+
export const BONE_TURNS_TEXTURE_UNIT = BONE_PLACES_TEXTURE_UNIT + 1;
|
|
256
|
+
export const INSTANCE_CLOCKS_TEXTURE_UNIT = BONE_TURNS_TEXTURE_UNIT + 1;
|
|
247
257
|
/**
|
|
248
258
|
* A cookie's tile, in texels a side.
|
|
249
259
|
*
|
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
import { mat4 } from 'gl-matrix';
|
|
2
2
|
import type { Vec3 } from '../math/color.ts';
|
|
3
3
|
export declare function computeLightMatrix(lightDir: Vec3, focusX: number, focusY: number, focusZ: number, radius: number, shadowMapSize: number, out: mat4): number;
|
|
4
|
+
/**
|
|
5
|
+
* The depth an orthographic light matrix spans, in metres: what `computeLightMatrix` returns, read
|
|
6
|
+
* back off any such matrix. Its depth row is the light's axis scaled by two over the span, so the
|
|
7
|
+
* span is two over that row's length — whatever focus, radius or bearing built it.
|
|
8
|
+
*
|
|
9
|
+
* **For a matrix the renderer is handed rather than one it built**: the sun's moving layer is read
|
|
10
|
+
* through the matrix its shadow pass was drawn with, and its depth tolerance and distance fade are
|
|
11
|
+
* in metres of that matrix. What would make this wrong is a perspective light, whose depth is not
|
|
12
|
+
* linear; the sun's never is.
|
|
13
|
+
*/
|
|
14
|
+
export declare function orthographicDepthSpan(m: ArrayLike<number>): number;
|
|
@@ -43,3 +43,17 @@ export function computeLightMatrix(lightDir, focusX, focusY, focusZ, radius, sha
|
|
|
43
43
|
// to express its residual depth tolerance in stable world-space metres.
|
|
44
44
|
return radius * 6;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The depth an orthographic light matrix spans, in metres: what `computeLightMatrix` returns, read
|
|
48
|
+
* back off any such matrix. Its depth row is the light's axis scaled by two over the span, so the
|
|
49
|
+
* span is two over that row's length — whatever focus, radius or bearing built it.
|
|
50
|
+
*
|
|
51
|
+
* **For a matrix the renderer is handed rather than one it built**: the sun's moving layer is read
|
|
52
|
+
* through the matrix its shadow pass was drawn with, and its depth tolerance and distance fade are
|
|
53
|
+
* in metres of that matrix. What would make this wrong is a perspective light, whose depth is not
|
|
54
|
+
* linear; the sun's never is.
|
|
55
|
+
*/
|
|
56
|
+
export function orthographicDepthSpan(m) {
|
|
57
|
+
const length = Math.hypot(m[2], m[6], m[10]);
|
|
58
|
+
return length > 0 ? 2 / length : 1;
|
|
59
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* light = irradiance(uv) * max(0, dot(d.xyz, n) + d.w)
|
|
16
16
|
* diffuse += albedo * (1 - metal) * light
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* A directional lightmap read, once its own vectors have decoded the
|
|
19
19
|
* page — the first-order spherical harmonic its direction carries, in this engine's axes. **Added
|
|
20
20
|
* to what the dynamic lights give**, and to the ambient: a consumer whose bake already holds the
|
|
21
21
|
* sky's light zeroes the ambient for those draws itself, with `setAmbientSH` and nine zeros.
|
package/dist/render/mesh.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export declare class Mesh {
|
|
|
26
26
|
* same frame.
|
|
27
27
|
*/
|
|
28
28
|
readonly indexCount: number;
|
|
29
|
+
/** `UNSIGNED_SHORT` or `UNSIGNED_INT`, as `compactIndices` stored them: every draw says which. */
|
|
30
|
+
private readonly indexType;
|
|
29
31
|
/**
|
|
30
32
|
* The constant values this mesh's *absent* attributes are shaded with, re-applied on
|
|
31
33
|
* every draw.
|
|
@@ -63,6 +65,11 @@ export declare class Mesh {
|
|
|
63
65
|
readonly isSkinnedEight: boolean;
|
|
64
66
|
/** Whether it has texture coordinates, without which a material's maps read one texel. */
|
|
65
67
|
readonly hasUvs: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Whether it carries a grain lane, which is where second coordinates ride (`lightmap.ts`): what
|
|
70
|
+
* a bone animation reads each vertex's bone from.
|
|
71
|
+
*/
|
|
72
|
+
readonly hasGrain: boolean;
|
|
66
73
|
/** How many vertices it has, which a cloth binding is checked against. */
|
|
67
74
|
readonly vertexCount: number;
|
|
68
75
|
/**
|
|
@@ -137,7 +144,14 @@ export declare class Mesh {
|
|
|
137
144
|
* still visible, which is the kind of bug that only shows up at the edge of the frame.
|
|
138
145
|
*/
|
|
139
146
|
update(gl: WebGL2RenderingContext, positions: Float32Array, normals?: Float32Array): void;
|
|
147
|
+
/**
|
|
148
|
+
* One attribute's buffer, as floats or in its sixteen-bit fixed point (`vertexPacking.ts`), which
|
|
149
|
+
* WebGL2 reads back through a normalised `SHORT` or `UNSIGNED_SHORT`: three lanes wide where
|
|
150
|
+
* WebGPU needs four, so a normal costs six bytes here.
|
|
151
|
+
*/
|
|
140
152
|
private attachAttribute;
|
|
153
|
+
/** A buffer, or the one value every vertex shares as a constant, as the packing says. */
|
|
154
|
+
private attachShared;
|
|
141
155
|
/**
|
|
142
156
|
* Give a bound buffer its storage, and its contents now or later.
|
|
143
157
|
*
|
package/dist/render/mesh.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { MorphTexture } from './backend/webgl2/morphTexture.js';
|
|
3
3
|
import { ABSENT_ATTRIBUTE } from './vertexDefaults.js';
|
|
4
4
|
import { UPLOAD_BYTES_PER_STEP } from './uploadStep.js';
|
|
5
|
+
import { compactIndices } from './indexWidth.js';
|
|
6
|
+
import { fixedArray, sharedValue, vertexPackingOf, } from './vertexPacking.js';
|
|
5
7
|
/*
|
|
6
8
|
* `MeshData` and its validator live in `@driftengine/drft`, because the container's whole
|
|
7
9
|
* job is to carry one and the format package must be readable by something that never
|
|
@@ -99,6 +101,8 @@ export class Mesh {
|
|
|
99
101
|
* same frame.
|
|
100
102
|
*/
|
|
101
103
|
indexCount;
|
|
104
|
+
/** `UNSIGNED_SHORT` or `UNSIGNED_INT`, as `compactIndices` stored them: every draw says which. */
|
|
105
|
+
indexType;
|
|
102
106
|
/**
|
|
103
107
|
* The constant values this mesh's *absent* attributes are shaded with, re-applied on
|
|
104
108
|
* every draw.
|
|
@@ -136,6 +140,11 @@ export class Mesh {
|
|
|
136
140
|
isSkinnedEight;
|
|
137
141
|
/** Whether it has texture coordinates, without which a material's maps read one texel. */
|
|
138
142
|
hasUvs;
|
|
143
|
+
/**
|
|
144
|
+
* Whether it carries a grain lane, which is where second coordinates ride (`lightmap.ts`): what
|
|
145
|
+
* a bone animation reads each vertex's bone from.
|
|
146
|
+
*/
|
|
147
|
+
hasGrain;
|
|
139
148
|
/** How many vertices it has, which a cloth binding is checked against. */
|
|
140
149
|
vertexCount;
|
|
141
150
|
/**
|
|
@@ -202,12 +211,14 @@ export class Mesh {
|
|
|
202
211
|
* that has been told a lie every frame is the shape of a stall nobody can find.
|
|
203
212
|
*/
|
|
204
213
|
const usage = dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;
|
|
214
|
+
/* How each attribute travels: sixteen-bit fixed point, or one value as a constant. */
|
|
215
|
+
const packing = vertexPackingOf(data, dynamic);
|
|
205
216
|
const positions = this.attachAttribute(gl, ATTR_POSITION, data.positions, 3, usage);
|
|
206
|
-
const normals = this.attachAttribute(gl, ATTR_NORMAL, data.normals, 3, usage);
|
|
217
|
+
const normals = this.attachAttribute(gl, ATTR_NORMAL, data.normals, 3, usage, packing.normals);
|
|
207
218
|
if (dynamic)
|
|
208
219
|
this.deformable = { positions, normals };
|
|
209
|
-
this.
|
|
210
|
-
this.
|
|
220
|
+
this.attachShared(gl, ATTR_COLOR, data.colors, 3, packing.colors);
|
|
221
|
+
this.attachShared(gl, ATTR_EMISSIVE, data.emissive, 1, packing.emissive);
|
|
211
222
|
/*
|
|
212
223
|
* Absent means "not shiny", supplied as a constant rather than as a buffer of
|
|
213
224
|
* zeroes. `vertexAttrib1f` sets the value a *disabled* attribute reads, so this
|
|
@@ -284,7 +295,7 @@ export class Mesh {
|
|
|
284
295
|
}
|
|
285
296
|
else {
|
|
286
297
|
this.attachAttribute(gl, ATTR_JOINTS, data.joints, 4);
|
|
287
|
-
this.attachAttribute(gl, ATTR_WEIGHTS, data.weights, 4);
|
|
298
|
+
this.attachAttribute(gl, ATTR_WEIGHTS, data.weights, 4, gl.STATIC_DRAW, packing.weights);
|
|
288
299
|
}
|
|
289
300
|
/* The fifth to eighth influences, or zeros, which add nothing. See `isSkinnedEight`. */
|
|
290
301
|
this.isSkinnedEight = this.isSkinned && data.joints2 !== undefined;
|
|
@@ -294,7 +305,7 @@ export class Mesh {
|
|
|
294
305
|
}
|
|
295
306
|
else {
|
|
296
307
|
this.attachAttribute(gl, ATTR_JOINTS2, data.joints2, 4);
|
|
297
|
-
this.attachAttribute(gl, ATTR_WEIGHTS2, data.weights2, 4);
|
|
308
|
+
this.attachAttribute(gl, ATTR_WEIGHTS2, data.weights2, 4, gl.STATIC_DRAW, packing.weights2);
|
|
298
309
|
}
|
|
299
310
|
this.hasTangents = data.tangents !== undefined;
|
|
300
311
|
if (data.tangents === undefined) {
|
|
@@ -302,8 +313,9 @@ export class Mesh {
|
|
|
302
313
|
this.constants.push({ location: ATTR_TANGENT, value: ABSENT_ATTRIBUTE['tangents'] });
|
|
303
314
|
}
|
|
304
315
|
else {
|
|
305
|
-
this.attachAttribute(gl, ATTR_TANGENT, data.tangents, 4);
|
|
316
|
+
this.attachAttribute(gl, ATTR_TANGENT, data.tangents, 4, gl.STATIC_DRAW, packing.tangents);
|
|
306
317
|
}
|
|
318
|
+
this.hasGrain = data.grain !== undefined;
|
|
307
319
|
if (data.grain === undefined) {
|
|
308
320
|
gl.disableVertexAttribArray(ATTR_GRAIN);
|
|
309
321
|
this.constants.push({ location: ATTR_GRAIN, value: ABSENT_ATTRIBUTE['grain'] });
|
|
@@ -331,7 +343,9 @@ export class Mesh {
|
|
|
331
343
|
this.buffers.push(indexBuffer);
|
|
332
344
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
333
345
|
this.indexBuffer = indexBuffer;
|
|
334
|
-
|
|
346
|
+
const indices = compactIndices(data.indices, this.vertexCount);
|
|
347
|
+
this.indexType = indices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
|
|
348
|
+
this.allocate(gl, gl.ELEMENT_ARRAY_BUFFER, indexBuffer, indices, gl.STATIC_DRAW);
|
|
335
349
|
gl.bindVertexArray(null);
|
|
336
350
|
}
|
|
337
351
|
/** Caller is responsible for having the flat program + uniforms bound. */
|
|
@@ -348,7 +362,7 @@ export class Mesh {
|
|
|
348
362
|
const value = constant.value;
|
|
349
363
|
applyConstant(gl, constant.location, value);
|
|
350
364
|
}
|
|
351
|
-
gl.drawElements(gl.TRIANGLES, this.indexCount,
|
|
365
|
+
gl.drawElements(gl.TRIANGLES, this.indexCount, this.indexType, 0);
|
|
352
366
|
}
|
|
353
367
|
/**
|
|
354
368
|
* A vertex array for one instanced batch: every attribute this mesh binds from a buffer, its index
|
|
@@ -368,7 +382,7 @@ export class Mesh {
|
|
|
368
382
|
for (const attribute of this.layout) {
|
|
369
383
|
gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer);
|
|
370
384
|
gl.enableVertexAttribArray(attribute.location);
|
|
371
|
-
gl.vertexAttribPointer(attribute.location, attribute.size,
|
|
385
|
+
gl.vertexAttribPointer(attribute.location, attribute.size, attribute.type, attribute.normalized, 0, 0);
|
|
372
386
|
}
|
|
373
387
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
374
388
|
gl.bindBuffer(gl.ARRAY_BUFFER, instances);
|
|
@@ -393,7 +407,7 @@ export class Mesh {
|
|
|
393
407
|
const value = constant.value;
|
|
394
408
|
applyConstant(gl, constant.location, value);
|
|
395
409
|
}
|
|
396
|
-
gl.drawElementsInstanced(gl.TRIANGLES, this.indexCount,
|
|
410
|
+
gl.drawElementsInstanced(gl.TRIANGLES, this.indexCount, this.indexType, 0, count);
|
|
397
411
|
}
|
|
398
412
|
dispose(gl) {
|
|
399
413
|
this.morph?.dispose(gl);
|
|
@@ -431,18 +445,34 @@ export class Mesh {
|
|
|
431
445
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, normals);
|
|
432
446
|
}
|
|
433
447
|
}
|
|
434
|
-
|
|
448
|
+
/**
|
|
449
|
+
* One attribute's buffer, as floats or in its sixteen-bit fixed point (`vertexPacking.ts`), which
|
|
450
|
+
* WebGL2 reads back through a normalised `SHORT` or `UNSIGNED_SHORT`: three lanes wide where
|
|
451
|
+
* WebGPU needs four, so a normal costs six bytes here.
|
|
452
|
+
*/
|
|
453
|
+
attachAttribute(gl, location, data, size, usage = gl.STATIC_DRAW, packing = 'float') {
|
|
435
454
|
const buffer = gl.createBuffer();
|
|
436
455
|
if (buffer === null)
|
|
437
456
|
throw new Error('createBuffer failed');
|
|
438
457
|
this.buffers.push(buffer);
|
|
439
458
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
440
|
-
|
|
459
|
+
const fixed = packing === 'snorm16' || packing === 'unorm16';
|
|
460
|
+
this.allocate(gl, gl.ARRAY_BUFFER, buffer, fixed ? fixedArray(data, packing) : data, usage);
|
|
461
|
+
const type = packing === 'snorm16' ? gl.SHORT : packing === 'unorm16' ? gl.UNSIGNED_SHORT : gl.FLOAT;
|
|
441
462
|
gl.enableVertexAttribArray(location);
|
|
442
|
-
gl.vertexAttribPointer(location, size,
|
|
443
|
-
this.layout.push({ location, buffer, size });
|
|
463
|
+
gl.vertexAttribPointer(location, size, type, fixed, 0, 0);
|
|
464
|
+
this.layout.push({ location, buffer, size, type, normalized: fixed });
|
|
444
465
|
return buffer;
|
|
445
466
|
}
|
|
467
|
+
/** A buffer, or the one value every vertex shares as a constant, as the packing says. */
|
|
468
|
+
attachShared(gl, location, data, size, packing) {
|
|
469
|
+
if (packing !== 'constant') {
|
|
470
|
+
this.attachAttribute(gl, location, data, size, gl.STATIC_DRAW, packing);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
gl.disableVertexAttribArray(location);
|
|
474
|
+
this.constants.push({ location, value: sharedValue(data, size) });
|
|
475
|
+
}
|
|
446
476
|
/**
|
|
447
477
|
* Give a bound buffer its storage, and its contents now or later.
|
|
448
478
|
*
|
|
@@ -71,10 +71,10 @@ export interface PointLightSource {
|
|
|
71
71
|
inLightField?: boolean;
|
|
72
72
|
/**
|
|
73
73
|
* How the light fades with distance when it names its own way: `(1 - (d/R)^2)^n` with no distance
|
|
74
|
-
* term, `R` its radius and `n` this.
|
|
75
|
-
* so a rig authored
|
|
76
|
-
* the space it was authored for rather than one that ends a few metres out. Absent or
|
|
77
|
-
* frame's own falloff (`pointLightFalloff`), which is every light before 4.8.7.
|
|
74
|
+
* term, `R` its radius and `n` this. The usual falloff for a light without inverse-square
|
|
75
|
+
* falloff, so a rig authored that way — a fill of brightness 160 across 164 m, a key at exponent
|
|
76
|
+
* 8 — lights the space it was authored for rather than one that ends a few metres out. Absent or
|
|
77
|
+
* 0, the frame's own falloff (`pointLightFalloff`), which is every light before 4.8.7.
|
|
78
78
|
*/
|
|
79
79
|
falloffExponent?: number;
|
|
80
80
|
}
|
|
@@ -42,6 +42,8 @@ export type OutputTransform = 'none' | 'srgb' | 'aces' | 'shoulder' | 'filmic';
|
|
|
42
42
|
export type ShadowFilterTaps = 4 | 8 | 12;
|
|
43
43
|
export type DirectionalShadowDepthLayers = 1 | 2;
|
|
44
44
|
export type WaterReflectionFilterTaps = 1 | 5 | 9;
|
|
45
|
+
/** What a draw does when its pipeline has not compiled yet: see `RenderQuality.pipelineCompile`. */
|
|
46
|
+
export type PipelineCompile = 'wait' | 'skip';
|
|
45
47
|
/** How light through glass is coloured: see `RenderQuality.glassShadows`. */
|
|
46
48
|
export type GlassShadows = 'full' | 'half' | 'off';
|
|
47
49
|
/** How light scattered beneath skin is drawn: see `RenderQuality.skinScattering`. */
|
|
@@ -210,6 +212,34 @@ export interface RenderQuality {
|
|
|
210
212
|
* lot of glass; that is what `'half'` is for. See `glassShadow.ts`.
|
|
211
213
|
*/
|
|
212
214
|
readonly glassShadows: GlassShadows;
|
|
215
|
+
/**
|
|
216
|
+
* What a lit draw does when the pipeline it needs has not compiled: one first needed inside a
|
|
217
|
+
* frame, by a material's own state — two-sided, cut out, a surface model — or a draw state a
|
|
218
|
+
* mesh's creation did not warm.
|
|
219
|
+
*
|
|
220
|
+
* **`'wait'`, the default**: the pipeline is built where the draw asks for it, and on WebGPU the
|
|
221
|
+
* browser compiles it on its GPU process's main thread, holding the frame — 130 to 400 ms a
|
|
222
|
+
* pipeline was reported on a phone, for a material first drawn mid-scene. **`'skip'`** starts the
|
|
223
|
+
* compile off that thread and leaves the draw out until it lands, a few frames to a few dozen,
|
|
224
|
+
* and the draw then appears; nothing waits. Static draws always wait, since a recorded list keeps
|
|
225
|
+
* what it recorded, and a compile that fails is built where it is next asked for, so the device
|
|
226
|
+
* says why rather than the draw vanishing for good.
|
|
227
|
+
*
|
|
228
|
+
* **The way to have neither** is to draw what a scene will use once, behind a loading screen, and
|
|
229
|
+
* await `ready()`, which waits for every compile started — whichever this says. What would make
|
|
230
|
+
* `'skip'` wrong is a frame that must hold everything it was given, such as a capture of the first
|
|
231
|
+
* frame of a scene not awaited.
|
|
232
|
+
*
|
|
233
|
+
* **Measured on a desktop GPU** (`demo/dev/pipelineCompile.html`, eight new variants at once): on
|
|
234
|
+
* WebGPU the frame that met them took 700 to 3,700 ms waiting and 17 ms skipping, the draws
|
|
235
|
+
* landing 21 to 26 frames later. WebGL2 compiles its lit programs when it is made, all but a
|
|
236
|
+
* surface model's, which it compiles at the first draw that wants one: there `'skip'` asks the
|
|
237
|
+
* driver through `KHR_parallel_shader_compile` and waits where a context does not offer it — which
|
|
238
|
+
* ANGLE on Vulkan does not. Where it is offered, ANGLE on OpenGL, the longest frame went from 820 to
|
|
239
|
+
* 1,120 ms to 630 to 700 ms: the driver still finishes its own compile at the program's first
|
|
240
|
+
* draw, which nothing above it can move.
|
|
241
|
+
*/
|
|
242
|
+
readonly pipelineCompile: PipelineCompile;
|
|
213
243
|
/**
|
|
214
244
|
* How light that scatters beneath skin is drawn, for a material whose model is `skinModel`.
|
|
215
245
|
*
|
|
@@ -63,6 +63,7 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
63
63
|
lightVolumeSamples: 32,
|
|
64
64
|
directionalShadowDepthLayers: 2,
|
|
65
65
|
glassShadows: 'full',
|
|
66
|
+
pipelineCompile: 'wait',
|
|
66
67
|
skinScattering: 'pre-integrated',
|
|
67
68
|
directionalShadowMaxDistance: 6,
|
|
68
69
|
directionalShadowMaxSlope: 3,
|
|
@@ -218,6 +219,7 @@ export function resolveRenderQuality(options = {}) {
|
|
|
218
219
|
lightVolumeSamples: options.lightVolumeSamples ?? DEFAULT_RENDER_QUALITY.lightVolumeSamples,
|
|
219
220
|
directionalShadowDepthLayers: options.directionalShadowDepthLayers ?? DEFAULT_RENDER_QUALITY.directionalShadowDepthLayers,
|
|
220
221
|
glassShadows: options.glassShadows ?? DEFAULT_RENDER_QUALITY.glassShadows,
|
|
222
|
+
pipelineCompile: options.pipelineCompile ?? DEFAULT_RENDER_QUALITY.pipelineCompile,
|
|
221
223
|
skinScattering: options.skinScattering ?? DEFAULT_RENDER_QUALITY.skinScattering,
|
|
222
224
|
directionalShadowMaxDistance: options.directionalShadowMaxDistance ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxDistance,
|
|
223
225
|
directionalShadowMaxSlope: options.directionalShadowMaxSlope ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxSlope,
|
|
@@ -318,6 +320,9 @@ export function resolveRenderQuality(options = {}) {
|
|
|
318
320
|
quality.glassShadows !== 'off') {
|
|
319
321
|
throw new Error(`RenderQuality.glassShadows must be 'full', 'half' or 'off', got ${String(quality.glassShadows)}`);
|
|
320
322
|
}
|
|
323
|
+
if (quality.pipelineCompile !== 'wait' && quality.pipelineCompile !== 'skip') {
|
|
324
|
+
throw new Error(`RenderQuality.pipelineCompile must be 'wait' or 'skip', got ${String(quality.pipelineCompile)}`);
|
|
325
|
+
}
|
|
321
326
|
if (quality.skinScattering !== 'pre-integrated' && quality.skinScattering !== 'screen-space') {
|
|
322
327
|
throw new Error(`RenderQuality.skinScattering must be 'pre-integrated' or 'screen-space', got ${String(quality.skinScattering)}`);
|
|
323
328
|
}
|
|
@@ -38,6 +38,13 @@ export declare function recordingGl(options?: {
|
|
|
38
38
|
* recording.
|
|
39
39
|
*/
|
|
40
40
|
readonly uniforms?: readonly string[];
|
|
41
|
+
/**
|
|
42
|
+
* Whether a program started under `KHR_parallel_shader_compile` has finished, which the test
|
|
43
|
+
* flips: `COMPLETION_STATUS_KHR` answers `done`. Finished by default, as every program here is.
|
|
44
|
+
*/
|
|
45
|
+
readonly compiling?: {
|
|
46
|
+
done: boolean;
|
|
47
|
+
};
|
|
41
48
|
}): {
|
|
42
49
|
gl: WebGL2RenderingContext;
|
|
43
50
|
canvas: HTMLCanvasElement;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { countUniformVectors } from './uniformVectorBudget.js';
|
|
19
19
|
export function recordingGl(options = {}) {
|
|
20
|
+
const compiling = options.compiling ?? { done: true };
|
|
20
21
|
const extensions = new Set(options.extensions ?? []);
|
|
21
22
|
const uniforms = options.uniforms ?? [];
|
|
22
23
|
/* What `clearDepth` last set, so a module that borrows the value can be seen to put it back. */
|
|
@@ -93,6 +94,13 @@ export function recordingGl(options = {}) {
|
|
|
93
94
|
HALF_FLOAT: 0x140b,
|
|
94
95
|
DEPTH_COMPONENT: 0x1902,
|
|
95
96
|
UNSIGNED_INT: 0x1405,
|
|
97
|
+
/* Named so a mesh's index width and each attribute's type can be read back: the buffer its
|
|
98
|
+
indices fill, the type every draw of it names, and how each attribute is read. See
|
|
99
|
+
`mesh.test.ts`. */
|
|
100
|
+
UNSIGNED_SHORT: 0x1403,
|
|
101
|
+
SHORT: 0x1402,
|
|
102
|
+
FLOAT: 0x1406,
|
|
103
|
+
ELEMENT_ARRAY_BUFFER: 0x8893,
|
|
96
104
|
LINEAR: 0x2601,
|
|
97
105
|
COLOR_BUFFER_BIT: 0x00004000,
|
|
98
106
|
/* Named so a test can tell a mesh that declared itself deforming from one that did not. */
|
|
@@ -137,6 +145,8 @@ export function recordingGl(options = {}) {
|
|
|
137
145
|
if (name === 'EXT_clip_control') {
|
|
138
146
|
return { LOWER_LEFT_EXT: 0x8ca1, ZERO_TO_ONE_EXT: 0x935f, clipControlEXT: () => undefined };
|
|
139
147
|
}
|
|
148
|
+
if (name === 'KHR_parallel_shader_compile')
|
|
149
|
+
return { COMPLETION_STATUS_KHR: 0x91b1 };
|
|
140
150
|
return {};
|
|
141
151
|
},
|
|
142
152
|
getShaderParameter: () => true,
|
|
@@ -166,6 +176,8 @@ export function recordingGl(options = {}) {
|
|
|
166
176
|
/* Linked yes; zero active uniforms and attributes, which ends every reflection loop at once
|
|
167
177
|
and is why this harness does not have to know a single uniform name. */
|
|
168
178
|
getProgramParameter: (program, pname) => {
|
|
179
|
+
if (pname === 0x91b1)
|
|
180
|
+
return compiling.done;
|
|
169
181
|
if (pname === constants.ACTIVE_UNIFORMS)
|
|
170
182
|
return uniforms.length;
|
|
171
183
|
if (pname === constants.ACTIVE_ATTRIBUTES)
|
|
@@ -179,7 +179,8 @@ export interface ReflectiveSurfaceOptions extends DecalBox {
|
|
|
179
179
|
* box. Each pixel then returns `F0 · A + B` of what its ray finds, the split-sum environment BRDF
|
|
180
180
|
* at its own view and `roughness` — Karis's fit, the one the lit stage reflects its environment
|
|
181
181
|
* by — so a floor returns more toward the horizon than underfoot, and the share follows the
|
|
182
|
-
* camera as
|
|
182
|
+
* camera as a physically based renderer weighs its reflections. **False by default**, the one
|
|
183
|
+
* share as before.
|
|
183
184
|
*
|
|
184
185
|
* **What it gives up**: the pass has no material buffer, so the roughness and F0 are the box's,
|
|
185
186
|
* not each pixel's, and nothing occludes the reflection where the lit stage's ambient occlusion
|
package/dist/render/shader.d.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
/** Shader compilation with fail-fast diagnostics (init-time only, never per-frame). */
|
|
2
2
|
export declare function compileProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string, label: string): WebGLProgram;
|
|
3
|
+
/**
|
|
4
|
+
* A program compiled and linked without asking whether that worked, since asking waits for the
|
|
5
|
+
* driver to finish: for a context offering `KHR_parallel_shader_compile`, whose
|
|
6
|
+
* `COMPLETION_STATUS_KHR` says when asking no longer waits. `finishProgram` asks then, and fails
|
|
7
|
+
* as loudly as `compileProgram` does; the stages are kept until it does, for their logs.
|
|
8
|
+
*/
|
|
9
|
+
export interface StartedProgram {
|
|
10
|
+
readonly program: WebGLProgram;
|
|
11
|
+
readonly vertex: WebGLShader;
|
|
12
|
+
readonly fragment: WebGLShader;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function startProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string, label: string): StartedProgram;
|
|
16
|
+
/** A started program, checked as `compileProgram` checks one: its stages, then its link. */
|
|
17
|
+
export declare function finishProgram(gl: WebGL2RenderingContext, started: StartedProgram): WebGLProgram;
|
|
3
18
|
export declare function setUniformStrictMode(on: boolean): void;
|
|
4
19
|
/** Locations of every active uniform, keyed by name. */
|
|
5
20
|
export declare function uniformLocations(gl: WebGL2RenderingContext, program: WebGLProgram, label?: string): Record<string, WebGLUniformLocation>;
|
package/dist/render/shader.js
CHANGED
|
@@ -17,6 +17,47 @@ export function compileProgram(gl, vertexSource, fragmentSource, label) {
|
|
|
17
17
|
}
|
|
18
18
|
return program;
|
|
19
19
|
}
|
|
20
|
+
export function startProgram(gl, vertexSource, fragmentSource, label) {
|
|
21
|
+
const stage = (type, source, name) => {
|
|
22
|
+
const shader = gl.createShader(type);
|
|
23
|
+
if (shader === null)
|
|
24
|
+
throw new Error(`[${label}.${name}] createShader failed`);
|
|
25
|
+
gl.shaderSource(shader, source);
|
|
26
|
+
gl.compileShader(shader);
|
|
27
|
+
return shader;
|
|
28
|
+
};
|
|
29
|
+
const vertex = stage(gl.VERTEX_SHADER, vertexSource, 'vert');
|
|
30
|
+
const fragment = stage(gl.FRAGMENT_SHADER, fragmentSource, 'frag');
|
|
31
|
+
const program = gl.createProgram();
|
|
32
|
+
if (program === null)
|
|
33
|
+
throw new Error(`[${label}] createProgram failed`);
|
|
34
|
+
gl.attachShader(program, vertex);
|
|
35
|
+
gl.attachShader(program, fragment);
|
|
36
|
+
gl.linkProgram(program);
|
|
37
|
+
return { program, vertex, fragment, label };
|
|
38
|
+
}
|
|
39
|
+
/** A started program, checked as `compileProgram` checks one: its stages, then its link. */
|
|
40
|
+
export function finishProgram(gl, started) {
|
|
41
|
+
const { program, vertex, fragment, label } = started;
|
|
42
|
+
const fail = (what, log) => {
|
|
43
|
+
gl.deleteShader(vertex);
|
|
44
|
+
gl.deleteShader(fragment);
|
|
45
|
+
gl.deleteProgram(program);
|
|
46
|
+
throw new Error(`[${label}${what}] ${log ?? 'no log'}`);
|
|
47
|
+
};
|
|
48
|
+
if (!gl.getShaderParameter(vertex, gl.COMPILE_STATUS)) {
|
|
49
|
+
fail('.vert compile failed:', gl.getShaderInfoLog(vertex));
|
|
50
|
+
}
|
|
51
|
+
if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) {
|
|
52
|
+
fail('.frag compile failed:', gl.getShaderInfoLog(fragment));
|
|
53
|
+
}
|
|
54
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
55
|
+
fail(' link failed:', gl.getProgramInfoLog(program));
|
|
56
|
+
}
|
|
57
|
+
gl.deleteShader(vertex);
|
|
58
|
+
gl.deleteShader(fragment);
|
|
59
|
+
return program;
|
|
60
|
+
}
|
|
20
61
|
/**
|
|
21
62
|
* Whether a uniform lookup that cannot reach the GPU is reported.
|
|
22
63
|
*
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vertex-stage bone animation: a crowd instance's vertex turned and placed by its bone, at its
|
|
3
|
+
* instance's moment of the clip — the GLSL of `boneAnimation.ts`'s `animateBoneVertex`, line for
|
|
4
|
+
* line. Shared by the lit stage and the depth stage, so a crowd's shadow moves with it.
|
|
5
|
+
*
|
|
6
|
+
* **Three textures, read by index and never filtered**: the places and the turns, a texel a bone a
|
|
7
|
+
* frame (bones across, frames down, sizes read with `textureSize` so no uniform says them), and the
|
|
8
|
+
* batch's clocks, two floats an instance wrapped onto rows, read at `gl_InstanceID`. The clip's speed
|
|
9
|
+
* and its bone scale are the one uniform, `uBoneClip`; the scene's time is the pass's `uSceneTime`.
|
|
10
|
+
*
|
|
11
|
+
* **The bone is read from the grain lane**, which is where a mesh's second coordinates ride, stored
|
|
12
|
+
* as `-1 - u` (`lightmap.ts`). An instanced batch reads no grain of its own from that lane: a crowd's
|
|
13
|
+
* mesh carries second coordinates, which is what makes it one.
|
|
14
|
+
*
|
|
15
|
+
* Integer arithmetic for the lattice, by the 2026-09-20 rule, except where a float is clamped onto
|
|
16
|
+
* it: the first frame is `min(floor(wrapped), frames - 1)`, so the wrap that division-by-reciprocal
|
|
17
|
+
* lands on `frames` is the last frame fully blended into the first — the first, which is right.
|
|
18
|
+
*/
|
|
19
|
+
export declare const BONE_ANIMATION_GLSL = "\nuniform highp sampler2D uBonePlaces;\nuniform highp sampler2D uBoneTurns;\nuniform highp sampler2D uInstanceClocks;\n/** x the clip's frames a second, y how many bones one unit of the second coordinates' u counts. */\nuniform vec2 uBoneClip;\n\n/** v turned by the unit quaternion q. */\nvec3 boneTurn(vec4 q, vec3 v) {\n return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);\n}\n\n/**\n * Where the clip puts this vertex, from its place at rest; its normal and tangent turned with it.\n */\nvec3 boneAnimate(vec3 position, inout vec3 normal, inout vec3 tangent) {\n ivec2 size = textureSize(uBonePlaces, 0);\n int clocksWidth = textureSize(uInstanceClocks, 0).x;\n vec2 clock = texelFetch(\n uInstanceClocks,\n ivec2(gl_InstanceID % clocksWidth, gl_InstanceID / clocksWidth),\n 0\n ).xy;\n float moment = (uSceneTime * clock.y + clock.x) * uBoneClip.x;\n float frames = float(size.y);\n float wrapped = moment - frames * floor(moment / frames);\n int first = min(int(floor(wrapped)), size.y - 1);\n int second = first + 1 == size.y ? 0 : first + 1;\n float blend = clamp(wrapped - float(first), 0.0, 1.0);\n int bone = clamp(int(floor((-1.0 - aGrain) * uBoneClip.y + 0.5)), 0, size.x - 1);\n vec4 q0 = texelFetch(uBoneTurns, ivec2(bone, first), 0);\n vec4 q1 = texelFetch(uBoneTurns, ivec2(bone, second), 0);\n /* The shorter arc: a quaternion and its negation are one turn. */\n if (dot(q0, q1) < 0.0) q1 = -q1;\n vec4 q = normalize(mix(q0, q1, blend));\n vec3 place = mix(\n texelFetch(uBonePlaces, ivec2(bone, first), 0).xyz,\n texelFetch(uBonePlaces, ivec2(bone, second), 0).xyz,\n blend\n );\n normal = boneTurn(q, normal);\n tangent = boneTurn(q, tangent);\n return boneTurn(q, position) + place;\n}\n";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vertex-stage bone animation: a crowd instance's vertex turned and placed by its bone, at its
|
|
3
|
+
* instance's moment of the clip — the GLSL of `boneAnimation.ts`'s `animateBoneVertex`, line for
|
|
4
|
+
* line. Shared by the lit stage and the depth stage, so a crowd's shadow moves with it.
|
|
5
|
+
*
|
|
6
|
+
* **Three textures, read by index and never filtered**: the places and the turns, a texel a bone a
|
|
7
|
+
* frame (bones across, frames down, sizes read with `textureSize` so no uniform says them), and the
|
|
8
|
+
* batch's clocks, two floats an instance wrapped onto rows, read at `gl_InstanceID`. The clip's speed
|
|
9
|
+
* and its bone scale are the one uniform, `uBoneClip`; the scene's time is the pass's `uSceneTime`.
|
|
10
|
+
*
|
|
11
|
+
* **The bone is read from the grain lane**, which is where a mesh's second coordinates ride, stored
|
|
12
|
+
* as `-1 - u` (`lightmap.ts`). An instanced batch reads no grain of its own from that lane: a crowd's
|
|
13
|
+
* mesh carries second coordinates, which is what makes it one.
|
|
14
|
+
*
|
|
15
|
+
* Integer arithmetic for the lattice, by the 2026-09-20 rule, except where a float is clamped onto
|
|
16
|
+
* it: the first frame is `min(floor(wrapped), frames - 1)`, so the wrap that division-by-reciprocal
|
|
17
|
+
* lands on `frames` is the last frame fully blended into the first — the first, which is right.
|
|
18
|
+
*/
|
|
19
|
+
export const BONE_ANIMATION_GLSL = `
|
|
20
|
+
uniform highp sampler2D uBonePlaces;
|
|
21
|
+
uniform highp sampler2D uBoneTurns;
|
|
22
|
+
uniform highp sampler2D uInstanceClocks;
|
|
23
|
+
/** x the clip's frames a second, y how many bones one unit of the second coordinates' u counts. */
|
|
24
|
+
uniform vec2 uBoneClip;
|
|
25
|
+
|
|
26
|
+
/** v turned by the unit quaternion q. */
|
|
27
|
+
vec3 boneTurn(vec4 q, vec3 v) {
|
|
28
|
+
return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Where the clip puts this vertex, from its place at rest; its normal and tangent turned with it.
|
|
33
|
+
*/
|
|
34
|
+
vec3 boneAnimate(vec3 position, inout vec3 normal, inout vec3 tangent) {
|
|
35
|
+
ivec2 size = textureSize(uBonePlaces, 0);
|
|
36
|
+
int clocksWidth = textureSize(uInstanceClocks, 0).x;
|
|
37
|
+
vec2 clock = texelFetch(
|
|
38
|
+
uInstanceClocks,
|
|
39
|
+
ivec2(gl_InstanceID % clocksWidth, gl_InstanceID / clocksWidth),
|
|
40
|
+
0
|
|
41
|
+
).xy;
|
|
42
|
+
float moment = (uSceneTime * clock.y + clock.x) * uBoneClip.x;
|
|
43
|
+
float frames = float(size.y);
|
|
44
|
+
float wrapped = moment - frames * floor(moment / frames);
|
|
45
|
+
int first = min(int(floor(wrapped)), size.y - 1);
|
|
46
|
+
int second = first + 1 == size.y ? 0 : first + 1;
|
|
47
|
+
float blend = clamp(wrapped - float(first), 0.0, 1.0);
|
|
48
|
+
int bone = clamp(int(floor((-1.0 - aGrain) * uBoneClip.y + 0.5)), 0, size.x - 1);
|
|
49
|
+
vec4 q0 = texelFetch(uBoneTurns, ivec2(bone, first), 0);
|
|
50
|
+
vec4 q1 = texelFetch(uBoneTurns, ivec2(bone, second), 0);
|
|
51
|
+
/* The shorter arc: a quaternion and its negation are one turn. */
|
|
52
|
+
if (dot(q0, q1) < 0.0) q1 = -q1;
|
|
53
|
+
vec4 q = normalize(mix(q0, q1, blend));
|
|
54
|
+
vec3 place = mix(
|
|
55
|
+
texelFetch(uBonePlaces, ivec2(bone, first), 0).xyz,
|
|
56
|
+
texelFetch(uBonePlaces, ivec2(bone, second), 0).xyz,
|
|
57
|
+
blend
|
|
58
|
+
);
|
|
59
|
+
normal = boneTurn(q, normal);
|
|
60
|
+
tangent = boneTurn(q, tangent);
|
|
61
|
+
return boneTurn(q, position) + place;
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
@@ -23,6 +23,13 @@ export declare const DEPTH_INSTANCED_VERT: string;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const DEPTH_CUTOUT_VERT: string;
|
|
25
25
|
export declare const DEPTH_INSTANCED_CUTOUT_VERT: string;
|
|
26
|
+
/**
|
|
27
|
+
* The variants a crowd casts through: an instanced batch playing a bone animation, its vertices
|
|
28
|
+
* where the clip puts them at each instance's moment. See `shaders/boneAnimation.ts`. No glass
|
|
29
|
+
* variant: a crowd is not glass, and a glass batch that animates casts its tint from rest.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEPTH_INSTANCED_ANIMATED_VERT: string;
|
|
32
|
+
export declare const DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT: string;
|
|
26
33
|
export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\n\nuniform highp sampler2DArray uPreviousShadowMap;\nuniform int uPeelShadowLayer;\n\nvoid main() {\n if (uPeelShadowLayer != 0) {\n vec3 p = vLightPosition.xyz / vLightPosition.w;\n vec2 uv = p.xy * 0.5 + 0.5;\n float previousDepth = texture(uPreviousShadowMap, vec3(uv, 0.0)).r;\n // Discard the already-recorded surface and everything in front of it. The\n // ordinary depth test then stores the next independently fading occluder.\n if (gl_FragCoord.z <= previousDepth + 0.00001) discard;\n }\n}\n";
|
|
27
34
|
/**
|
|
28
35
|
* The cutout caster's depth: the plain pass, and a texel of alpha under the cutoff writes nothing.
|