@driftengine/core 4.9.0 → 4.10.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 +62 -11
- package/README.md +43 -38
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -3
- 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 +90 -2
- package/dist/render/backend/webgl2/renderer.js +253 -24
- 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 +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- 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 +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +136 -20
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- package/dist/render/backend/webgpu/renderer.d.ts +152 -35
- package/dist/render/backend/webgpu/renderer.js +787 -142
- 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 +241 -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/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/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- 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/index.d.ts +6 -0
- package/dist/render/shaders/flat/index.js +27 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +15 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/overlay.js +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +38 -24
- 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 +3077 -2903
- package/dist/render/shaders/generated/flat.wgsl.js +3100 -2925
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceTexture.d.ts +38 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/package.json +6 -6
- package/src/index.ts +8 -2
- 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 +315 -24
- 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 +42 -16
- 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 +22 -4
- package/src/render/backend/webgpu/flatPass.ts +187 -14
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- package/src/render/backend/webgpu/renderer.ts +928 -155
- 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 +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- 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/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/index.ts +33 -5
- package/src/render/shaders/flat/main.ts +15 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +1 -1
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +38 -24
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3100 -2925
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceTexture.ts +94 -27
|
@@ -48,3 +48,10 @@ export declare const HIGHLIGHT_SHOULDER_GLSL = "\nvec3 highlightShoulder(vec3 c)
|
|
|
48
48
|
* a branch that costs one compare.
|
|
49
49
|
*/
|
|
50
50
|
export declare const OUTPUT_TRANSFORM_GLSL = "\n/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */\nuniform int uOutputTransform;\n/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */\nuniform float uOutputExposure;\n\n/*\n * ACES: the reference fit, not the cheap one.\n *\n * This was Narkowicz's single rational function, on the argument that the difference from\n * the reference curve is invisible next to the gain. That argument holds for a look and\n * fails for a *match*, which is what this option is for \u2014 aces exists so a world ported\n * from three.js reads the same, and three.js applies the full fit: into AP1 through\n * ACESInputMat, the RRT and ODT together, then back out through ACESOutputMat.\n *\n * The two curves are not a shade apart. Narkowicz is markedly brighter through the\n * midtones, which is exactly where an interior scene lives, and it is a large part of a\n * measured ~1.7x that survived correcting the colour space and the missing Lambert pi.\n * An approximation of a standard is the wrong thing to hold when the standard is the\n * specification.\n */\nvec3 rrtAndOdtFit(vec3 v) {\n vec3 a = v * (v + 0.0245786) - 0.000090537;\n vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081;\n return a / b;\n}\n\nvec3 acesFilmic(vec3 x) {\n /*\n * Exposure first, because a tone curve is only defined relative to one.\n *\n * The reference RRT and ODT are graded for a dark cinema. Every renderer that ships ACES\n * scales its input to suit whatever it is actually shown on, and the factor each one\n * picks is a *look*, not a standard - so this engine takes no position and reads it from\n * the caller. Neutral by default, which is the reference grade unmodified. three.js's own\n * pick is 1 / 0.6, for a consumer matching one.\n */\n x *= uOutputExposure;\n // Columns, as GLSL reads them: sRGB -> XYZ -> D65_2_D60 -> AP1 -> RRT_SAT.\n const mat3 ACES_INPUT = mat3(\n 0.59719, 0.07600, 0.02840,\n 0.35458, 0.90834, 0.13383,\n 0.04823, 0.01566, 0.83777\n );\n // ODT_SAT -> XYZ -> D60_2_D65 -> sRGB.\n const mat3 ACES_OUTPUT = mat3(\n 1.60475, -0.10208, -0.00327,\n -0.53108, 1.10813, -0.07276,\n -0.07367, -0.00605, 1.07602\n );\n return clamp(ACES_OUTPUT * rrtAndOdtFit(ACES_INPUT * x), 0.0, 1.0);\n}\n\n/*\n * Linear to sRGB. The piecewise form rather than a plain pow: the linear toe near black\n * is where banding lives in an 8-bit buffer, and the cheap approximation puts it in the\n * wrong place.\n */\nvec3 linearToSrgb(vec3 c) {\n vec3 low = c * 12.92;\n vec3 high = 1.055 * pow(max(c, vec3(0.0)), vec3(1.0 / 2.4)) - 0.055;\n return mix(high, low, step(c, vec3(0.0031308)));\n}\n\n\nvec3 highlightShoulder(vec3 c) {\n float m = max(c.r, max(c.g, c.b));\n if (m <= 0.8) return c;\n float e = m - 0.8;\n return c * ((0.8 + 0.2 * e / (e + 0.2)) / m);\n}\n\nvec3 applyOutputTransform(vec3 c) {\n if (uOutputTransform == 0) return c;\n if (uOutputTransform == 2) c = acesFilmic(c);\n if (uOutputTransform == 3) c = highlightShoulder(c * uOutputExposure);\n return linearToSrgb(c);\n}\n";
|
|
51
|
+
/**
|
|
52
|
+
* The same, for the lit stage, whose two uniforms live in its material block: a draw that opts out
|
|
53
|
+
* of the grade sets them, so they change with the material and not with the pass. See
|
|
54
|
+
* `hoistUniformBlock` in `scripts/wgsl/transform.mjs`. Text otherwise identical, so WebGL2 runs
|
|
55
|
+
* the same program it always has.
|
|
56
|
+
*/
|
|
57
|
+
export declare const LIT_OUTPUT_TRANSFORM_GLSL = "\n/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */\nuniform int uOutputTransform; // wgsl:material\n/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */\nuniform float uOutputExposure; // wgsl:material\n\n/*\n * ACES: the reference fit, not the cheap one.\n *\n * This was Narkowicz's single rational function, on the argument that the difference from\n * the reference curve is invisible next to the gain. That argument holds for a look and\n * fails for a *match*, which is what this option is for \u2014 aces exists so a world ported\n * from three.js reads the same, and three.js applies the full fit: into AP1 through\n * ACESInputMat, the RRT and ODT together, then back out through ACESOutputMat.\n *\n * The two curves are not a shade apart. Narkowicz is markedly brighter through the\n * midtones, which is exactly where an interior scene lives, and it is a large part of a\n * measured ~1.7x that survived correcting the colour space and the missing Lambert pi.\n * An approximation of a standard is the wrong thing to hold when the standard is the\n * specification.\n */\nvec3 rrtAndOdtFit(vec3 v) {\n vec3 a = v * (v + 0.0245786) - 0.000090537;\n vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081;\n return a / b;\n}\n\nvec3 acesFilmic(vec3 x) {\n /*\n * Exposure first, because a tone curve is only defined relative to one.\n *\n * The reference RRT and ODT are graded for a dark cinema. Every renderer that ships ACES\n * scales its input to suit whatever it is actually shown on, and the factor each one\n * picks is a *look*, not a standard - so this engine takes no position and reads it from\n * the caller. Neutral by default, which is the reference grade unmodified. three.js's own\n * pick is 1 / 0.6, for a consumer matching one.\n */\n x *= uOutputExposure;\n // Columns, as GLSL reads them: sRGB -> XYZ -> D65_2_D60 -> AP1 -> RRT_SAT.\n const mat3 ACES_INPUT = mat3(\n 0.59719, 0.07600, 0.02840,\n 0.35458, 0.90834, 0.13383,\n 0.04823, 0.01566, 0.83777\n );\n // ODT_SAT -> XYZ -> D60_2_D65 -> sRGB.\n const mat3 ACES_OUTPUT = mat3(\n 1.60475, -0.10208, -0.00327,\n -0.53108, 1.10813, -0.07276,\n -0.07367, -0.00605, 1.07602\n );\n return clamp(ACES_OUTPUT * rrtAndOdtFit(ACES_INPUT * x), 0.0, 1.0);\n}\n\n/*\n * Linear to sRGB. The piecewise form rather than a plain pow: the linear toe near black\n * is where banding lives in an 8-bit buffer, and the cheap approximation puts it in the\n * wrong place.\n */\nvec3 linearToSrgb(vec3 c) {\n vec3 low = c * 12.92;\n vec3 high = 1.055 * pow(max(c, vec3(0.0)), vec3(1.0 / 2.4)) - 0.055;\n return mix(high, low, step(c, vec3(0.0031308)));\n}\n\n\nvec3 highlightShoulder(vec3 c) {\n float m = max(c.r, max(c.g, c.b));\n if (m <= 0.8) return c;\n float e = m - 0.8;\n return c * ((0.8 + 0.2 * e / (e + 0.2)) / m);\n}\n\nvec3 applyOutputTransform(vec3 c) {\n if (uOutputTransform == 0) return c;\n if (uOutputTransform == 2) c = acesFilmic(c);\n if (uOutputTransform == 3) c = highlightShoulder(c * uOutputExposure);\n return linearToSrgb(c);\n}\n";
|
|
@@ -46,20 +46,8 @@ vec3 highlightShoulder(vec3 c) {
|
|
|
46
46
|
return c * ((0.8 + 0.2 * e / (e + 0.2)) / m);
|
|
47
47
|
}
|
|
48
48
|
`;
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
*
|
|
52
|
-
* Declares its own two uniforms, so including it is the whole of what a fragment stage has to
|
|
53
|
-
* do besides calling it. Both are ints and floats rather than defines because the choice is a
|
|
54
|
-
* caller's, per draw, and a permutation per output transform would double a pipeline count for
|
|
55
|
-
* a branch that costs one compare.
|
|
56
|
-
*/
|
|
57
|
-
export const OUTPUT_TRANSFORM_GLSL = `
|
|
58
|
-
/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */
|
|
59
|
-
uniform int uOutputTransform;
|
|
60
|
-
/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */
|
|
61
|
-
uniform float uOutputExposure;
|
|
62
|
-
|
|
49
|
+
/** The fit and `applyOutputTransform`, below whichever declaration of the two uniforms. */
|
|
50
|
+
const OUTPUT_TRANSFORM_BODY_GLSL = `
|
|
63
51
|
/*
|
|
64
52
|
* ACES: the reference fit, not the cheap one.
|
|
65
53
|
*
|
|
@@ -126,3 +114,29 @@ vec3 applyOutputTransform(vec3 c) {
|
|
|
126
114
|
return linearToSrgb(c);
|
|
127
115
|
}
|
|
128
116
|
`;
|
|
117
|
+
/**
|
|
118
|
+
* `uOutputTransform` and `uOutputExposure`, the fit, and `applyOutputTransform`.
|
|
119
|
+
*
|
|
120
|
+
* Declares its own two uniforms, so including it is the whole of what a fragment stage has to
|
|
121
|
+
* do besides calling it. Both are ints and floats rather than defines because the choice is a
|
|
122
|
+
* caller's, per draw, and a permutation per output transform would double a pipeline count for
|
|
123
|
+
* a branch that costs one compare.
|
|
124
|
+
*/
|
|
125
|
+
export const OUTPUT_TRANSFORM_GLSL = `
|
|
126
|
+
/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */
|
|
127
|
+
uniform int uOutputTransform;
|
|
128
|
+
/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */
|
|
129
|
+
uniform float uOutputExposure;
|
|
130
|
+
${OUTPUT_TRANSFORM_BODY_GLSL}`;
|
|
131
|
+
/**
|
|
132
|
+
* The same, for the lit stage, whose two uniforms live in its material block: a draw that opts out
|
|
133
|
+
* of the grade sets them, so they change with the material and not with the pass. See
|
|
134
|
+
* `hoistUniformBlock` in `scripts/wgsl/transform.mjs`. Text otherwise identical, so WebGL2 runs
|
|
135
|
+
* the same program it always has.
|
|
136
|
+
*/
|
|
137
|
+
export const LIT_OUTPUT_TRANSFORM_GLSL = `
|
|
138
|
+
/** 0 none, 1 sRGB, 2 ACES then sRGB, 3 the highlight shoulder then sRGB. See renderQuality.ts. */
|
|
139
|
+
uniform int uOutputTransform; // wgsl:material
|
|
140
|
+
/** Scales the scene into the tone curve. 1 is the reference grade. See renderQuality.ts. */
|
|
141
|
+
uniform float uOutputExposure; // wgsl:material
|
|
142
|
+
${OUTPUT_TRANSFORM_BODY_GLSL}`;
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* 4.1 KB across five, so a `#if` here would buy nothing measurable and cost a variant axis.
|
|
19
19
|
*/
|
|
20
20
|
export declare const CHANNEL_ATTRIBUTE = "\nlayout(location = 13) in vec4 aChannel;\n\n/*\n * The frame's wind, already converted by `resolveScatterDeform` \u2014 a normalised direction, metres\n * of bend, a gust amplitude and a scaled clock.\n *\n * **The same numbers the scatter batch reads, and that is the rule rather than an economy.** A\n * second conversion here would be a second wind however identical its inputs, and the failure is\n * not localised: it shows up as a scene that does not cohere, a canopy leaning one way while the\n * grass beneath it leans another, with no single element looking wrong. See \"One wind, sampled\n * once\" in AGENTS.md.\n */\nuniform vec2 uWindDirection;\nuniform float uWindSpeed;\nuniform float uWindGust;\nuniform float uWindTime;\n/* Spatial frequency of the travelling gust, per metre. */\nuniform vec2 uWindSpatialPhase;\n";
|
|
21
|
+
/**
|
|
22
|
+
* The same, for the lit stage, whose wind lives in its view block: the frame's and not the draw's,
|
|
23
|
+
* so a draw's own block does not change when the wind does. See `hoistUniformBlock` in
|
|
24
|
+
* `scripts/wgsl/transform.mjs`. Text otherwise identical, so WebGL2 runs the same program it always
|
|
25
|
+
* has; the depth stage keeps the unmarked chunk, having no view block.
|
|
26
|
+
*/
|
|
27
|
+
export declare const LIT_CHANNEL_ATTRIBUTE = "\nlayout(location = 13) in vec4 aChannel;\n\n/*\n * The frame's wind, already converted by `resolveScatterDeform` \u2014 a normalised direction, metres\n * of bend, a gust amplitude and a scaled clock.\n *\n * **The same numbers the scatter batch reads, and that is the rule rather than an economy.** A\n * second conversion here would be a second wind however identical its inputs, and the failure is\n * not localised: it shows up as a scene that does not cohere, a canopy leaning one way while the\n * grass beneath it leans another, with no single element looking wrong. See \"One wind, sampled\n * once\" in AGENTS.md.\n */\nuniform vec2 uWindDirection; // wgsl:view\nuniform float uWindSpeed; // wgsl:view\nuniform float uWindGust; // wgsl:view\nuniform float uWindTime; // wgsl:view\n/* Spatial frequency of the travelling gust, per metre. */\nuniform vec2 uWindSpatialPhase; // wgsl:view\n";
|
|
21
28
|
/**
|
|
22
29
|
* Where the wind puts a vertex, given how much of it that vertex takes.
|
|
23
30
|
*
|
|
@@ -37,6 +37,32 @@ uniform float uWindTime;
|
|
|
37
37
|
/* Spatial frequency of the travelling gust, per metre. */
|
|
38
38
|
uniform vec2 uWindSpatialPhase;
|
|
39
39
|
`;
|
|
40
|
+
/**
|
|
41
|
+
* The same, for the lit stage, whose wind lives in its view block: the frame's and not the draw's,
|
|
42
|
+
* so a draw's own block does not change when the wind does. See `hoistUniformBlock` in
|
|
43
|
+
* `scripts/wgsl/transform.mjs`. Text otherwise identical, so WebGL2 runs the same program it always
|
|
44
|
+
* has; the depth stage keeps the unmarked chunk, having no view block.
|
|
45
|
+
*/
|
|
46
|
+
export const LIT_CHANNEL_ATTRIBUTE = `
|
|
47
|
+
layout(location = 13) in vec4 aChannel;
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
* The frame's wind, already converted by \`resolveScatterDeform\` — a normalised direction, metres
|
|
51
|
+
* of bend, a gust amplitude and a scaled clock.
|
|
52
|
+
*
|
|
53
|
+
* **The same numbers the scatter batch reads, and that is the rule rather than an economy.** A
|
|
54
|
+
* second conversion here would be a second wind however identical its inputs, and the failure is
|
|
55
|
+
* not localised: it shows up as a scene that does not cohere, a canopy leaning one way while the
|
|
56
|
+
* grass beneath it leans another, with no single element looking wrong. See "One wind, sampled
|
|
57
|
+
* once" in AGENTS.md.
|
|
58
|
+
*/
|
|
59
|
+
uniform vec2 uWindDirection; // wgsl:view
|
|
60
|
+
uniform float uWindSpeed; // wgsl:view
|
|
61
|
+
uniform float uWindGust; // wgsl:view
|
|
62
|
+
uniform float uWindTime; // wgsl:view
|
|
63
|
+
/* Spatial frequency of the travelling gust, per metre. */
|
|
64
|
+
uniform vec2 uWindSpatialPhase; // wgsl:view
|
|
65
|
+
`;
|
|
40
66
|
/**
|
|
41
67
|
* Where the wind puts a vertex, given how much of it that vertex takes.
|
|
42
68
|
*
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws that do not change between frames, captured once from an enumeration and replayed into any
|
|
3
|
+
* colour pass: what `createStaticDraws` keeps, and the one rule both backends replay it by.
|
|
4
|
+
*
|
|
5
|
+
* **Captured, not referenced.** An enumeration is a closure over the caller's state, written to
|
|
6
|
+
* allocate nothing, so it hands a sink its scratch: the matrix it fills for every entry and the
|
|
7
|
+
* count it rewrites for the next batch. A list kept across frames copies both when it is made, so
|
|
8
|
+
* what it draws is what the enumeration said then. Meshes, batches and materials are held by
|
|
9
|
+
* reference: they are the caller's, and disposing one a list still holds is the caller's error.
|
|
10
|
+
*
|
|
11
|
+
* **Rigid meshes and instanced batches only.** A skinned mesh is posed every frame and a scatter
|
|
12
|
+
* batch bends with every frame's wind, so neither can be kept, and both are refused by name rather
|
|
13
|
+
* than captured in the pose they happened to have. A caller draws them each frame as it always has.
|
|
14
|
+
*
|
|
15
|
+
* What a backend does with the list is its own: WebGL2 replays the entries every frame through
|
|
16
|
+
* the scene-caster path, and WebGPU records them once into a render bundle per view. See
|
|
17
|
+
* `RendererApi.createStaticDraws` for what a caller is promised, which is the same on both.
|
|
18
|
+
*/
|
|
19
|
+
import type { InstancedHandle, MeshHandle } from './backend/api.ts';
|
|
20
|
+
import type { MeshInstances } from './instances.ts';
|
|
21
|
+
import type { SceneCasterMaterial, ShadowCasterSink, ShadowCasters } from './shadowCasters.ts';
|
|
22
|
+
/** A list from `createStaticDraws`. Opaque but for its size. */
|
|
23
|
+
export interface StaticDrawsHandle {
|
|
24
|
+
/** How many entries it holds: meshes and batches, a batch counting once. */
|
|
25
|
+
readonly draws: number;
|
|
26
|
+
}
|
|
27
|
+
/** One captured entry. */
|
|
28
|
+
export type StaticEntry = {
|
|
29
|
+
readonly kind: 'mesh';
|
|
30
|
+
readonly mesh: MeshHandle;
|
|
31
|
+
readonly model: Float32Array;
|
|
32
|
+
readonly material: SceneCasterMaterial;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'instanced';
|
|
35
|
+
readonly batch: InstancedHandle;
|
|
36
|
+
readonly data: MeshInstances;
|
|
37
|
+
readonly material: SceneCasterMaterial;
|
|
38
|
+
};
|
|
39
|
+
/** What both backends hold: the entries, and whether the list has been disposed. */
|
|
40
|
+
export interface StaticDrawsList extends StaticDrawsHandle {
|
|
41
|
+
readonly entries: readonly StaticEntry[];
|
|
42
|
+
disposed: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Enumerate `casters` once into a list. Allocates: it runs when the list is made, not per frame. */
|
|
45
|
+
export declare function captureStaticDraws(casters: ShadowCasters): StaticDrawsList;
|
|
46
|
+
/** Hand every entry to `sink`, in the order it was captured. */
|
|
47
|
+
export declare function replayStaticDraws(list: StaticDrawsList, sink: ShadowCasterSink): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Enumerate `casters` once into a list. Allocates: it runs when the list is made, not per frame. */
|
|
2
|
+
export function captureStaticDraws(casters) {
|
|
3
|
+
const entries = [];
|
|
4
|
+
const sink = {
|
|
5
|
+
mesh: (mesh, model, material) => {
|
|
6
|
+
entries.push({
|
|
7
|
+
kind: 'mesh',
|
|
8
|
+
mesh,
|
|
9
|
+
model: new Float32Array(model),
|
|
10
|
+
material: material ?? null,
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
instanced: (batch, data, material) => {
|
|
14
|
+
/* The batch's placements are on the device already; the count is the caller's scratch. */
|
|
15
|
+
entries.push({ kind: 'instanced', batch, data: { ...data }, material: material ?? null });
|
|
16
|
+
},
|
|
17
|
+
skinnedMesh: () => {
|
|
18
|
+
throw new Error('createStaticDraws: a skinned mesh is posed every frame, so a list kept across frames ' +
|
|
19
|
+
'cannot hold one. Draw it each frame, with drawMesh or drawSceneCasters.');
|
|
20
|
+
},
|
|
21
|
+
scatter: () => {
|
|
22
|
+
throw new Error('createStaticDraws: a scatter batch bends with every frame’s wind, so a list kept across ' +
|
|
23
|
+
'frames cannot hold one. Draw it each frame with drawScatter.');
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
casters(sink);
|
|
27
|
+
return {
|
|
28
|
+
entries,
|
|
29
|
+
get draws() {
|
|
30
|
+
return entries.length;
|
|
31
|
+
},
|
|
32
|
+
disposed: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Hand every entry to `sink`, in the order it was captured. */
|
|
36
|
+
export function replayStaticDraws(list, sink) {
|
|
37
|
+
for (const entry of list.entries) {
|
|
38
|
+
if (entry.kind === 'mesh')
|
|
39
|
+
sink.mesh(entry.mesh, entry.model, entry.material);
|
|
40
|
+
else
|
|
41
|
+
sink.instanced?.(entry.batch, entry.data, entry.material);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* so a merged block wearing forty facades is one draw.
|
|
10
10
|
*/
|
|
11
11
|
import type { SurfaceModel } from './surfaceModel.ts';
|
|
12
|
-
import type { CompressedTextureFormat, SurfaceSource } from './compressedSource.ts';
|
|
12
|
+
import type { CompressedTextureFormat, CompressedTextureSource, SurfaceSource } from './compressedSource.ts';
|
|
13
13
|
import type { SurfaceLayerEffect } from './surfaceEffects.ts';
|
|
14
14
|
import type { CutoutMode } from './cutoutDither.ts';
|
|
15
15
|
import type { LightmapTexels } from './lightmap.ts';
|
|
@@ -107,7 +107,7 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
107
107
|
* which is every light's default too, so a scene that names no channel is lit as it always was.
|
|
108
108
|
*
|
|
109
109
|
* A point or spot light shades this surface only where its `PointLightSet.lightChannels` and this
|
|
110
|
-
* share a bit —
|
|
110
|
+
* share a bit — lighting channels. A character's own key and rim lights on channel 2,
|
|
111
111
|
* and the character's materials on 3, light the character and leave the floor around it as the
|
|
112
112
|
* stage's lights alone have it. **What it does not reach**: the sun, the sky and the probes, area
|
|
113
113
|
* lights, DriftLight's volume and the GPU-driven pipeline light every surface whatever its mask.
|
|
@@ -141,6 +141,22 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
141
141
|
* which is the opposite of what the caller asked for.
|
|
142
142
|
*/
|
|
143
143
|
occlusionStrength?: number;
|
|
144
|
+
/**
|
|
145
|
+
* How much of the environment this material's draws mirror, 0 to 1: the number
|
|
146
|
+
* `setSurfaceReflectivity` sets for every draw, stated by the material for its own. Absent, its
|
|
147
|
+
* draws wear the setter's, as every material's did before this existed.
|
|
148
|
+
*
|
|
149
|
+
* **Here so a static list can carry it.** A list keeps each entry's material and none of the
|
|
150
|
+
* renderer's state between draws (`createStaticDraws`), so a stage whose batches each mirror their
|
|
151
|
+
* own share of a baked reflection says so in the material it records. What it gives up is nothing a
|
|
152
|
+
* setter could do: one called after this material is set is ignored for the material's draws.
|
|
153
|
+
*/
|
|
154
|
+
reflectivity?: number;
|
|
155
|
+
/**
|
|
156
|
+
* How bright the environment this material's draws reflect is: `setEnvironmentGain`'s number,
|
|
157
|
+
* stated by the material for its own, and absent the setter's. Held non-negative.
|
|
158
|
+
*/
|
|
159
|
+
environmentGain?: number;
|
|
144
160
|
/**
|
|
145
161
|
* Scales the emissive map, per channel. Default 1.
|
|
146
162
|
*
|
|
@@ -164,14 +180,14 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
164
180
|
modelMap?: Texture | null;
|
|
165
181
|
/**
|
|
166
182
|
* Whether a lamp's and the sun's highlight on this surface is GGX's own, `π · D · Vis · F · N·L`,
|
|
167
|
-
* as
|
|
168
|
-
* peak of one. **False by default**, every surface as it was.
|
|
183
|
+
* as physically based renderers and this engine's skin and eye shade it, rather than the engine's
|
|
184
|
+
* lobe scaled to a peak of one. **False by default**, every surface as it was.
|
|
169
185
|
*
|
|
170
186
|
* The peak-normalised lobe is a look control: the specular attribute says how strong a highlight
|
|
171
187
|
* is and roughness how wide, apart. Physical, they are entangled as they are in a real surface —
|
|
172
188
|
* the peak is `1 / (4 α²)` of the light head-on, so a polished surface's highlight is many times
|
|
173
189
|
* the look's and a rough one's lower and broader — and the specular attribute is read as the
|
|
174
|
-
* reflectance at normal incidence, F0:
|
|
190
|
+
* reflectance at normal incidence, F0: `0.08 × Specular`, 0.04 at its default. Fresnel
|
|
175
191
|
* then brightens every surface toward grazing, not only a metal. A rectangle's highlight is the
|
|
176
192
|
* lobe integrated over it with the attribute as F0 already, so it is unchanged either way.
|
|
177
193
|
*
|
|
@@ -275,7 +291,9 @@ export declare class SurfaceTexture {
|
|
|
275
291
|
/** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
|
|
276
292
|
private effectsTable;
|
|
277
293
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
278
|
-
private
|
|
294
|
+
private blockFormat;
|
|
295
|
+
/** How the texture is sampled, kept so blocks swapped in by `update` are sampled the same way. */
|
|
296
|
+
private readonly sampling;
|
|
279
297
|
/** Level 0's size, which `flatLayer` copies; kept as `update` changes it. */
|
|
280
298
|
private width;
|
|
281
299
|
private height;
|
|
@@ -303,7 +321,20 @@ export declare class SurfaceTexture {
|
|
|
303
321
|
* Not a hot path: it re-uploads the whole image and regenerates the chain. A caller
|
|
304
322
|
* doing this every frame at any size wants to know that it costs what it costs.
|
|
305
323
|
*/
|
|
306
|
-
update(gl: WebGL2RenderingContext, source: TexImageSource): void;
|
|
324
|
+
update(gl: WebGL2RenderingContext, source: TexImageSource | CompressedTextureSource, compressed?: readonly CompressedTextureFormat[]): void;
|
|
325
|
+
/**
|
|
326
|
+
* Blocks in place of the image, behind the same handle: **a new GL object** rather than the
|
|
327
|
+
* image's levels respecified, because a chain shorter than the image's would leave the levels
|
|
328
|
+
* past it allocated, and immutable storage could not be respecified at all. Sampled as the image
|
|
329
|
+
* was, and refused before anything changes where the context does not take the format.
|
|
330
|
+
*/
|
|
331
|
+
private swapToBlocks;
|
|
332
|
+
/**
|
|
333
|
+
* Wrap, filters and anisotropy on the bound texture. Nearest where the caller said their pixels
|
|
334
|
+
* are the subject; see `filter`. The mip chain is still blended between levels, because that is
|
|
335
|
+
* minification and this option is about magnification.
|
|
336
|
+
*/
|
|
337
|
+
private applySampling;
|
|
307
338
|
/**
|
|
308
339
|
* Attach the only layer as the bound framebuffer's colour: a scene capture's target, drawn into
|
|
309
340
|
* by `captureScene`. Engine-internal, as `bind` is.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { layerSize, refuseArrayUpdate, sourceSize } from './textureSource.js';
|
|
2
|
-
import { compressedLayers, planBlocks, refuseBlockUpdate } from './compressedSource.js';
|
|
2
|
+
import { compressedLayers, isCompressedSource, planBlocks, refuseBlockUpdate, } from './compressedSource.js';
|
|
3
3
|
import { uploadCompressedArray } from './glCompressed.js';
|
|
4
4
|
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.js';
|
|
5
5
|
import { isLightmapTexels } from './lightmap.js';
|
|
@@ -25,6 +25,8 @@ export class SurfaceTexture {
|
|
|
25
25
|
effectsTable = null;
|
|
26
26
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
27
27
|
blockFormat;
|
|
28
|
+
/** How the texture is sampled, kept so blocks swapped in by `update` are sampled the same way. */
|
|
29
|
+
sampling;
|
|
28
30
|
/** Level 0's size, which `flatLayer` copies; kept as `update` changes it. */
|
|
29
31
|
width = 0;
|
|
30
32
|
height = 0;
|
|
@@ -101,23 +103,14 @@ export class SurfaceTexture {
|
|
|
101
103
|
this.storage = this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA8;
|
|
102
104
|
uploadImages(gl, sources, this.srgb);
|
|
103
105
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
|
|
111
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, this.mipmapped
|
|
112
|
-
? nearest
|
|
113
|
-
? gl.NEAREST_MIPMAP_LINEAR
|
|
114
|
-
: gl.LINEAR_MIPMAP_LINEAR
|
|
115
|
-
: nearest
|
|
116
|
-
? gl.NEAREST
|
|
117
|
-
: gl.LINEAR);
|
|
106
|
+
this.sampling = {
|
|
107
|
+
wrap,
|
|
108
|
+
nearest: options.filter === 'nearest',
|
|
109
|
+
anisotropy: options.anisotropy ?? DEFAULT_ANISOTROPY,
|
|
110
|
+
};
|
|
111
|
+
this.applySampling(gl);
|
|
118
112
|
if (this.mipmapped && this.blockFormat === null)
|
|
119
113
|
gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
120
|
-
applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
|
|
121
114
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
122
115
|
/* Half floats, nearest, clamped: a table read by texelFetch, whose every whole number is under
|
|
123
116
|
what a half holds exactly. */
|
|
@@ -155,10 +148,14 @@ export class SurfaceTexture {
|
|
|
155
148
|
* Not a hot path: it re-uploads the whole image and regenerates the chain. A caller
|
|
156
149
|
* doing this every frame at any size wants to know that it costs what it costs.
|
|
157
150
|
*/
|
|
158
|
-
update(gl, source) {
|
|
151
|
+
update(gl, source, compressed = []) {
|
|
159
152
|
if (this.texture === null)
|
|
160
153
|
return;
|
|
161
154
|
refuseArrayUpdate(this.layers);
|
|
155
|
+
if (isCompressedSource(source)) {
|
|
156
|
+
this.swapToBlocks(gl, source, compressed);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
162
159
|
refuseBlockUpdate(this.blockFormat);
|
|
163
160
|
const { width, height } = sourceSize(source);
|
|
164
161
|
this.width = width;
|
|
@@ -171,6 +168,48 @@ export class SurfaceTexture {
|
|
|
171
168
|
gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
172
169
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
173
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Blocks in place of the image, behind the same handle: **a new GL object** rather than the
|
|
173
|
+
* image's levels respecified, because a chain shorter than the image's would leave the levels
|
|
174
|
+
* past it allocated, and immutable storage could not be respecified at all. Sampled as the image
|
|
175
|
+
* was, and refused before anything changes where the context does not take the format.
|
|
176
|
+
*/
|
|
177
|
+
swapToBlocks(gl, source, compressed) {
|
|
178
|
+
const plan = planBlocks([source], this.srgb, compressed);
|
|
179
|
+
const texture = gl.createTexture();
|
|
180
|
+
if (texture === null)
|
|
181
|
+
throw new Error('SurfaceTexture: createTexture failed');
|
|
182
|
+
this.dropFlat(gl);
|
|
183
|
+
if (this.texture !== null)
|
|
184
|
+
gl.deleteTexture(this.texture);
|
|
185
|
+
this.texture = texture;
|
|
186
|
+
this.blockFormat = source.format;
|
|
187
|
+
this.width = source.width;
|
|
188
|
+
this.height = source.height;
|
|
189
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
190
|
+
uploadCompressedArray(gl, plan.name, [source], this.mipmapped ? plan.levels : 1);
|
|
191
|
+
this.applySampling(gl);
|
|
192
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Wrap, filters and anisotropy on the bound texture. Nearest where the caller said their pixels
|
|
196
|
+
* are the subject; see `filter`. The mip chain is still blended between levels, because that is
|
|
197
|
+
* minification and this option is about magnification.
|
|
198
|
+
*/
|
|
199
|
+
applySampling(gl) {
|
|
200
|
+
const { wrap, nearest, anisotropy } = this.sampling;
|
|
201
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
|
|
202
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
|
|
203
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
|
|
204
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, this.mipmapped
|
|
205
|
+
? nearest
|
|
206
|
+
? gl.NEAREST_MIPMAP_LINEAR
|
|
207
|
+
: gl.LINEAR_MIPMAP_LINEAR
|
|
208
|
+
: nearest
|
|
209
|
+
? gl.NEAREST
|
|
210
|
+
: gl.LINEAR);
|
|
211
|
+
applyAnisotropy(gl, anisotropy);
|
|
212
|
+
}
|
|
174
213
|
/**
|
|
175
214
|
* Attach the only layer as the bound framebuffer's colour: a scene capture's target, drawn into
|
|
176
215
|
* by `captureScene`. Engine-internal, as `bind` is.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "The runtime of DriftEngine, a WebGPU game engine with a WebGL2 fallback: renderer, loop, input, cameras, scene graph and physics",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.
|
|
33
|
-
"@driftengine/physics": "4.
|
|
32
|
+
"@driftengine/drft": "4.10.0",
|
|
33
|
+
"@driftengine/physics": "4.10.0",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@driftengine/nav": "4.
|
|
71
|
-
"@driftengine/network": "4.
|
|
72
|
-
"@driftengine/texture": "4.
|
|
70
|
+
"@driftengine/nav": "4.10.0",
|
|
71
|
+
"@driftengine/network": "4.10.0",
|
|
72
|
+
"@driftengine/texture": "4.10.0"
|
|
73
73
|
}
|
|
74
74
|
}
|
package/src/index.ts
CHANGED
|
@@ -568,6 +568,10 @@ export type {
|
|
|
568
568
|
ShadowCasterSink,
|
|
569
569
|
ShadowCasters,
|
|
570
570
|
} from './render/shadowCasters.ts';
|
|
571
|
+
/* What `createStaticDraws` hands back, so a consumer can hold one in a field it declares. */
|
|
572
|
+
export type { StaticDrawsHandle } from './render/staticDraws.ts';
|
|
573
|
+
/* A bone animation's clip as a caller hands it over, and the handle `createBoneAnimation` returns. */
|
|
574
|
+
export type { BoneAnimationClip, BoneAnimationHandle } from './render/boneAnimation.ts';
|
|
571
575
|
export type { Atmosphere, UnderwaterAtmosphere } from './render/atmosphere.ts';
|
|
572
576
|
/*
|
|
573
577
|
* Reading an image's texels back exactly, which no canvas and no `VideoFrame` will do.
|
|
@@ -660,12 +664,14 @@ export { MAX_CLUSTERED_LIGHTS } from './render/clusteredLights.ts';
|
|
|
660
664
|
export { SurfaceTexture } from './render/surfaceTexture.ts';
|
|
661
665
|
export type { SurfaceMaterial, SurfaceTextureOptions } from './render/surfaceTexture.ts';
|
|
662
666
|
/*
|
|
663
|
-
* BC
|
|
664
|
-
* ask before handing one over — whether this device takes its format
|
|
667
|
+
* Compressed textures — BC, ETC2 and EAC, ASTC: the source `createSurfaceTexture` takes as blocks,
|
|
668
|
+
* the one question to ask before handing one over — whether this device takes its format
|
|
669
|
+
* (`renderer.compressedFormats`) — and the bytes a level of it holds, for a reader checking a file.
|
|
665
670
|
*/
|
|
666
671
|
export {
|
|
667
672
|
compressedFormatName,
|
|
668
673
|
isCompressedSource,
|
|
674
|
+
levelBytes,
|
|
669
675
|
uploadsCompressed,
|
|
670
676
|
} from './render/compressedSource.ts';
|
|
671
677
|
export type {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* **Why a draw needs its own.** The engine's diffuse ambient is one answer for a whole frame — the
|
|
6
6
|
* sky-and-ground gradient, or the probe grid's irradiance — so everything standing in one place is
|
|
7
|
-
* lit alike. A stage with a baked volume of indirect light (
|
|
7
|
+
* lit alike. A stage with a baked volume of indirect light (a volumetric lightmap) lights a
|
|
8
8
|
* character by sampling that volume where the character stands; a consumer that samples it each
|
|
9
9
|
* frame hands the result here, around that character's draws, and the character takes the light of
|
|
10
10
|
* the spot it is in rather than the frame's average.
|
package/src/render/areaLights.ts
CHANGED
|
@@ -94,14 +94,14 @@ export interface AreaLightSource {
|
|
|
94
94
|
* others. **Read only where the rectangle is shaded through the froxel table** — the ones past
|
|
95
95
|
* `maxAreaLights` with `clusteredLights` on; the fixed four light everything they face, as they
|
|
96
96
|
* always have. Absent, it is where the rectangle's irradiance on its axis falls to a thousandth
|
|
97
|
-
* of a scene unit, and never inside the rectangle itself.
|
|
97
|
+
* of a scene unit, and never inside the rectangle itself. An attenuation radius, in metres.
|
|
98
98
|
*/
|
|
99
99
|
range?: number;
|
|
100
100
|
/**
|
|
101
101
|
* Barn doors: a flap hinged at each of the rectangle's four edges, standing this many degrees from
|
|
102
102
|
* its normal. 90, or absent, folds them flat and they hide nothing; smaller closes them, so the
|
|
103
103
|
* light narrows to the opening they leave — a rectangle at 50° throws a beam rather than filling
|
|
104
|
-
* its hemisphere.
|
|
104
|
+
* its hemisphere. 88° is the usual default, nearly flat.
|
|
105
105
|
*
|
|
106
106
|
* What a fragment sees past the doors is the part of the rectangle no door's tip hides, one axis
|
|
107
107
|
* at a time, and the rectangle then shades as that smaller rectangle, its highlight included.
|
|
@@ -111,7 +111,7 @@ export interface AreaLightSource {
|
|
|
111
111
|
* rectangle's back face has none.
|
|
112
112
|
*/
|
|
113
113
|
barnDoorAngle?: number;
|
|
114
|
-
/** The barn doors' length in metres. Absent, 0.2:
|
|
114
|
+
/** The barn doors' length in metres. Absent, 0.2: the usual default of 20 cm. */
|
|
115
115
|
barnDoorLength?: number;
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -150,7 +150,7 @@ export function createAreaLightBuffer(capacity: number = MAX_AREA_LIGHTS): AreaL
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/**
|
|
153
|
+
/** The usual default barn door length, in metres. See `AreaLightSource.barnDoorLength`. */
|
|
154
154
|
export const DEFAULT_BARN_DOOR_LENGTH = 0.2;
|
|
155
155
|
|
|
156
156
|
/**
|
|
@@ -8,7 +8,7 @@ import type { Renderer, TranslucentMeshOptions } from './webgl2/renderer.ts';
|
|
|
8
8
|
import type { InstancedOptions, MeshInstances } from '../instances.ts';
|
|
9
9
|
import type { SurfaceMaterial } from '../surfaceTexture.ts';
|
|
10
10
|
import type { SurfaceOverlay } from '../surfaceOverlay.ts';
|
|
11
|
-
import type { SurfaceSource } from '../compressedSource.ts';
|
|
11
|
+
import type { CompressedTextureSource, SurfaceSource } from '../compressedSource.ts';
|
|
12
12
|
import type { LightmapPage } from '../lightmap.ts';
|
|
13
13
|
import type { DeviceParticles } from '../particlePool.ts';
|
|
14
14
|
import type { ClothBindingData } from '../clothBindingData.ts';
|
|
@@ -415,12 +415,22 @@ export type RendererApi = Omit<
|
|
|
415
415
|
): boolean;
|
|
416
416
|
|
|
417
417
|
/**
|
|
418
|
-
* Replace a texture's pixels, keeping the
|
|
418
|
+
* Replace a texture's pixels, keeping the handle and its sampler state.
|
|
419
419
|
*
|
|
420
420
|
* The binding a draw loop already holds stays valid, so the swap is a swap rather than a
|
|
421
421
|
* rebuild. Not a hot path: it re-uploads the whole image and rebuilds the mip chain.
|
|
422
|
+
*
|
|
423
|
+
* **Blocks may replace an image**, at the format's own size and with the chain they carry: the
|
|
424
|
+
* same picture at a fraction of the memory, which is how a loader that first showed a decoded
|
|
425
|
+
* image hands the device the blocks it encoded afterwards. Read in the colour space the texture
|
|
426
|
+
* was made with, and refused as `createSurfaceTexture` refuses them where the device does not take
|
|
427
|
+
* that format (`compressedFormats`). **The way back is refused**: a texture holding blocks takes
|
|
428
|
+
* no image, since nothing could say what its chain should become.
|
|
422
429
|
*/
|
|
423
|
-
updateSurfaceTexture(
|
|
430
|
+
updateSurfaceTexture(
|
|
431
|
+
texture: SurfaceTextureHandle,
|
|
432
|
+
source: TexImageSource | CompressedTextureSource,
|
|
433
|
+
): void;
|
|
424
434
|
|
|
425
435
|
disposeSurfaceTexture(texture: SurfaceTextureHandle): void;
|
|
426
436
|
|
|
@@ -248,12 +248,17 @@ async function acquireWebGpu(
|
|
|
248
248
|
features.push('timestamp-query');
|
|
249
249
|
}
|
|
250
250
|
/*
|
|
251
|
-
*
|
|
252
|
-
* a device without
|
|
253
|
-
* texture at a byte a texel instead of four.
|
|
251
|
+
* The three block families, on the same terms: each asked for wherever the adapter offers it,
|
|
252
|
+
* because a device without one still draws — the loader decodes, or encodes ETC2, for it — and
|
|
253
|
+
* one with it samples a texture at a byte a texel or half of one instead of four. Desktops
|
|
254
|
+
* offer BC; phones ETC2 and ASTC. See `compressedUpload.ts`.
|
|
254
255
|
*/
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
for (const family of [
|
|
257
|
+
'texture-compression-bc',
|
|
258
|
+
'texture-compression-etc2',
|
|
259
|
+
'texture-compression-astc',
|
|
260
|
+
] as const) {
|
|
261
|
+
if (adapter.features?.has(family) === true) features.push(family);
|
|
257
262
|
}
|
|
258
263
|
const descriptor: GPUDeviceDescriptor = {};
|
|
259
264
|
if (Object.keys(required).length > 0) descriptor.requiredLimits = required;
|