@driftengine/core 4.8.1 → 4.8.2
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 +86 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/input/actionMap.d.ts +6 -1
- package/dist/input/actionMap.js +20 -7
- package/dist/input/input.d.ts +28 -0
- package/dist/input/input.js +71 -9
- package/dist/input/inputHarness.d.ts +9 -0
- package/dist/input/inputHarness.js +34 -2
- package/dist/render/backend/webgl2/renderer.d.ts +52 -3
- package/dist/render/backend/webgl2/renderer.js +151 -5
- package/dist/render/backend/webgpu/buffers.d.ts +2 -0
- package/dist/render/backend/webgpu/buffers.js +2 -0
- package/dist/render/backend/webgpu/flatPass.js +16 -7
- package/dist/render/backend/webgpu/pipelineCache.d.ts +40 -8
- package/dist/render/backend/webgpu/pipelineCache.js +76 -9
- package/dist/render/backend/webgpu/renderer.d.ts +56 -1
- package/dist/render/backend/webgpu/renderer.js +151 -26
- package/dist/render/mapsWithoutUvs.d.ts +24 -0
- package/dist/render/mapsWithoutUvs.js +24 -0
- package/dist/render/mesh.d.ts +2 -0
- package/dist/render/mesh.js +3 -0
- package/dist/render/particlePool.d.ts +15 -0
- package/dist/render/particlePool.js +72 -37
- package/dist/render/pass.d.ts +12 -0
- package/dist/render/renderQuality.d.ts +7 -6
- package/dist/render/shaders/flat/glassTint.d.ts +28 -0
- package/dist/render/shaders/flat/glassTint.js +31 -0
- package/dist/render/shaders/flat/index.d.ts +12 -0
- package/dist/render/shaders/flat/index.js +7 -1
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +60 -25
- package/dist/render/shaders/flat/surfaceEffects.d.ts +1 -1
- package/dist/render/shaders/flat/surfaceEffects.js +6 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +64 -0
- package/dist/render/shaders/generated/flat.wgsl.js +102 -38
- package/dist/render/surfaceTexture.d.ts +2 -0
- package/dist/render/surfaceTexture.js +4 -0
- package/dist/render/viewModel.d.ts +29 -0
- package/dist/render/viewModel.js +35 -0
- package/package.json +6 -6
- package/src/index.ts +9 -1
- package/src/input/actionMap.ts +23 -8
- package/src/input/input.ts +79 -9
- package/src/input/inputHarness.ts +27 -2
- package/src/render/backend/webgl2/renderer.ts +166 -13
- package/src/render/backend/webgpu/buffers.ts +5 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -25
- package/src/render/backend/webgpu/pipelineCache.ts +82 -8
- package/src/render/backend/webgpu/renderer.ts +151 -26
- package/src/render/mapsWithoutUvs.ts +43 -0
- package/src/render/mesh.ts +3 -0
- package/src/render/particlePool.ts +101 -54
- package/src/render/pass.ts +12 -0
- package/src/render/renderQuality.ts +7 -6
- package/src/render/shaders/flat/glassTint.ts +48 -0
- package/src/render/shaders/flat/index.ts +19 -1
- package/src/render/shaders/flat/main.ts +60 -25
- package/src/render/shaders/flat/surfaceEffects.ts +6 -2
- package/src/render/shaders/generated/flat.wgsl.ts +102 -38
- package/src/render/surfaceTexture.ts +5 -0
- package/src/render/viewModel.ts +41 -0
|
@@ -23,3 +23,51 @@ vec4 glassUnmix(vec4 mip, float centreClarity) {
|
|
|
23
23
|
}
|
|
24
24
|
`;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The lit stage's switches, by the name of the constant each is: the decision both backends make
|
|
29
|
+
* about which optional code a lit program carries. See `litSwitchesGlsl`.
|
|
30
|
+
*/
|
|
31
|
+
export type LitSwitch =
|
|
32
|
+
'GLASS_SHADOWS' | 'CLUSTERED_LIGHTS' | 'LIGHT_FIXTURES' | 'SURFACE_EFFECTS' | 'DRIFT_LIGHT';
|
|
33
|
+
|
|
34
|
+
/** Every switch off: what a renderer starts from before its profile and its content say otherwise. */
|
|
35
|
+
export function noLitSwitches(): Record<LitSwitch, boolean> {
|
|
36
|
+
return {
|
|
37
|
+
GLASS_SHADOWS: false,
|
|
38
|
+
CLUSTERED_LIGHTS: false,
|
|
39
|
+
LIGHT_FIXTURES: false,
|
|
40
|
+
SURFACE_EFFECTS: false,
|
|
41
|
+
DRIFT_LIGHT: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Which of the lit stage's optional features a build reads. See `litSwitchesGlsl`. */
|
|
46
|
+
export interface LitSwitches {
|
|
47
|
+
readonly clusteredLights: boolean;
|
|
48
|
+
readonly lightFixtures: boolean;
|
|
49
|
+
readonly surfaceEffects: boolean;
|
|
50
|
+
readonly driftLight: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The lit stage's other switches, declared after `GLASS_SHADOWS` so that one keeps the id 0 the
|
|
55
|
+
* pipelines and their tests already name, each a pipeline-overridable constant on the generated
|
|
56
|
+
* WGSL and a written-in value on WebGL2 — the arrangement `GLASS_SHADOWS` has, for its reason:
|
|
57
|
+
* code a branch never runs still costs a shader its registers, and the device compiles it away
|
|
58
|
+
* only where the switch is a constant.
|
|
59
|
+
*
|
|
60
|
+
* **Clustering is the profile's and fixed.** The other three are content, off until a consumer
|
|
61
|
+
* first uses them — a measured profile or a cookie, a material with an effects table, a DriftLight
|
|
62
|
+
* volume — and then on for good; see `PipelineCache.enable` and the WebGL2 renderer's rebuild.
|
|
63
|
+
* Measured on RADV against the lit shader with glass already out: 6,852 instructions and 120
|
|
64
|
+
* registers, 4,641 and 96 with all four out.
|
|
65
|
+
*/
|
|
66
|
+
export function litSwitchesGlsl(switches: LitSwitches): string {
|
|
67
|
+
return /* glsl */ `
|
|
68
|
+
const bool CLUSTERED_LIGHTS = ${switches.clusteredLights ? 'true' : 'false'}; // wgsl:override
|
|
69
|
+
const bool LIGHT_FIXTURES = ${switches.lightFixtures ? 'true' : 'false'}; // wgsl:override
|
|
70
|
+
const bool SURFACE_EFFECTS = ${switches.surfaceEffects ? 'true' : 'false'}; // wgsl:override
|
|
71
|
+
const bool DRIFT_LIGHT = ${switches.driftLight ? 'true' : 'false'}; // wgsl:override
|
|
72
|
+
`;
|
|
73
|
+
}
|
|
@@ -7,7 +7,7 @@ import { preambleGlsl } from './preamble.ts';
|
|
|
7
7
|
import { LOBES_GLSL } from './lobes.ts';
|
|
8
8
|
import { POINTSHADOW_GLSL } from './pointShadow.ts';
|
|
9
9
|
import { DIRECTIONALSHADOW_GLSL } from './directionalShadow.ts';
|
|
10
|
-
import { glassTintGlsl } from './glassTint.ts';
|
|
10
|
+
import { glassTintGlsl, litSwitchesGlsl } from './glassTint.ts';
|
|
11
11
|
import { MORPH_GLSL } from '../morph.ts';
|
|
12
12
|
import { SKINNING_GLSL } from '../skinning.ts';
|
|
13
13
|
import { SURFACE_GLSL } from './surface.ts';
|
|
@@ -379,6 +379,18 @@ export interface FlatShaderOptions {
|
|
|
379
379
|
* courtyard at night, measured, and nothing once compiled out.
|
|
380
380
|
*/
|
|
381
381
|
readonly glassShadows?: boolean;
|
|
382
|
+
/**
|
|
383
|
+
* Whether this build has the clustered arm of the light loop: `clusteredLights`, fixed per
|
|
384
|
+
* renderer. True when absent, which is what the generated WGSL is built with. See
|
|
385
|
+
* `litSwitchesGlsl`.
|
|
386
|
+
*/
|
|
387
|
+
readonly clusteredLights?: boolean;
|
|
388
|
+
/** Whether it reads measured profiles and cookies. True when absent. See `litSwitchesGlsl`. */
|
|
389
|
+
readonly lightFixtures?: boolean;
|
|
390
|
+
/** Whether it reads a material's effects table. True when absent. See `litSwitchesGlsl`. */
|
|
391
|
+
readonly surfaceEffects?: boolean;
|
|
392
|
+
/** Whether it reads a DriftLight volume. True when absent. See `litSwitchesGlsl`. */
|
|
393
|
+
readonly driftLight?: boolean;
|
|
382
394
|
/**
|
|
383
395
|
* How many point lights this build declares room for. `MAX_POINT_LIGHTS` when absent.
|
|
384
396
|
*
|
|
@@ -484,6 +496,12 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
484
496
|
/* Depth zero as well: both glass lookups, sun and lamps, call it, and every permutation
|
|
485
497
|
declares its switch, which the pipelines set whether the permutation reads it or not. */
|
|
486
498
|
glassTintGlsl(options.glassShadows ?? true),
|
|
499
|
+
litSwitchesGlsl({
|
|
500
|
+
clusteredLights: options.clusteredLights ?? true,
|
|
501
|
+
lightFixtures: options.lightFixtures ?? true,
|
|
502
|
+
surfaceEffects: options.surfaceEffects ?? true,
|
|
503
|
+
driftLight: options.driftLight ?? true,
|
|
504
|
+
}),
|
|
487
505
|
LOBES_GLSL,
|
|
488
506
|
POINTSHADOW_GLSL,
|
|
489
507
|
DIRECTIONALSHADOW_GLSL,
|
|
@@ -14,8 +14,13 @@ export const MAIN_GLSL = `void main() {
|
|
|
14
14
|
* the material moves together. The gradients are taken here, outside every branch, for the room
|
|
15
15
|
* a window samples through textureGrad further down.
|
|
16
16
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
/* Zero rows unless a material has carried a table: \`SURFACE_EFFECTS\` is off until one does, and
|
|
18
|
+
every effect below reads exactly nothing from zero rows, which the device folds away. */
|
|
19
|
+
fxClear();
|
|
20
|
+
if (SURFACE_EFFECTS) {
|
|
21
|
+
fxLoad(int(surfaceAt.z));
|
|
22
|
+
surfaceAt = fxAnimate(surfaceAt);
|
|
23
|
+
}
|
|
19
24
|
vec2 fxDx = dFdx(surfaceAt.xy);
|
|
20
25
|
vec2 fxDy = dFdy(surfaceAt.xy);
|
|
21
26
|
if (uWriteMode.y != 0.0 && !ditherKeeps(uWriteMode.y)) discard;
|
|
@@ -908,7 +913,12 @@ export const MAIN_GLSL = `void main() {
|
|
|
908
913
|
*/
|
|
909
914
|
vec3 lampOpen = vec3(0.0);
|
|
910
915
|
vec3 lampShadowed = vec3(0.0);
|
|
911
|
-
|
|
916
|
+
/* \`DRIFT_LIGHT\` is off until a consumer gives the renderer a DriftLight volume, and the device
|
|
917
|
+
compiles the volume's reads away with it. */
|
|
918
|
+
float driftShare = 0.0;
|
|
919
|
+
if (DRIFT_LIGHT) {
|
|
920
|
+
driftShare = driftLightShare(vWorldPos);
|
|
921
|
+
}
|
|
912
922
|
|
|
913
923
|
/*
|
|
914
924
|
* Which froxel this fragment is in.
|
|
@@ -921,28 +931,53 @@ export const MAIN_GLSL = `void main() {
|
|
|
921
931
|
* same order and with the same clamps, because a fragment reading a froxel the binner filled
|
|
922
932
|
* differently is a fragment lit by somebody else's lights.
|
|
923
933
|
*/
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
int
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
934
|
+
/*
|
|
935
|
+
* **Only in a build that clusters, and there only in a frame that does.** \`CLUSTERED_LIGHTS\` is
|
|
936
|
+
* the profile's \`clusteredLights\`, a pipeline constant the device compiles this whole arm away
|
|
937
|
+
* on: the view transform, two logarithms and the froxel fetch below ran on every lit pixel of
|
|
938
|
+
* every scene, clustered or not, and the arm's records held their registers through the loop.
|
|
939
|
+
*/
|
|
940
|
+
int clusterBaseTexel = 0;
|
|
941
|
+
int clusterLights = 0;
|
|
942
|
+
bool clusteredArm = false;
|
|
943
|
+
if (CLUSTERED_LIGHTS) {
|
|
944
|
+
if (uClustered != 0) {
|
|
945
|
+
vec3 clusterView = (uView * vec4(vWorldPos, 1.0)).xyz;
|
|
946
|
+
float clusterDepth = -clusterView.z;
|
|
947
|
+
float clusterHalfH = max(clusterDepth, 1e-4) * uClusterFrustum.z;
|
|
948
|
+
float clusterHalfW = clusterHalfH * uClusterFrustum.w;
|
|
949
|
+
int clusterTileX = clamp(
|
|
950
|
+
int(floor((clusterView.x / clusterHalfW + 1.0) * 0.5 * float(CLUSTER_X))), 0, CLUSTER_X - 1);
|
|
951
|
+
int clusterTileY = clamp(
|
|
952
|
+
int(floor((clusterView.y / clusterHalfH + 1.0) * 0.5 * float(CLUSTER_Y))), 0, CLUSTER_Y - 1);
|
|
953
|
+
int clusterSlice = 0;
|
|
954
|
+
if (clusterDepth > uClusterFrustum.x) {
|
|
955
|
+
clusterSlice = clamp(
|
|
956
|
+
int(floor(log(clusterDepth / uClusterFrustum.x)
|
|
957
|
+
/ log(uClusterFrustum.y / uClusterFrustum.x) * float(CLUSTER_Z))),
|
|
958
|
+
0, CLUSTER_Z - 1);
|
|
959
|
+
}
|
|
960
|
+
clusterBaseTexel = LIGHT_REGION_TEXELS
|
|
961
|
+
+ (clusterTileX + clusterTileY * CLUSTER_X + clusterSlice * CLUSTER_X * CLUSTER_Y)
|
|
962
|
+
* CLUSTER_TEXELS;
|
|
963
|
+
clusterLights = int(clusterTexel(clusterBaseTexel).x);
|
|
964
|
+
clusteredArm = true;
|
|
965
|
+
}
|
|
938
966
|
}
|
|
939
|
-
int clusterBaseTexel = LIGHT_REGION_TEXELS
|
|
940
|
-
+ (clusterTileX + clusterTileY * CLUSTER_X + clusterSlice * CLUSTER_X * CLUSTER_Y)
|
|
941
|
-
* CLUSTER_TEXELS;
|
|
942
|
-
int clusterLights = uClustered != 0 ? int(clusterTexel(clusterBaseTexel).x) : 0;
|
|
943
967
|
|
|
944
968
|
/* The fixture atlas's size, asked once: its cookie band and its profile rows are found from it. */
|
|
945
969
|
vec2 fixtureSize = vec2(textureSize(uFixtureAtlas, 0));
|
|
970
|
+
/*
|
|
971
|
+
* Whether any light can carry a measured profile or a cookie, decided once rather than per
|
|
972
|
+
* light. \`LIGHT_FIXTURES\` is off until a consumer loads one, and the device compiles the
|
|
973
|
+
* fixture lookups away with it, which they hold their registers for in every scene otherwise.
|
|
974
|
+
*/
|
|
975
|
+
bool fixtureProfiles = false;
|
|
976
|
+
bool fixtureShapes = false;
|
|
977
|
+
if (LIGHT_FIXTURES) {
|
|
978
|
+
fixtureProfiles = uIesAtlasRows > 1.0;
|
|
979
|
+
fixtureShapes = uIesPlaneCount > 1.0 || uCookieTiles > 0.0;
|
|
980
|
+
}
|
|
946
981
|
|
|
947
982
|
// Point lights. Inverse-square-ish falloff clipped to a finite radius, so a
|
|
948
983
|
// light can be culled without a visible seam.
|
|
@@ -968,7 +1003,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
968
1003
|
float iesProfile;
|
|
969
1004
|
vec3 iesAxis;
|
|
970
1005
|
float cookie;
|
|
971
|
-
if (
|
|
1006
|
+
if (clusteredArm) {
|
|
972
1007
|
if (slot >= clusterLights) break;
|
|
973
1008
|
/*
|
|
974
1009
|
* Four indices to a texel. Selected with compares rather than by indexing the vector,
|
|
@@ -1273,7 +1308,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1273
1308
|
vec3 iesRay = -toLight / max(dist, 1e-4);
|
|
1274
1309
|
/* The angle from the fixture's axis, as a fraction of the 0-to-180 arc every row spans. */
|
|
1275
1310
|
float iesU = 0.0;
|
|
1276
|
-
if (
|
|
1311
|
+
if (fixtureProfiles) {
|
|
1277
1312
|
float iesAngle = acos(clamp(dot(iesRay, lightDir), -1.0, 1.0));
|
|
1278
1313
|
/* Clamped to the row's first and last texel centres, which is what the edge clamp of a
|
|
1279
1314
|
texture exactly one row wide gave; the atlas can be wider now, with cookies beside it. */
|
|
@@ -1297,7 +1332,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1297
1332
|
* \`uLightIesAxis\` to answer it. Behind a branch on two uniforms, so a scene using neither
|
|
1298
1333
|
* builds no frame at all.
|
|
1299
1334
|
*/
|
|
1300
|
-
if (
|
|
1335
|
+
if (fixtureShapes) {
|
|
1301
1336
|
vec3 iesRef = iesAxis - lightDir * dot(iesAxis, lightDir);
|
|
1302
1337
|
float iesRefLen = length(iesRef);
|
|
1303
1338
|
if (iesRefLen > 1e-5) {
|
|
@@ -1354,7 +1389,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1354
1389
|
* bytes on \`core-only\`** across the sixteen fragment permutations.
|
|
1355
1390
|
*/
|
|
1356
1391
|
float photometric = 1.0;
|
|
1357
|
-
if (
|
|
1392
|
+
if (fixtureProfiles) {
|
|
1358
1393
|
float iesBase = iesRow * (uIesPlaneCount + 1.0);
|
|
1359
1394
|
/* The profiles are the atlas's bottom rows, below any cookies. */
|
|
1360
1395
|
float iesTop = fixtureSize.y - uIesAtlasRows;
|
|
@@ -41,14 +41,18 @@ vec4 fx5;
|
|
|
41
41
|
vec3 fxNormal;
|
|
42
42
|
float fxDistance;
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
void
|
|
44
|
+
/** Every row zero: the layer carries no effect. */
|
|
45
|
+
void fxClear() {
|
|
46
46
|
fx0 = vec4(0.0);
|
|
47
47
|
fx1 = vec4(0.0);
|
|
48
48
|
fx2 = vec4(0.0);
|
|
49
49
|
fx3 = vec4(0.0);
|
|
50
50
|
fx4 = vec4(0.0);
|
|
51
51
|
fx5 = vec4(0.0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Fill the rows for this layer, or leave them zero where the array carries no table. */
|
|
55
|
+
void fxLoad(int layer) {
|
|
52
56
|
ivec2 size = textureSize(uSurfaceEffects, 0);
|
|
53
57
|
if (size.x < 6 || layer < 0 || layer >= size.y) return;
|
|
54
58
|
fx0 = texelFetch(uSurfaceEffects, ivec2(0, layer), 0);
|