@driftengine/core 4.10.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 +71 -0
- package/README.md +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/render/backend/webgl2/renderer.d.ts +99 -2
- package/dist/render/backend/webgl2/renderer.js +361 -42
- package/dist/render/backend/webgpu/buffers.d.ts +5 -0
- package/dist/render/backend/webgpu/buffers.js +98 -48
- 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/drawCommand.d.ts +2 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flatPass.js +2 -1
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +22 -1
- package/dist/render/backend/webgpu/pipelineCache.js +39 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +48 -0
- package/dist/render/backend/webgpu/renderer.js +207 -27
- 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/staticBundles.js +2 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -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/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/mesh.d.ts +9 -0
- package/dist/render/mesh.js +38 -14
- 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/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- 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 +15 -0
- package/dist/render/shaders/flat/index.js +15 -2
- 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 +43 -16
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- 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 +41 -19
- package/dist/render/shaders/flat/preamble.js +23 -5
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +768 -368
- package/dist/render/shaders/generated/flat.wgsl.js +791 -391
- 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/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- 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 +30 -0
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/package.json +6 -6
- package/src/index.ts +4 -2
- package/src/render/backend/webgl2/renderer.ts +408 -55
- package/src/render/backend/webgpu/buffers.ts +153 -54
- 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/drawCommand.ts +3 -0
- package/src/render/backend/webgpu/flatPass.ts +26 -28
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +46 -7
- package/src/render/backend/webgpu/renderer.ts +274 -48
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/staticBundles.ts +2 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/mesh.ts +66 -14
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/shader.ts +59 -0
- 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 +30 -2
- package/src/render/shaders/flat/layered.ts +115 -0
- package/src/render/shaders/flat/main.ts +43 -16
- package/src/render/shaders/flat/mapReads.ts +38 -0
- 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 +41 -19
- package/src/render/shaders/flat/preamble.ts +23 -5
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/flat.wgsl.ts +791 -391
- 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/tangentFrame.ts +16 -3
- package/src/render/skinnedClothRun.ts +138 -38
- 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 +30 -0
- 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
|
@@ -90,7 +90,8 @@ import { clamp } from '../../../math/scalar.ts';
|
|
|
90
90
|
import { attachContextLoss } from '../../contextLoss.ts';
|
|
91
91
|
import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.ts';
|
|
92
92
|
import { GpuTimer } from '../../gpuTimer.ts';
|
|
93
|
-
import { compileProgram, uniformLocations } from '../../shader.ts';
|
|
93
|
+
import { compileProgram, finishProgram, startProgram, uniformLocations } from '../../shader.ts';
|
|
94
|
+
import type { StartedProgram } from '../../shader.ts';
|
|
94
95
|
import {
|
|
95
96
|
countUniformVectors,
|
|
96
97
|
FULL_LIGHT_BUDGET,
|
|
@@ -104,6 +105,11 @@ import {
|
|
|
104
105
|
packSurfaceOverlay,
|
|
105
106
|
type SurfaceOverlay,
|
|
106
107
|
} from '../../surfaceOverlay.ts';
|
|
108
|
+
import { PROJECTION_FLOATS, packSurfaceProjection, projects } from '../../surfaceProjection.ts';
|
|
109
|
+
import { orthographicDepthSpan } from '../../lightMatrix.ts';
|
|
110
|
+
import { LAYER_FLOATS, packSurfaceLayers } from '../../surfaceLayers.ts';
|
|
111
|
+
import type { SurfaceLayers } from '../../surfaceLayers.ts';
|
|
112
|
+
import type { SurfaceProjection } from '../../surfaceProjection.ts';
|
|
107
113
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.ts';
|
|
108
114
|
import { SceneTarget } from '../../sceneTarget.ts';
|
|
109
115
|
import type { ColourGradeLut } from '../../colourGrade.ts';
|
|
@@ -1049,6 +1055,37 @@ const IDENTITY_CLIP = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0
|
|
|
1049
1055
|
/** Standing still, for a caller that asked for dust and not for wind. */
|
|
1050
1056
|
const NO_DRIFT: Vec3 = [0, 0, 0];
|
|
1051
1057
|
|
|
1058
|
+
/** Whether two matrices hold the same sixteen numbers. */
|
|
1059
|
+
function sameMatrix(a: ArrayLike<number>, b: ArrayLike<number>): boolean {
|
|
1060
|
+
for (let k = 0; k < 16; k++) if (a[k] !== b[k]) return false;
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/** How often `ready` asks the driver whether a program is done, and how many times before waiting. */
|
|
1065
|
+
const READY_POLL_MS = 4;
|
|
1066
|
+
const READY_MAX_POLLS = 500;
|
|
1067
|
+
|
|
1068
|
+
/** The halves a skin draws under the screen-space blur, each a program of its own. */
|
|
1069
|
+
const SPLIT_SKIN_HALVES = ['scene', 'diffuse', 'albedo'] as const;
|
|
1070
|
+
|
|
1071
|
+
/** A lit program with its uniform table, as every flat program is kept. */
|
|
1072
|
+
interface FlatProgram {
|
|
1073
|
+
program: WebGLProgram;
|
|
1074
|
+
uniforms: Record<string, WebGLUniformLocation>;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/** The vertex variant a draw takes, and its program where it is not the plain one. */
|
|
1078
|
+
interface DrawVariant {
|
|
1079
|
+
/** What a model's program is keyed by: `plain`, `skinned8`, `cloth4m` and the rest. */
|
|
1080
|
+
name: string;
|
|
1081
|
+
skinned: boolean;
|
|
1082
|
+
morphed: boolean;
|
|
1083
|
+
cloth: { binding: GlClothBinding; particles: GlClothParticles } | null;
|
|
1084
|
+
clothed: FlatProgram | undefined;
|
|
1085
|
+
program: WebGLProgram | null;
|
|
1086
|
+
uniforms: Record<string, WebGLUniformLocation>;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1052
1089
|
export class WebGL2Renderer implements RendererApi {
|
|
1053
1090
|
/**
|
|
1054
1091
|
* Private on purpose. The GL context never leaves this directory, so a
|
|
@@ -1347,6 +1384,23 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1347
1384
|
string,
|
|
1348
1385
|
{ program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> }
|
|
1349
1386
|
>();
|
|
1387
|
+
/**
|
|
1388
|
+
* Model programs still compiling, where draws skip a compile (`pipelineCompile: 'skip'`) and the
|
|
1389
|
+
* context offers `KHR_parallel_shader_compile`; `parallelCompile` is its completion query then,
|
|
1390
|
+
* and null where draws wait for a program here instead. See `modelProgram`.
|
|
1391
|
+
*/
|
|
1392
|
+
private readonly modelPending = new Map<string, StartedProgram>();
|
|
1393
|
+
/** What `drawVariantOf` answers, rewritten each draw rather than made. */
|
|
1394
|
+
private readonly drawVariant: DrawVariant = {
|
|
1395
|
+
name: 'plain',
|
|
1396
|
+
skinned: false,
|
|
1397
|
+
morphed: false,
|
|
1398
|
+
cloth: null,
|
|
1399
|
+
clothed: undefined,
|
|
1400
|
+
program: null,
|
|
1401
|
+
uniforms: {},
|
|
1402
|
+
};
|
|
1403
|
+
private readonly parallelCompile: { readonly COMPLETION_STATUS_KHR: number } | null;
|
|
1350
1404
|
/** The last palette chosen, which an eye's axis is turned by. See `setSkinPalette`. */
|
|
1351
1405
|
private paletteData: Float32Array | null = null;
|
|
1352
1406
|
|
|
@@ -1410,7 +1464,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1410
1464
|
private oitReplaying = false;
|
|
1411
1465
|
/** `uSurfaceScene`, resolved once a pass. See `surfaceScene.ts`. */
|
|
1412
1466
|
private readonly surfaceScene = new Float32Array(4);
|
|
1413
|
-
/** The
|
|
1467
|
+
/** The three lanes of `uWriteMode`, kept here because a `vec3` is written whole. */
|
|
1414
1468
|
private oitWeight = 0;
|
|
1415
1469
|
/**
|
|
1416
1470
|
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
@@ -1418,6 +1472,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1418
1472
|
*/
|
|
1419
1473
|
private surfaceFog = FOG_RECEDE;
|
|
1420
1474
|
private ditherFade = 0;
|
|
1475
|
+
private ditherOpacity = 0;
|
|
1421
1476
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
1422
1477
|
private oitRefused = false;
|
|
1423
1478
|
/** Said once for the whole renderer. See `refuseOitMultisampled`. */
|
|
@@ -1697,6 +1752,28 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1697
1752
|
private overlayTexture: WebGLTexture | null = null;
|
|
1698
1753
|
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
1699
1754
|
private overlayRoom: boolean | null = null;
|
|
1755
|
+
/**
|
|
1756
|
+
* The matrix the sun's dynamic shadow pass was last drawn with, uncorrected, and whether one has
|
|
1757
|
+
* been: what the moving layer is read through. See `movingLightFor`.
|
|
1758
|
+
*/
|
|
1759
|
+
private readonly movingLightViewProj = new Float32Array(16);
|
|
1760
|
+
private movingLightKnown = false;
|
|
1761
|
+
/** Whether the lit stage has room for the moving layer's matrix, and whether its refusal was said. */
|
|
1762
|
+
private movingRoom: boolean | null = null;
|
|
1763
|
+
private warnedMovingRoom = false;
|
|
1764
|
+
/** The same answer for a material's projection, and whether its refusal was said. */
|
|
1765
|
+
private projectionRoom: boolean | null = null;
|
|
1766
|
+
private warnedProjectionRoom = false;
|
|
1767
|
+
/** `uWorldUv`, the current material's projection packed. See `surfaceProjection.ts`. */
|
|
1768
|
+
private readonly projection = new Float32Array(PROJECTION_FLOATS);
|
|
1769
|
+
/** The current material's `diffuseTransmission`, `uGlassTint.w`, 0 to 1. */
|
|
1770
|
+
private diffuseTransmission = 0;
|
|
1771
|
+
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
1772
|
+
private readonly layerFloats = new Float32Array(LAYER_FLOATS);
|
|
1773
|
+
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
1774
|
+
private layersRoom: boolean | null = null;
|
|
1775
|
+
private warnedLayersRoom = false;
|
|
1776
|
+
private warnedLayersModelMap = false;
|
|
1700
1777
|
private warnedOverlayRoom = false;
|
|
1701
1778
|
private warnedOverlayBlocks = false;
|
|
1702
1779
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -3024,6 +3101,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3024
3101
|
* Measured on an RX 9070 XT through ANGLE/Vulkan: offered. `scripts/depth-survey.mjs` is what
|
|
3025
3102
|
* asks, and it also reports what WebGPU can do.
|
|
3026
3103
|
*/
|
|
3104
|
+
/*
|
|
3105
|
+
* Where draws skip a compile, the query that says a program is done without waiting for it;
|
|
3106
|
+
* where the context has none, a program is waited for as it always was. See `modelProgram`.
|
|
3107
|
+
*/
|
|
3108
|
+
this.parallelCompile =
|
|
3109
|
+
this.quality.pipelineCompile === 'skip'
|
|
3110
|
+
? (gl.getExtension('KHR_parallel_shader_compile') as {
|
|
3111
|
+
readonly COMPLETION_STATUS_KHR: number;
|
|
3112
|
+
} | null)
|
|
3113
|
+
: null;
|
|
3027
3114
|
this.clipControl = gl.getExtension('EXT_clip_control') as ClipControlExtension | null;
|
|
3028
3115
|
if (REVERSED_DEPTH && this.clipControl !== null) {
|
|
3029
3116
|
this.clipControl.clipControlEXT(
|
|
@@ -3361,7 +3448,36 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3361
3448
|
* this before its first frame gets the right behaviour on both without asking which backend
|
|
3362
3449
|
* it has, which is the contract `RendererApi` exists to keep.
|
|
3363
3450
|
*/
|
|
3364
|
-
|
|
3451
|
+
/**
|
|
3452
|
+
* Resolve once every program a draw has started compiling has finished, adopting each: what a
|
|
3453
|
+
* loading screen awaits after drawing what its scene will use, where draws skip a compile. See
|
|
3454
|
+
* `RenderQuality.pipelineCompile`; nothing is started anywhere else here, so otherwise it is now.
|
|
3455
|
+
*
|
|
3456
|
+
* **It waits without holding the thread**, as the other backend's does: it asks the driver
|
|
3457
|
+
* whether each is done, which does not wait, and yields between asks. A driver that never says
|
|
3458
|
+
* done is asked the way that waits after `READY_MAX_POLLS`, so this cannot hang a loading screen.
|
|
3459
|
+
*/
|
|
3460
|
+
async ready(): Promise<void> {
|
|
3461
|
+
for (let polls = 0; this.modelPending.size > 0 && !this.contextLost; polls++) {
|
|
3462
|
+
for (const [key, started] of this.modelPending) {
|
|
3463
|
+
const done =
|
|
3464
|
+
polls >= READY_MAX_POLLS ||
|
|
3465
|
+
this.parallelCompile === null ||
|
|
3466
|
+
this.gl.getProgramParameter(
|
|
3467
|
+
started.program,
|
|
3468
|
+
this.parallelCompile.COMPLETION_STATUS_KHR,
|
|
3469
|
+
) === true;
|
|
3470
|
+
if (!done) continue;
|
|
3471
|
+
this.modelPending.delete(key);
|
|
3472
|
+
this.adoptModel(key, finishProgram(this.gl, started), started.label);
|
|
3473
|
+
}
|
|
3474
|
+
this.useFlatProgram();
|
|
3475
|
+
if (this.modelPending.size > 0) {
|
|
3476
|
+
// platform: browser default — a yield between two asks of the driver, in a browser module
|
|
3477
|
+
await new Promise<void>((resolve) => setTimeout(resolve, READY_POLL_MS));
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3365
3481
|
|
|
3366
3482
|
dispose(options: { releaseContext?: boolean } = {}): void {
|
|
3367
3483
|
if (this.disposed) return;
|
|
@@ -3845,18 +3961,43 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3845
3961
|
kind: SurfaceModelKind,
|
|
3846
3962
|
/** Which half of a skin it draws under the screen-space blur. See `SkinHalf`. */
|
|
3847
3963
|
half: SkinHalf = 'whole',
|
|
3848
|
-
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } {
|
|
3964
|
+
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } | null {
|
|
3849
3965
|
const key = `${variant}|${kind}|${half}`;
|
|
3850
3966
|
const held = this.modelFlat.get(key);
|
|
3851
3967
|
if (held !== undefined) return held;
|
|
3852
3968
|
const { gl } = this;
|
|
3853
3969
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
3854
|
-
const
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3970
|
+
const vertex = modelVertexOf(variant, kind);
|
|
3971
|
+
const fragment = skinHalfBound(modelBound(this.flatFragSource, kind), half);
|
|
3972
|
+
/*
|
|
3973
|
+
* **Where draws skip a compile, none until the driver says asking will not wait**: the program
|
|
3974
|
+
* is started at the first draw that wants it and that draw, and every one before it is ready,
|
|
3975
|
+
* is left out. See `RenderQuality.pipelineCompile`.
|
|
3976
|
+
*/
|
|
3977
|
+
if (this.parallelCompile !== null) {
|
|
3978
|
+
const started = this.modelPending.get(key);
|
|
3979
|
+
if (started === undefined) {
|
|
3980
|
+
this.modelPending.set(key, startProgram(gl, vertex, fragment, label));
|
|
3981
|
+
return null;
|
|
3982
|
+
}
|
|
3983
|
+
if (
|
|
3984
|
+
gl.getProgramParameter(started.program, this.parallelCompile.COMPLETION_STATUS_KHR) !== true
|
|
3985
|
+
) {
|
|
3986
|
+
return null;
|
|
3987
|
+
}
|
|
3988
|
+
this.modelPending.delete(key);
|
|
3989
|
+
return this.adoptModel(key, finishProgram(gl, started), label);
|
|
3990
|
+
}
|
|
3991
|
+
return this.adoptModel(key, compileProgram(gl, vertex, fragment, label), label);
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
/** A model program compiled: kept, written to by every material setter, and fed the pass. */
|
|
3995
|
+
private adoptModel(
|
|
3996
|
+
key: string,
|
|
3997
|
+
program: WebGLProgram,
|
|
3998
|
+
label: string,
|
|
3999
|
+
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } {
|
|
4000
|
+
const { gl } = this;
|
|
3860
4001
|
const made = { program, uniforms: uniformLocations(gl, program, label) };
|
|
3861
4002
|
this.modelFlat.set(key, made);
|
|
3862
4003
|
this.flatTargets.push(made);
|
|
@@ -4182,9 +4323,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4182
4323
|
* and read neither — one decision for every program, as `setMaterial` says.
|
|
4183
4324
|
*/
|
|
4184
4325
|
const modelMap = material?.modelMap ?? null;
|
|
4326
|
+
/* A layered material's mask goes where a model's map would: see `SurfaceMaterial.layers`. */
|
|
4327
|
+
const layers = this.layersOf(material);
|
|
4328
|
+
const boundMap = modelMap ?? layers?.mask ?? null;
|
|
4185
4329
|
gl.activeTexture(gl.TEXTURE0 + MODEL_MAP_TEXTURE_UNIT);
|
|
4186
4330
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
4187
|
-
if (
|
|
4331
|
+
if (boundMap !== null) boundMap.bind(gl, MODEL_MAP_TEXTURE_UNIT);
|
|
4332
|
+
packSurfaceLayers(layers, this.layerFloats);
|
|
4333
|
+
if (layers !== null) this.askLit('LAYERED');
|
|
4334
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
4188
4335
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
4189
4336
|
packModel(
|
|
4190
4337
|
material?.model ?? null,
|
|
@@ -4197,6 +4344,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4197
4344
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
4198
4345
|
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
4199
4346
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
4347
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
4348
|
+
/* How much light from behind the surface lets through, in the glass tint's spare lane. */
|
|
4349
|
+
this.diffuseTransmission = Math.min(Math.max(material?.diffuseTransmission ?? 0, 0), 1) || 0;
|
|
4350
|
+
gl.uniform4f(u['uGlassTint'] ?? null, 1, 1, 1, this.diffuseTransmission);
|
|
4351
|
+
/* Maps placed by the world, where the material asks and the lit stage has room: worldUv.ts. */
|
|
4352
|
+
const projection = material?.projection ?? null;
|
|
4353
|
+
packSurfaceProjection(this.projectionFits(projection) ? projection : null, this.projection);
|
|
4354
|
+
if ((this.projection[0] as number) > 0) this.askLit('WORLD_UVS');
|
|
4355
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
4200
4356
|
|
|
4201
4357
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
4202
4358
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -4371,17 +4527,110 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4371
4527
|
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
4372
4528
|
private overlayRoomLeft(): number {
|
|
4373
4529
|
return (
|
|
4374
|
-
this.fragmentVectorLimit() -
|
|
4530
|
+
this.fragmentVectorLimit() -
|
|
4531
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, { SURFACE_OVERLAY: false }))
|
|
4375
4532
|
);
|
|
4376
4533
|
}
|
|
4377
4534
|
|
|
4378
4535
|
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
4379
4536
|
private overlayFits(): boolean {
|
|
4380
|
-
this.overlayRoom ??=
|
|
4381
|
-
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
4537
|
+
this.overlayRoom ??= this.switchFits('SURFACE_OVERLAY');
|
|
4382
4538
|
return this.overlayRoom;
|
|
4383
4539
|
}
|
|
4384
4540
|
|
|
4541
|
+
/**
|
|
4542
|
+
* Whether a material's projection can be drawn: none asked, or the lit stage has the room for its
|
|
4543
|
+
* one vector. **Refused, said once, where it has not**, as the overlay is, and the material's
|
|
4544
|
+
* maps are then laid by the mesh's coordinates, as they were before it existed.
|
|
4545
|
+
*/
|
|
4546
|
+
private projectionFits(projection: SurfaceProjection | null): boolean {
|
|
4547
|
+
if (!projects(projection)) return true;
|
|
4548
|
+
this.projectionRoom ??= this.switchFits('WORLD_UVS');
|
|
4549
|
+
if (!this.projectionRoom && !this.warnedProjectionRoom) {
|
|
4550
|
+
this.warnedProjectionRoom = true;
|
|
4551
|
+
console.warn(
|
|
4552
|
+
"Renderer: a material's projection is refused on this device. It takes one fragment " +
|
|
4553
|
+
'uniform vector and the lit shader already uses all ' +
|
|
4554
|
+
`${this.fragmentVectorLimit()} this GPU offers; its maps follow the mesh's coordinates. ` +
|
|
4555
|
+
'A lower `maxLights` makes the room, or WebGPU has it.',
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
return this.projectionRoom;
|
|
4559
|
+
}
|
|
4560
|
+
|
|
4561
|
+
/**
|
|
4562
|
+
* What the sun's moving layer is read through: the matrix its shadow pass was last drawn with,
|
|
4563
|
+
* where the lit stage carries one (`MOVING_SUN`); the environment's otherwise, which every caller
|
|
4564
|
+
* drawing both layers with one matrix hands it anyway.
|
|
4565
|
+
*/
|
|
4566
|
+
private movingLightFor(env: Environment): ReadonlyMat4 {
|
|
4567
|
+
return this.movingLightKnown && this.litOn.MOVING_SUN
|
|
4568
|
+
? this.movingLightViewProj
|
|
4569
|
+
: env.lightViewProj;
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4572
|
+
/**
|
|
4573
|
+
* Turn the moving layer's own matrix on the first time a frame draws that layer with one the
|
|
4574
|
+
* environment's does not share. **Refused, said once, where the lit stage has no room for its five
|
|
4575
|
+
* vectors**: the layer is then read through the environment's matrix, so its shadows land where
|
|
4576
|
+
* that matrix puts them, and a caller there draws both layers with one.
|
|
4577
|
+
*/
|
|
4578
|
+
private askMovingSun(env: Environment): void {
|
|
4579
|
+
if (this.litWanted.MOVING_SUN || !this.movingLightKnown) return;
|
|
4580
|
+
if (sameMatrix(this.movingLightViewProj, env.lightViewProj)) return;
|
|
4581
|
+
this.movingRoom ??= this.switchFits('MOVING_SUN');
|
|
4582
|
+
if (this.movingRoom) {
|
|
4583
|
+
this.askLit('MOVING_SUN');
|
|
4584
|
+
return;
|
|
4585
|
+
}
|
|
4586
|
+
if (this.warnedMovingRoom) return;
|
|
4587
|
+
this.warnedMovingRoom = true;
|
|
4588
|
+
console.warn(
|
|
4589
|
+
"Renderer: the sun's moving layer cannot be read through a matrix of its own on this " +
|
|
4590
|
+
'device; it takes five fragment uniform vectors the lit shader does not have. It is read ' +
|
|
4591
|
+
"through the environment's `lightViewProj`, so draw the dynamic shadow pass with that " +
|
|
4592
|
+
'matrix here, or lower `maxLights` to make the room. WebGPU has it.',
|
|
4593
|
+
);
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
/**
|
|
4597
|
+
* A material's layers where they can be drawn: none asked, a model's map in the mask's place, or
|
|
4598
|
+
* no room for their two vectors each leave one layer, the last two said once each.
|
|
4599
|
+
*/
|
|
4600
|
+
private layersOf(material: SurfaceMaterial | null): SurfaceLayers<SurfaceTexture> | null {
|
|
4601
|
+
const layers = material?.layers ?? null;
|
|
4602
|
+
if (layers === null || layers.repeats.length === 0) return null;
|
|
4603
|
+
if ((material?.modelMap ?? null) !== null) {
|
|
4604
|
+
if (!this.warnedLayersModelMap) {
|
|
4605
|
+
this.warnedLayersModelMap = true;
|
|
4606
|
+
console.warn(
|
|
4607
|
+
"Renderer: a material's layers are drawn as one: its mask is read where a model's map " +
|
|
4608
|
+
'goes, and this material carries a `modelMap` too.',
|
|
4609
|
+
);
|
|
4610
|
+
}
|
|
4611
|
+
return null;
|
|
4612
|
+
}
|
|
4613
|
+
this.layersRoom ??= this.switchFits('LAYERED');
|
|
4614
|
+
if (this.layersRoom) return layers as SurfaceLayers<SurfaceTexture>;
|
|
4615
|
+
if (!this.warnedLayersRoom) {
|
|
4616
|
+
this.warnedLayersRoom = true;
|
|
4617
|
+
console.warn(
|
|
4618
|
+
"Renderer: a material's layers are refused on this device. They take two fragment uniform " +
|
|
4619
|
+
'vectors the lit shader does not have; the material is drawn as its base layer. A lower ' +
|
|
4620
|
+
'`maxLights` makes the room, or WebGPU has it.',
|
|
4621
|
+
);
|
|
4622
|
+
}
|
|
4623
|
+
return null;
|
|
4624
|
+
}
|
|
4625
|
+
|
|
4626
|
+
/** Whether the lit shader with `feature` compiled in fits this GPU's fragment uniform vectors. */
|
|
4627
|
+
private switchFits(feature: LitSwitch): boolean {
|
|
4628
|
+
const asIf: Partial<Record<LitSwitch, boolean>> = { [feature]: true };
|
|
4629
|
+
return (
|
|
4630
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, asIf)) <= this.fragmentVectorLimit()
|
|
4631
|
+
);
|
|
4632
|
+
}
|
|
4633
|
+
|
|
4385
4634
|
/**
|
|
4386
4635
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
4387
4636
|
*
|
|
@@ -4525,9 +4774,37 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4525
4774
|
this.useFlatProgram();
|
|
4526
4775
|
}
|
|
4527
4776
|
|
|
4528
|
-
/**
|
|
4777
|
+
/**
|
|
4778
|
+
* Whether the draws that follow spend their own opacity on a screen door rather than on blending:
|
|
4779
|
+
* an instance's `MeshInstances.alphas`, or a vertex's channel alpha, keeps that share of the
|
|
4780
|
+
* pixels `setDitherFade` would and draws the rest not at all. So an opaque or cut-out batch fades
|
|
4781
|
+
* one instance at a time, with no sorting and no blend, and an instance at 1 is drawn whole.
|
|
4782
|
+
*
|
|
4783
|
+
* Off is what `bindMeshPass` restores. It shares the fade's pattern, so a crossfade at `t` and
|
|
4784
|
+
* `-t` of an instance at opacity `a` covers that instance's share once, without holes. Material
|
|
4785
|
+
* state like `setDitherFade`; instanced draws read it, and shadow casters do not.
|
|
4786
|
+
*
|
|
4787
|
+
* What it gives up: a fade reads as grain while it lasts, which a temporal resolve smooths and a
|
|
4788
|
+
* plain frame does not; a blended draw that asks spends its opacity here and blends none of it;
|
|
4789
|
+
* and a lightmapped batch carries regions where the opacities would be, so it has none to fade.
|
|
4790
|
+
*/
|
|
4791
|
+
setDitherOpacity(on: boolean): void {
|
|
4792
|
+
if (this.contextLost) return;
|
|
4793
|
+
this.materials.dirty();
|
|
4794
|
+
this.ditherOpacity = on ? 1 : 0;
|
|
4795
|
+
for (const target of this.flatTargets) {
|
|
4796
|
+
this.gl.useProgram(target.program);
|
|
4797
|
+
this.writeMode(target.uniforms);
|
|
4798
|
+
}
|
|
4799
|
+
this.useFlatProgram();
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
/**
|
|
4803
|
+
* `uWriteMode`: the order-independent weight, the fade and whether the opacity is dithered, which
|
|
4804
|
+
* share one uniform row.
|
|
4805
|
+
*/
|
|
4529
4806
|
private writeMode(u: Record<string, WebGLUniformLocation>): void {
|
|
4530
|
-
this.gl.
|
|
4807
|
+
this.gl.uniform3f(u['uWriteMode'] ?? null, this.oitWeight, this.ditherFade, this.ditherOpacity);
|
|
4531
4808
|
}
|
|
4532
4809
|
|
|
4533
4810
|
/**
|
|
@@ -4852,6 +5129,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4852
5129
|
modelKind === null
|
|
4853
5130
|
? null
|
|
4854
5131
|
: this.modelProgram(clip === null ? 'instanced' : 'animated', modelKind);
|
|
5132
|
+
/* Its model's program still compiling, where draws skip a compile: left out until it lands. */
|
|
5133
|
+
if (modelKind !== null && modelled === null) return;
|
|
4855
5134
|
const program =
|
|
4856
5135
|
modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
4857
5136
|
const u =
|
|
@@ -4996,7 +5275,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4996
5275
|
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
4997
5276
|
const tint = options.refractTint ?? WHITE_TINT;
|
|
4998
5277
|
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
4999
|
-
|
|
5278
|
+
/* The pane's colour; w is the material's light through from behind, which glass never reads. */
|
|
5279
|
+
gl.uniform4f(
|
|
5280
|
+
u['uGlassTint'] ?? null,
|
|
5281
|
+
glass.tint[0] ?? 1,
|
|
5282
|
+
glass.tint[1] ?? 1,
|
|
5283
|
+
glass.tint[2] ?? 1,
|
|
5284
|
+
this.diffuseTransmission,
|
|
5285
|
+
);
|
|
5000
5286
|
return true;
|
|
5001
5287
|
}
|
|
5002
5288
|
|
|
@@ -6144,6 +6430,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6144
6430
|
this.shadowPassActive = true;
|
|
6145
6431
|
this.activeSunLayer = layer;
|
|
6146
6432
|
this.glassCasters.clear();
|
|
6433
|
+
/* The moving layer is read through the matrix it is drawn with: see `movingLightFor`. */
|
|
6434
|
+
if (layer === 'dynamic') {
|
|
6435
|
+
this.movingLightViewProj.set(lightViewProj as Float32Array);
|
|
6436
|
+
this.movingLightKnown = true;
|
|
6437
|
+
}
|
|
6147
6438
|
/* The static layer opens first, so a frame's peel counts from here. See `peelFilled`. */
|
|
6148
6439
|
if (layer === 'static') this.peelFilled = false;
|
|
6149
6440
|
/*
|
|
@@ -6560,7 +6851,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6560
6851
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
6561
6852
|
*/
|
|
6562
6853
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
6563
|
-
private flatFragAt(
|
|
6854
|
+
private flatFragAt(
|
|
6855
|
+
budget: LightBudget,
|
|
6856
|
+
/* A switch counted as it would be, for asking whether it fits: see `switchFits`. */
|
|
6857
|
+
asIf: Partial<Record<LitSwitch, boolean>> = {},
|
|
6858
|
+
): string {
|
|
6564
6859
|
return flatFrag({
|
|
6565
6860
|
pointShadows: this.quality.pointShadows,
|
|
6566
6861
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -6573,8 +6868,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6573
6868
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
6574
6869
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
6575
6870
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
6576
|
-
surfaceOverlay:
|
|
6871
|
+
surfaceOverlay: asIf.SURFACE_OVERLAY ?? this.litOn.SURFACE_OVERLAY,
|
|
6577
6872
|
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
6873
|
+
worldUvs: asIf.WORLD_UVS ?? this.litOn.WORLD_UVS,
|
|
6874
|
+
movingSun: asIf.MOVING_SUN ?? this.litOn.MOVING_SUN,
|
|
6875
|
+
layered: asIf.LAYERED ?? this.litOn.LAYERED,
|
|
6578
6876
|
maxLights: budget.maxLights,
|
|
6579
6877
|
maxAreaLights: budget.maxAreaLights,
|
|
6580
6878
|
});
|
|
@@ -6662,6 +6960,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6662
6960
|
this.clothFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
6663
6961
|
}
|
|
6664
6962
|
|
|
6963
|
+
/* Started against the source before this switch: begun again at the next draw that asks. */
|
|
6964
|
+
for (const started of this.modelPending.values()) gl.deleteProgram(started.program);
|
|
6965
|
+
this.modelPending.clear();
|
|
6665
6966
|
for (const [key, entry] of this.modelFlat) {
|
|
6666
6967
|
const [variant, kind, half] = key.split('|') as [string, SurfaceModelKind, SkinHalf];
|
|
6667
6968
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
@@ -8450,6 +8751,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8450
8751
|
ambient: env?.ambient ?? [0, 0, 0],
|
|
8451
8752
|
sunShadow,
|
|
8452
8753
|
lightViewProj: env?.lightViewProj ?? IDENTITY_MAT4,
|
|
8754
|
+
movingLightViewProj: env === null ? IDENTITY_MAT4 : this.movingLightFor(env),
|
|
8453
8755
|
/* The placeholder rather than null where a map is merely absent, for the reason
|
|
8454
8756
|
`emptyTexture.ts` gives. It is black, which as a depth is an occluder at the light,
|
|
8455
8757
|
so it is safe only because `sunShadow` is 0 whenever these maps do not exist. */
|
|
@@ -8705,6 +9007,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8705
9007
|
this.currentSurfaceTexture = null;
|
|
8706
9008
|
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
8707
9009
|
this.overlayFloats.fill(0);
|
|
9010
|
+
/* Nor a projection: maps follow the mesh's coordinates until a material asks otherwise. */
|
|
9011
|
+
this.projection.fill(0);
|
|
9012
|
+
/* Nor light through from behind. See `SurfaceMaterial.diffuseTransmission`. */
|
|
9013
|
+
this.diffuseTransmission = 0;
|
|
9014
|
+
/* Nor layers: one layer until a material asks for more. */
|
|
9015
|
+
this.layerFloats.fill(0);
|
|
8708
9016
|
this.overlayMaps = null;
|
|
8709
9017
|
this.overlayTexture = null;
|
|
8710
9018
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
@@ -8718,6 +9026,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8718
9026
|
this.environmentDials[2] = 1;
|
|
8719
9027
|
this.emission[0] = env.emissiveGain;
|
|
8720
9028
|
this.emission[1] = env.nightFactor;
|
|
9029
|
+
/* The moving layer's own matrix, the first time a frame draws that layer with one. */
|
|
9030
|
+
this.askMovingSun(env);
|
|
9031
|
+
/* No dither and no weighting: every draw whole, until a crossfade or an opacity asks. */
|
|
9032
|
+
this.oitWeight = 0;
|
|
9033
|
+
this.ditherFade = 0;
|
|
9034
|
+
this.ditherOpacity = 0;
|
|
8721
9035
|
for (const target of this.flatTargets) {
|
|
8722
9036
|
this.gl.useProgram(target.program);
|
|
8723
9037
|
this.writeMeshPassState(target.uniforms, camera, env);
|
|
@@ -8871,11 +9185,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8871
9185
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
8872
9186
|
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
8873
9187
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
9188
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
9189
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
8874
9190
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
8875
9191
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
8876
9192
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
8877
9193
|
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
8878
|
-
|
|
9194
|
+
/* White, and no light through from behind: no material at a pass's start. */
|
|
9195
|
+
gl.uniform4f(u['uGlassTint'] ?? null, 1, 1, 1, this.diffuseTransmission);
|
|
8879
9196
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
8880
9197
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
8881
9198
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -8942,10 +9259,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8942
9259
|
this.surfaceFog = FOG_RECEDE;
|
|
8943
9260
|
/* Grain on by default, so every scene written before it was a choice looks unchanged. */
|
|
8944
9261
|
gl.uniform1f(u['uGrain'] ?? null, 1);
|
|
8945
|
-
/*
|
|
8946
|
-
|
|
8947
|
-
this.
|
|
8948
|
-
gl.uniform2f(u['uWriteMode'] ?? null, 0, 0);
|
|
9262
|
+
/* The weighting and the dither as they stand, which `bindMeshPass` has just cleared: a program
|
|
9263
|
+
built in the middle of a pass takes what the caller set. See `setDitherFade`. */
|
|
9264
|
+
this.writeMode(u);
|
|
8949
9265
|
/* The draw's own ambient as it stands, which `bindMeshPass` has just cleared: a program built
|
|
8950
9266
|
in the middle of a pass takes what the caller set. See `setAmbientSH`. */
|
|
8951
9267
|
gl.uniform4fv(u['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
@@ -8993,6 +9309,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8993
9309
|
gl.uniform1f(u['uShadowStrength'] ?? null, this.sunShadows === null ? 0 : env.shadowStrength);
|
|
8994
9310
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
8995
9311
|
gl.uniform1f(u['uShadowDepthSpan'] ?? null, env.shadowDepthSpan);
|
|
9312
|
+
/* The moving layer's own matrix and span, where the program reads one: see `movingLightFor`. */
|
|
9313
|
+
const moving = this.movingLightFor(env);
|
|
9314
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, moving);
|
|
9315
|
+
gl.uniform1f(u['uMovingShadowDepthSpan'] ?? null, orthographicDepthSpan(moving));
|
|
8996
9316
|
gl.uniform1f(u['uShadowMaxDistance'] ?? null, this.quality.directionalShadowMaxDistance);
|
|
8997
9317
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
8998
9318
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
@@ -9330,6 +9650,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9330
9650
|
* light and its colour, each into the blur's own target.
|
|
9331
9651
|
*/
|
|
9332
9652
|
const splits = this.skinSplits();
|
|
9653
|
+
/* A split skin draws its three halves or none, where a half may still be compiling. */
|
|
9654
|
+
if (splits && this.parallelCompile !== null && !this.skinHalvesReady(mesh)) return;
|
|
9333
9655
|
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
9334
9656
|
const keeps = this.reflectionKeeps();
|
|
9335
9657
|
if (keeps) this.reflectionTargets?.on();
|
|
@@ -9410,14 +9732,25 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9410
9732
|
}
|
|
9411
9733
|
|
|
9412
9734
|
/** One draw of a mesh, as `drawMesh` describes, of the half of a skin `half` names. */
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
const
|
|
9735
|
+
/**
|
|
9736
|
+
* Whether every half a split skin draws has its program, starting any that has not: a skin drawn
|
|
9737
|
+
* without its kept halves would lose its diffuse light for the frames they take to compile.
|
|
9738
|
+
*/
|
|
9739
|
+
private skinHalvesReady(mesh: Mesh): boolean {
|
|
9740
|
+
const variant = this.drawVariantOf(mesh).name;
|
|
9741
|
+
let ready = true;
|
|
9742
|
+
for (const half of SPLIT_SKIN_HALVES) {
|
|
9743
|
+
if (this.modelProgram(variant, 'skin', half) === null) ready = false;
|
|
9744
|
+
}
|
|
9745
|
+
return ready;
|
|
9746
|
+
}
|
|
9747
|
+
|
|
9748
|
+
/**
|
|
9749
|
+
* The vertex variant a draw of `mesh` takes now, and its program and uniforms where it is not the
|
|
9750
|
+
* plain one: what a model's program is keyed by, and what `drawMeshHalf` draws with.
|
|
9751
|
+
*/
|
|
9752
|
+
private drawVariantOf(mesh: Mesh): DrawVariant {
|
|
9753
|
+
const out = this.drawVariant;
|
|
9421
9754
|
/*
|
|
9422
9755
|
* A rigged mesh takes the skinned program, an unrigged one the plain program.
|
|
9423
9756
|
*
|
|
@@ -9445,10 +9778,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9445
9778
|
* when none were asked for — an unbound sampler would default to a shadow map's unit.
|
|
9446
9779
|
*/
|
|
9447
9780
|
const cloth = wantSkin ? this.cloth : null;
|
|
9448
|
-
const
|
|
9449
|
-
|
|
9450
|
-
? undefined
|
|
9451
|
-
: this.clothFlat.get(`${eight ? 8 : 4}|${mesh.morph !== null ? 'm' : '-'}`);
|
|
9781
|
+
const clothKey = `${eight ? 8 : 4}|${mesh.morph !== null ? 'm' : '-'}`;
|
|
9782
|
+
const clothed = cloth === null ? undefined : this.clothFlat.get(clothKey);
|
|
9452
9783
|
const bothUniforms = eight ? this.flatBoth8Uniforms : this.flatBothUniforms;
|
|
9453
9784
|
const skinnedUniforms = eight ? this.flatSkinned8Uniforms : this.flatSkinnedUniforms;
|
|
9454
9785
|
const both = wantSkin && wantMorph && bothUniforms !== null;
|
|
@@ -9462,8 +9793,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9462
9793
|
: morphed
|
|
9463
9794
|
? this.flatMorphedUniforms
|
|
9464
9795
|
: null;
|
|
9465
|
-
|
|
9466
|
-
|
|
9796
|
+
out.skinned = skinned;
|
|
9797
|
+
out.morphed = morphed;
|
|
9798
|
+
out.cloth = cloth;
|
|
9799
|
+
out.clothed = clothed;
|
|
9800
|
+
out.uniforms = clothed?.uniforms ?? variant ?? this.flatUniforms;
|
|
9801
|
+
out.program =
|
|
9467
9802
|
clothed !== undefined
|
|
9468
9803
|
? clothed.program
|
|
9469
9804
|
: skinned && morphed
|
|
@@ -9477,26 +9812,41 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9477
9812
|
: morphed
|
|
9478
9813
|
? this.flatMorphedProgram
|
|
9479
9814
|
: null;
|
|
9815
|
+
out.name =
|
|
9816
|
+
clothed !== undefined
|
|
9817
|
+
? `cloth${eight ? 8 : 4}${mesh.morph !== null ? 'm' : '-'}`
|
|
9818
|
+
: skinned && morphed
|
|
9819
|
+
? eight
|
|
9820
|
+
? 'both8'
|
|
9821
|
+
: 'both'
|
|
9822
|
+
: skinned
|
|
9823
|
+
? eight
|
|
9824
|
+
? 'skinned8'
|
|
9825
|
+
: 'skinned'
|
|
9826
|
+
: morphed
|
|
9827
|
+
? 'morphed'
|
|
9828
|
+
: 'plain';
|
|
9829
|
+
return out;
|
|
9830
|
+
}
|
|
9831
|
+
|
|
9832
|
+
private drawMeshHalf(
|
|
9833
|
+
mesh: Mesh,
|
|
9834
|
+
model: ReadonlyMat4,
|
|
9835
|
+
depthLayer: number,
|
|
9836
|
+
tint: Vec3 | null,
|
|
9837
|
+
half: SkinHalf,
|
|
9838
|
+
): void {
|
|
9839
|
+
const { gl } = this;
|
|
9840
|
+
const drawn = this.drawVariantOf(mesh);
|
|
9841
|
+
const { skinned, morphed, cloth, clothed } = drawn;
|
|
9842
|
+
let u = drawn.uniforms;
|
|
9843
|
+
let program = drawn.program;
|
|
9480
9844
|
/* A material with a model takes that model's program for the same vertex variant. */
|
|
9481
9845
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
9482
9846
|
if (modelKind !== null) {
|
|
9483
|
-
const modelled = this.modelProgram(
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
: skinned && morphed
|
|
9487
|
-
? eight
|
|
9488
|
-
? 'both8'
|
|
9489
|
-
: 'both'
|
|
9490
|
-
: skinned
|
|
9491
|
-
? eight
|
|
9492
|
-
? 'skinned8'
|
|
9493
|
-
: 'skinned'
|
|
9494
|
-
: morphed
|
|
9495
|
-
? 'morphed'
|
|
9496
|
-
: 'plain',
|
|
9497
|
-
modelKind,
|
|
9498
|
-
half,
|
|
9499
|
-
);
|
|
9847
|
+
const modelled = this.modelProgram(drawn.name, modelKind, half);
|
|
9848
|
+
/* Still compiling, where draws skip a compile: left out until it lands. */
|
|
9849
|
+
if (modelled === null) return;
|
|
9500
9850
|
program = modelled.program;
|
|
9501
9851
|
u = modelled.uniforms;
|
|
9502
9852
|
}
|
|
@@ -9664,6 +10014,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9664
10014
|
/* A material with a model takes that model's plain program; see `modelProgram`. */
|
|
9665
10015
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
9666
10016
|
const modelled = modelKind === null ? null : this.modelProgram('plain', modelKind);
|
|
10017
|
+
/* Still compiling, where draws skip a compile: left out until it lands. */
|
|
10018
|
+
if (modelKind !== null && modelled === null) return;
|
|
9667
10019
|
const u = modelled?.uniforms ?? this.flatUniforms;
|
|
9668
10020
|
if (modelled === null) this.useFlatProgram();
|
|
9669
10021
|
else gl.useProgram(modelled.program);
|
|
@@ -10195,6 +10547,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
10195
10547
|
/* Uncorrected, for the reason the lit program's copy of this line carries: the shaft's
|
|
10196
10548
|
lookup does its own `* 0.5 + 0.5` and correcting the matrix as well doubles it. */
|
|
10197
10549
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, env.lightViewProj);
|
|
10550
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, this.movingLightFor(env));
|
|
10198
10551
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
10199
10552
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
10200
10553
|
|