@driftengine/core 4.9.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +133 -11
- package/README.md +43 -38
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -4
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/backend/api.d.ts +10 -3
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +189 -4
- package/dist/render/backend/webgl2/renderer.js +614 -66
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +24 -4
- package/dist/render/backend/webgpu/buffers.js +116 -59
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/drawCommand.d.ts +20 -4
- package/dist/render/backend/webgpu/drawCommand.js +11 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +137 -20
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
- package/dist/render/backend/webgpu/pipelineCache.js +46 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +200 -35
- package/dist/render/backend/webgpu/renderer.js +993 -168
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +242 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +14 -0
- package/dist/render/mesh.js +44 -14
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +21 -0
- package/dist/render/shaders/flat/index.js +42 -7
- package/dist/render/shaders/flat/layered.d.ts +19 -0
- package/dist/render/shaders/flat/layered.js +115 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +58 -21
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +14 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +42 -20
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +59 -27
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3616 -3042
- package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceLayers.d.ts +39 -0
- package/dist/render/surfaceLayers.js +26 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +68 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/package.json +6 -6
- package/src/index.ts +12 -4
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +723 -79
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +195 -70
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +25 -4
- package/src/render/backend/webgpu/flatPass.ts +212 -41
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +53 -7
- package/src/render/backend/webgpu/renderer.ts +1201 -202
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +364 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +72 -14
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +63 -7
- package/src/render/shaders/flat/layered.ts +115 -0
- package/src/render/shaders/flat/main.ts +58 -21
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +14 -3
- package/src/render/shaders/flat/overlay.ts +42 -20
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +59 -27
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceLayers.ts +57 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +124 -27
- package/src/render/vertexPacking.ts +109 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -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';
|
|
@@ -218,6 +224,8 @@ import {
|
|
|
218
224
|
DEPTH_CUTOUT_FRAG,
|
|
219
225
|
DEPTH_CUTOUT_VERT,
|
|
220
226
|
DEPTH_FRAG,
|
|
227
|
+
DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT,
|
|
228
|
+
DEPTH_INSTANCED_ANIMATED_VERT,
|
|
221
229
|
DEPTH_INSTANCED_CUTOUT_VERT,
|
|
222
230
|
DEPTH_INSTANCED_VERT,
|
|
223
231
|
DEPTH_SKINNED_VERT,
|
|
@@ -232,6 +240,8 @@ import { GlassCasterList, type GlassReplaySink } from '../../glassCasters.ts';
|
|
|
232
240
|
import { SunGlassTint } from '../../sunGlassTint.ts';
|
|
233
241
|
import { GlassTintPrograms, type GlassTintProgram } from './glassTintPrograms.ts';
|
|
234
242
|
import type { SceneCasterMaterial, ShadowCasterSink, ShadowCasters } from '../../shadowCasters.ts';
|
|
243
|
+
import { captureStaticDraws, replayStaticDraws } from '../../staticDraws.ts';
|
|
244
|
+
import type { StaticDrawsHandle, StaticDrawsList } from '../../staticDraws.ts';
|
|
235
245
|
import { FACE_COUNT, PointShadowMap, type PointGlassDraw } from '../../pointShadowMap.ts';
|
|
236
246
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.ts';
|
|
237
247
|
import {
|
|
@@ -277,7 +287,11 @@ import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
|
277
287
|
import { sceneCaptureTexels } from '../../sceneCapture.ts';
|
|
278
288
|
import { GlSceneCaptureTarget } from './sceneCaptureTarget.ts';
|
|
279
289
|
import { glCompressedFormats } from '../../glCompressed.ts';
|
|
280
|
-
import type {
|
|
290
|
+
import type {
|
|
291
|
+
CompressedTextureFormat,
|
|
292
|
+
CompressedTextureSource,
|
|
293
|
+
SurfaceSource,
|
|
294
|
+
} from '../../compressedSource.ts';
|
|
281
295
|
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
282
296
|
import { PlumeRenderer } from '../../plumeRenderer.ts';
|
|
283
297
|
import { createScatterDeform, resolveScatterDeform } from '../../scatterDeform.ts';
|
|
@@ -335,6 +349,14 @@ import type { WindStreakOptions } from '../../windStreakRenderer.ts';
|
|
|
335
349
|
import type { WindField } from '../../windField.ts';
|
|
336
350
|
import type { FlockParams } from '../../flockRenderer.ts';
|
|
337
351
|
import { InstancedBatch } from './instanced.ts';
|
|
352
|
+
import { GlBoneAnimation } from './boneAnimations.ts';
|
|
353
|
+
import { packBoneAnimation } from '../../boneAnimation.ts';
|
|
354
|
+
import type { BoneAnimationClip, BoneAnimationHandle } from '../../boneAnimation.ts';
|
|
355
|
+
import {
|
|
356
|
+
BONE_PLACES_TEXTURE_UNIT,
|
|
357
|
+
BONE_TURNS_TEXTURE_UNIT,
|
|
358
|
+
INSTANCE_CLOCKS_TEXTURE_UNIT,
|
|
359
|
+
} from '../../lightBudget.ts';
|
|
338
360
|
import type { InstancedOptions, MeshInstances } from '../../instances.ts';
|
|
339
361
|
import { cutoutOf } from '../../cutoutCaster.ts';
|
|
340
362
|
import { CUTOUT_RESOLVE_CODE, resolveCutout } from '../../cutoutDither.ts';
|
|
@@ -1033,6 +1055,37 @@ const IDENTITY_CLIP = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0
|
|
|
1033
1055
|
/** Standing still, for a caller that asked for dust and not for wind. */
|
|
1034
1056
|
const NO_DRIFT: Vec3 = [0, 0, 0];
|
|
1035
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
|
+
|
|
1036
1089
|
export class WebGL2Renderer implements RendererApi {
|
|
1037
1090
|
/**
|
|
1038
1091
|
* Private on purpose. The GL context never leaves this directory, so a
|
|
@@ -1331,6 +1384,23 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1331
1384
|
string,
|
|
1332
1385
|
{ program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> }
|
|
1333
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;
|
|
1334
1404
|
/** The last palette chosen, which an eye's axis is turned by. See `setSkinPalette`. */
|
|
1335
1405
|
private paletteData: Float32Array | null = null;
|
|
1336
1406
|
|
|
@@ -1394,7 +1464,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1394
1464
|
private oitReplaying = false;
|
|
1395
1465
|
/** `uSurfaceScene`, resolved once a pass. See `surfaceScene.ts`. */
|
|
1396
1466
|
private readonly surfaceScene = new Float32Array(4);
|
|
1397
|
-
/** The
|
|
1467
|
+
/** The three lanes of `uWriteMode`, kept here because a `vec3` is written whole. */
|
|
1398
1468
|
private oitWeight = 0;
|
|
1399
1469
|
/**
|
|
1400
1470
|
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
@@ -1402,6 +1472,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1402
1472
|
*/
|
|
1403
1473
|
private surfaceFog = FOG_RECEDE;
|
|
1404
1474
|
private ditherFade = 0;
|
|
1475
|
+
private ditherOpacity = 0;
|
|
1405
1476
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
1406
1477
|
private oitRefused = false;
|
|
1407
1478
|
/** Said once for the whole renderer. See `refuseOitMultisampled`. */
|
|
@@ -1582,6 +1653,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1582
1653
|
private readonly depthCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1583
1654
|
private readonly depthInstancedCutoutProgram: WebGLProgram;
|
|
1584
1655
|
private readonly depthInstancedCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1656
|
+
/** A crowd's casters: the instanced depth programs that play a bone animation. */
|
|
1657
|
+
private readonly depthAnimatedProgram: WebGLProgram;
|
|
1658
|
+
private readonly depthAnimatedUniforms: Record<string, WebGLUniformLocation>;
|
|
1659
|
+
private readonly depthAnimatedCutoutProgram: WebGLProgram;
|
|
1660
|
+
private readonly depthAnimatedCutoutUniforms: Record<string, WebGLUniformLocation>;
|
|
1585
1661
|
private readonly scatterDepthProgram: WebGLProgram;
|
|
1586
1662
|
private readonly scatterDepthUniforms: Record<string, WebGLUniformLocation>;
|
|
1587
1663
|
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
@@ -1676,6 +1752,28 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
1676
1752
|
private overlayTexture: WebGLTexture | null = null;
|
|
1677
1753
|
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
1678
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;
|
|
1679
1777
|
private warnedOverlayRoom = false;
|
|
1680
1778
|
private warnedOverlayBlocks = false;
|
|
1681
1779
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -2002,24 +2100,33 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2002
2100
|
const gpuBatch = batch as InstancedBatch;
|
|
2003
2101
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
2004
2102
|
if (count === 0) return;
|
|
2103
|
+
const clip = gpuBatch.animation;
|
|
2104
|
+
if (clip !== null && clip.disposed) return;
|
|
2005
2105
|
this.shadowDrawBudget.ask();
|
|
2006
2106
|
const twoSided = material?.doubleSided === true;
|
|
2007
2107
|
if (twoSided) gl.disable(gl.CULL_FACE);
|
|
2008
2108
|
const cutout = cutoutOf(material);
|
|
2009
2109
|
if (cutout !== null) {
|
|
2010
|
-
|
|
2011
|
-
|
|
2110
|
+
/* A crowd casts where its clip has moved it, through the animated variant. */
|
|
2111
|
+
const uniforms =
|
|
2112
|
+
clip === null ? this.depthInstancedCutoutUniforms : this.depthAnimatedCutoutUniforms;
|
|
2113
|
+
gl.useProgram(
|
|
2114
|
+
clip === null ? this.depthInstancedCutoutProgram : this.depthAnimatedCutoutProgram,
|
|
2115
|
+
);
|
|
2116
|
+
this.bindCutoutDepth(uniforms, cutout);
|
|
2117
|
+
if (clip !== null) this.bindAnimation(uniforms, gpuBatch);
|
|
2012
2118
|
gpuBatch.draw(gl, count);
|
|
2013
2119
|
if (twoSided && this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
2014
2120
|
gl.useProgram(this.depthProgram);
|
|
2015
2121
|
this.restoreSurfaceTextureUnit();
|
|
2016
2122
|
return;
|
|
2017
2123
|
}
|
|
2018
|
-
const u = this.depthInstancedUniforms;
|
|
2019
|
-
gl.useProgram(this.depthInstancedProgram);
|
|
2124
|
+
const u = clip === null ? this.depthInstancedUniforms : this.depthAnimatedUniforms;
|
|
2125
|
+
gl.useProgram(clip === null ? this.depthInstancedProgram : this.depthAnimatedProgram);
|
|
2020
2126
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, this.activeDepthViewProj);
|
|
2021
2127
|
gl.uniform1i(u['uPeelShadowLayer'] ?? null, this.activeDepthPeel ? 1 : 0);
|
|
2022
2128
|
if (this.activeDepthPeel) gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
2129
|
+
if (clip !== null) this.bindAnimation(u, gpuBatch);
|
|
2023
2130
|
gpuBatch.draw(gl, count);
|
|
2024
2131
|
if (twoSided && this.depthPassCullsFaces) gl.enable(gl.CULL_FACE);
|
|
2025
2132
|
gl.useProgram(this.depthProgram);
|
|
@@ -2994,6 +3101,16 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
2994
3101
|
* Measured on an RX 9070 XT through ANGLE/Vulkan: offered. `scripts/depth-survey.mjs` is what
|
|
2995
3102
|
* asks, and it also reports what WebGPU can do.
|
|
2996
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;
|
|
2997
3114
|
this.clipControl = gl.getExtension('EXT_clip_control') as ClipControlExtension | null;
|
|
2998
3115
|
if (REVERSED_DEPTH && this.clipControl !== null) {
|
|
2999
3116
|
this.clipControl.clipControlEXT(
|
|
@@ -3182,10 +3299,33 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3182
3299
|
this.depthInstancedCutoutProgram,
|
|
3183
3300
|
'depth-instanced-cutout',
|
|
3184
3301
|
);
|
|
3302
|
+
this.depthAnimatedProgram = compileProgram(
|
|
3303
|
+
gl,
|
|
3304
|
+
DEPTH_INSTANCED_ANIMATED_VERT,
|
|
3305
|
+
DEPTH_FRAG,
|
|
3306
|
+
'depth-instanced-animated',
|
|
3307
|
+
);
|
|
3308
|
+
this.depthAnimatedUniforms = uniformLocations(
|
|
3309
|
+
gl,
|
|
3310
|
+
this.depthAnimatedProgram,
|
|
3311
|
+
'depth-instanced-animated',
|
|
3312
|
+
);
|
|
3313
|
+
this.depthAnimatedCutoutProgram = compileProgram(
|
|
3314
|
+
gl,
|
|
3315
|
+
DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT,
|
|
3316
|
+
DEPTH_CUTOUT_FRAG,
|
|
3317
|
+
'depth-instanced-animated-cutout',
|
|
3318
|
+
);
|
|
3319
|
+
this.depthAnimatedCutoutUniforms = uniformLocations(
|
|
3320
|
+
gl,
|
|
3321
|
+
this.depthAnimatedCutoutProgram,
|
|
3322
|
+
'depth-instanced-animated-cutout',
|
|
3323
|
+
);
|
|
3185
3324
|
/* The cutout map is always read from the surface unit, which the draw borrows and gives back. */
|
|
3186
3325
|
for (const [program, uniforms] of [
|
|
3187
3326
|
[this.depthCutoutProgram, this.depthCutoutUniforms],
|
|
3188
3327
|
[this.depthInstancedCutoutProgram, this.depthInstancedCutoutUniforms],
|
|
3328
|
+
[this.depthAnimatedCutoutProgram, this.depthAnimatedCutoutUniforms],
|
|
3189
3329
|
] as const) {
|
|
3190
3330
|
gl.useProgram(program);
|
|
3191
3331
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
@@ -3308,7 +3448,36 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3308
3448
|
* this before its first frame gets the right behaviour on both without asking which backend
|
|
3309
3449
|
* it has, which is the contract `RendererApi` exists to keep.
|
|
3310
3450
|
*/
|
|
3311
|
-
|
|
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
|
+
}
|
|
3312
3481
|
|
|
3313
3482
|
dispose(options: { releaseContext?: boolean } = {}): void {
|
|
3314
3483
|
if (this.disposed) return;
|
|
@@ -3379,6 +3548,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3379
3548
|
this.lightVolumeProgram,
|
|
3380
3549
|
this.depthCutoutProgram,
|
|
3381
3550
|
this.depthInstancedCutoutProgram,
|
|
3551
|
+
this.depthAnimatedProgram,
|
|
3552
|
+
this.depthAnimatedCutoutProgram,
|
|
3382
3553
|
]) {
|
|
3383
3554
|
gl.deleteProgram(program);
|
|
3384
3555
|
}
|
|
@@ -3725,9 +3896,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3725
3896
|
*
|
|
3726
3897
|
* Not a hot path — it re-uploads the whole image and regenerates the mip chain.
|
|
3727
3898
|
*/
|
|
3728
|
-
updateSurfaceTexture(
|
|
3899
|
+
updateSurfaceTexture(
|
|
3900
|
+
texture: SurfaceTexture,
|
|
3901
|
+
source: TexImageSource | CompressedTextureSource,
|
|
3902
|
+
): void {
|
|
3729
3903
|
if (this.contextLost) return;
|
|
3730
|
-
texture.update(this.gl, source);
|
|
3904
|
+
texture.update(this.gl, source, this.compressedFormats);
|
|
3731
3905
|
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
3732
3906
|
if (texture === this.overlayMaps) this.bindOverlayMaps(texture);
|
|
3733
3907
|
}
|
|
@@ -3787,18 +3961,43 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3787
3961
|
kind: SurfaceModelKind,
|
|
3788
3962
|
/** Which half of a skin it draws under the screen-space blur. See `SkinHalf`. */
|
|
3789
3963
|
half: SkinHalf = 'whole',
|
|
3790
|
-
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } {
|
|
3964
|
+
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } | null {
|
|
3791
3965
|
const key = `${variant}|${kind}|${half}`;
|
|
3792
3966
|
const held = this.modelFlat.get(key);
|
|
3793
3967
|
if (held !== undefined) return held;
|
|
3794
3968
|
const { gl } = this;
|
|
3795
3969
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
3796
|
-
const
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
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;
|
|
3802
4001
|
const made = { program, uniforms: uniformLocations(gl, program, label) };
|
|
3803
4002
|
this.modelFlat.set(key, made);
|
|
3804
4003
|
this.flatTargets.push(made);
|
|
@@ -3964,6 +4163,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3964
4163
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
3965
4164
|
this.materialHasMaps = materialHasMaps(material);
|
|
3966
4165
|
this.currentMaterial = material;
|
|
4166
|
+
const reflectivity = material?.reflectivity;
|
|
4167
|
+
const environmentGain = material?.environmentGain;
|
|
4168
|
+
this.materialReflectivity =
|
|
4169
|
+
reflectivity === undefined ? null : Math.min(1, Math.max(0, reflectivity));
|
|
4170
|
+
this.materialEnvironmentGain =
|
|
4171
|
+
environmentGain === undefined ? null : Math.max(0, environmentGain);
|
|
4172
|
+
this.writeEnvironmentDials();
|
|
3967
4173
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
3968
4174
|
const model = material?.model;
|
|
3969
4175
|
warnUnpagedLightmap(model, material?.modelMap);
|
|
@@ -4011,7 +4217,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4011
4217
|
const v = this.cutoutVector;
|
|
4012
4218
|
v[0] = material?.cutout ?? 0;
|
|
4013
4219
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
4014
|
-
|
|
4220
|
+
/* z unused: the dither's frame is the pass's, in `uHighlightMin.w`. See `uCutout`. */
|
|
4221
|
+
v[2] = 0;
|
|
4015
4222
|
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
4016
4223
|
v[3] = lightChannelsOf(material?.lightChannels);
|
|
4017
4224
|
return v;
|
|
@@ -4116,9 +4323,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4116
4323
|
* and read neither — one decision for every program, as `setMaterial` says.
|
|
4117
4324
|
*/
|
|
4118
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;
|
|
4119
4329
|
gl.activeTexture(gl.TEXTURE0 + MODEL_MAP_TEXTURE_UNIT);
|
|
4120
4330
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
4121
|
-
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);
|
|
4122
4335
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
4123
4336
|
packModel(
|
|
4124
4337
|
material?.model ?? null,
|
|
@@ -4131,6 +4344,15 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4131
4344
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
4132
4345
|
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
4133
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);
|
|
4134
4356
|
|
|
4135
4357
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
4136
4358
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -4225,8 +4447,24 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4225
4447
|
*/
|
|
4226
4448
|
setSurfaceReflectivity(amount: number): void {
|
|
4227
4449
|
if (this.contextLost) return;
|
|
4450
|
+
this.passReflectivity = Math.min(1, Math.max(0, amount));
|
|
4451
|
+
this.writeEnvironmentDials();
|
|
4452
|
+
}
|
|
4453
|
+
|
|
4454
|
+
/**
|
|
4455
|
+
* The reflectivity and environment gain the setters gave the pass, and the ones the material
|
|
4456
|
+
* set now states, or null where it states none (`SurfaceMaterial.reflectivity`). A draw wears the
|
|
4457
|
+
* material's where it has one and the pass's otherwise.
|
|
4458
|
+
*/
|
|
4459
|
+
private passReflectivity = 0;
|
|
4460
|
+
private passEnvironmentGain = 1;
|
|
4461
|
+
private materialReflectivity: number | null = null;
|
|
4462
|
+
private materialEnvironmentGain: number | null = null;
|
|
4463
|
+
|
|
4464
|
+
private writeEnvironmentDials(): void {
|
|
4228
4465
|
this.materials.dirty();
|
|
4229
|
-
this.environmentDials[1] =
|
|
4466
|
+
this.environmentDials[1] = this.materialReflectivity ?? this.passReflectivity;
|
|
4467
|
+
this.environmentDials[2] = this.materialEnvironmentGain ?? this.passEnvironmentGain;
|
|
4230
4468
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
4231
4469
|
}
|
|
4232
4470
|
|
|
@@ -4289,17 +4527,110 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4289
4527
|
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
4290
4528
|
private overlayRoomLeft(): number {
|
|
4291
4529
|
return (
|
|
4292
|
-
this.fragmentVectorLimit() -
|
|
4530
|
+
this.fragmentVectorLimit() -
|
|
4531
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, { SURFACE_OVERLAY: false }))
|
|
4293
4532
|
);
|
|
4294
4533
|
}
|
|
4295
4534
|
|
|
4296
4535
|
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
4297
4536
|
private overlayFits(): boolean {
|
|
4298
|
-
this.overlayRoom ??=
|
|
4299
|
-
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
4537
|
+
this.overlayRoom ??= this.switchFits('SURFACE_OVERLAY');
|
|
4300
4538
|
return this.overlayRoom;
|
|
4301
4539
|
}
|
|
4302
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
|
+
|
|
4303
4634
|
/**
|
|
4304
4635
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
4305
4636
|
*
|
|
@@ -4314,9 +4645,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4314
4645
|
*/
|
|
4315
4646
|
setEnvironmentGain(gain: number): void {
|
|
4316
4647
|
if (this.contextLost) return;
|
|
4317
|
-
this.
|
|
4318
|
-
this.
|
|
4319
|
-
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
4648
|
+
this.passEnvironmentGain = Math.max(0, gain);
|
|
4649
|
+
this.writeEnvironmentDials();
|
|
4320
4650
|
}
|
|
4321
4651
|
|
|
4322
4652
|
/**
|
|
@@ -4444,9 +4774,37 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4444
4774
|
this.useFlatProgram();
|
|
4445
4775
|
}
|
|
4446
4776
|
|
|
4447
|
-
/**
|
|
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
|
+
*/
|
|
4448
4806
|
private writeMode(u: Record<string, WebGLUniformLocation>): void {
|
|
4449
|
-
this.gl.
|
|
4807
|
+
this.gl.uniform3f(u['uWriteMode'] ?? null, this.oitWeight, this.ditherFade, this.ditherOpacity);
|
|
4450
4808
|
}
|
|
4451
4809
|
|
|
4452
4810
|
/**
|
|
@@ -4599,8 +4957,104 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4599
4957
|
'locations the joint indices and weights occupy, and this mesh supplies them.',
|
|
4600
4958
|
);
|
|
4601
4959
|
}
|
|
4960
|
+
const animation = (options.animation ?? null) as GlBoneAnimation | null;
|
|
4961
|
+
/* The bone a vertex follows is its second coordinates' u, which a mesh carries in its grain
|
|
4962
|
+
lane; a mesh with none has nothing to name a bone by. See `boneAnimation.ts`. */
|
|
4963
|
+
if (animation !== null && !mesh.hasGrain) {
|
|
4964
|
+
throw new Error(
|
|
4965
|
+
'WebGL2: a bone animation reads each vertex’s bone from the mesh’s second coordinates ' +
|
|
4966
|
+
'(`MeshData.lightmapUvs`), and this mesh has none.',
|
|
4967
|
+
);
|
|
4968
|
+
}
|
|
4602
4969
|
this.ensureInstancedProgram();
|
|
4603
|
-
|
|
4970
|
+
if (animation !== null) this.ensureAnimatedProgram();
|
|
4971
|
+
return new InstancedBatch(this.gl, mesh, capacity, options.cull ?? false, animation);
|
|
4972
|
+
}
|
|
4973
|
+
|
|
4974
|
+
/**
|
|
4975
|
+
* A bone animation an instanced batch can play (`InstancedOptions.animation`): per frame, a turn
|
|
4976
|
+
* and a place for each bone, which every vertex of the batch's mesh follows — the bone its second
|
|
4977
|
+
* coordinates' u names, times `boneScale` — at each instance's own moment of the clip. What a
|
|
4978
|
+
* crowd is made of: every instance moves at the frame's rate on its own clock, in the colour pass
|
|
4979
|
+
* and in every shadow, with nothing on the processor after this. See `boneAnimation.ts` for the
|
|
4980
|
+
* clip's layout and the arithmetic, which both backends run.
|
|
4981
|
+
*
|
|
4982
|
+
* What it gives up against skinning is the blend: a vertex follows one bone. Refused, with what
|
|
4983
|
+
* was wrong, for a clip whose numbers are not its size, or past 2,048 bones or frames.
|
|
4984
|
+
*/
|
|
4985
|
+
createBoneAnimation(clip: BoneAnimationClip): BoneAnimationHandle {
|
|
4986
|
+
return new GlBoneAnimation(this.gl, packBoneAnimation(clip));
|
|
4987
|
+
}
|
|
4988
|
+
|
|
4989
|
+
/** Release a clip. A batch still playing it draws nothing, and casts nothing, after. */
|
|
4990
|
+
disposeBoneAnimation(animation: BoneAnimationHandle): void {
|
|
4991
|
+
const clip = animation as GlBoneAnimation;
|
|
4992
|
+
if (this.contextLost || clip.disposed) return;
|
|
4993
|
+
clip.dispose(this.gl);
|
|
4994
|
+
}
|
|
4995
|
+
|
|
4996
|
+
/**
|
|
4997
|
+
* The clock a bone animation plays by this frame, in seconds on the caller's clock: an instance's
|
|
4998
|
+
* moment is this times its rate plus its phase (`MeshInstances.clocks`). **Set it before the
|
|
4999
|
+
* shadows are drawn**, once a frame, as the wind is set: a shadow drawn under one clock and its
|
|
5000
|
+
* caster under another come apart at the frame's rate. 0 until set, which holds every crowd at
|
|
5001
|
+
* the moment its phases name.
|
|
5002
|
+
*/
|
|
5003
|
+
setAnimationTime(seconds: number): void {
|
|
5004
|
+
if (Number.isFinite(seconds)) this.animationTime = seconds;
|
|
5005
|
+
}
|
|
5006
|
+
|
|
5007
|
+
/** The clock `setAnimationTime` set. */
|
|
5008
|
+
private animationTime = 0;
|
|
5009
|
+
|
|
5010
|
+
/**
|
|
5011
|
+
* An animated batch's clip, clocks and numbers into whichever program is bound: the three vertex
|
|
5012
|
+
* textures on units of their own (`lightBudget.ts`), the clip's speed and bone scale, the clock.
|
|
5013
|
+
*/
|
|
5014
|
+
private bindAnimation(u: Record<string, WebGLUniformLocation>, batch: InstancedBatch): void {
|
|
5015
|
+
const clip = batch.animation;
|
|
5016
|
+
const clocks = batch.clocks;
|
|
5017
|
+
if (clip === null || clocks === null) return;
|
|
5018
|
+
const { gl } = this;
|
|
5019
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_PLACES_TEXTURE_UNIT);
|
|
5020
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.places);
|
|
5021
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_TURNS_TEXTURE_UNIT);
|
|
5022
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.turns);
|
|
5023
|
+
gl.activeTexture(gl.TEXTURE0 + INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
5024
|
+
gl.bindTexture(gl.TEXTURE_2D, clocks.texture);
|
|
5025
|
+
gl.uniform1i(u['uBonePlaces'] ?? null, BONE_PLACES_TEXTURE_UNIT);
|
|
5026
|
+
gl.uniform1i(u['uBoneTurns'] ?? null, BONE_TURNS_TEXTURE_UNIT);
|
|
5027
|
+
gl.uniform1i(u['uInstanceClocks'] ?? null, INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
5028
|
+
gl.uniform2f(u['uBoneClip'] ?? null, clip.framesPerSecond, clip.boneScale);
|
|
5029
|
+
gl.uniform1f(u['uSceneTime'] ?? null, this.animationTime);
|
|
5030
|
+
}
|
|
5031
|
+
|
|
5032
|
+
/** The animated instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
5033
|
+
private flatAnimatedProgram: WebGLProgram | null = null;
|
|
5034
|
+
private flatAnimatedUniforms: Record<string, WebGLUniformLocation> | null = null;
|
|
5035
|
+
|
|
5036
|
+
/** Compile the animated variant, once, and feed it this frame's pass state. */
|
|
5037
|
+
private ensureAnimatedProgram(): void {
|
|
5038
|
+
if (this.contextLost || this.flatAnimatedProgram !== null) return;
|
|
5039
|
+
const { gl } = this;
|
|
5040
|
+
const program = compileProgram(
|
|
5041
|
+
gl,
|
|
5042
|
+
flatVert({ skinned: false, morphed: false, instanced: true, animated: true }),
|
|
5043
|
+
this.flatFragSource,
|
|
5044
|
+
'flat.animated',
|
|
5045
|
+
);
|
|
5046
|
+
const uniforms = uniformLocations(gl, program, 'flat.animated');
|
|
5047
|
+
this.flatAnimatedProgram = program;
|
|
5048
|
+
this.flatAnimatedUniforms = uniforms;
|
|
5049
|
+
this.flatTargets.push({ program, uniforms });
|
|
5050
|
+
const camera = this.lastPassCamera;
|
|
5051
|
+
const env = this.lastPassEnv;
|
|
5052
|
+
if (camera !== null && env !== null) {
|
|
5053
|
+
gl.useProgram(program);
|
|
5054
|
+
this.writeMeshPassState(uniforms, camera, env);
|
|
5055
|
+
this.writeMaterialState(uniforms, this.currentMaterial);
|
|
5056
|
+
this.useFlatProgram();
|
|
5057
|
+
}
|
|
4604
5058
|
}
|
|
4605
5059
|
|
|
4606
5060
|
/** Push placement and colour. Only the live prefix. */
|
|
@@ -4663,18 +5117,31 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4663
5117
|
order. See `InstancedOptions.cull`. */
|
|
4664
5118
|
const culling = !blend && batch.cull;
|
|
4665
5119
|
if (culling && !batchBoxVisible(batch.box, this.frustum, this.occlusion)) return;
|
|
5120
|
+
/* A batch playing a bone animation takes the animated variant; a disposed clip draws nothing. */
|
|
5121
|
+
const clip = batch.animation;
|
|
5122
|
+
if (clip !== null && clip.disposed) return;
|
|
4666
5123
|
this.drawBudget.ask();
|
|
4667
5124
|
this.ensureInstancedProgram();
|
|
5125
|
+
if (clip !== null) this.ensureAnimatedProgram();
|
|
4668
5126
|
/* A material with a model takes that model's instanced program; see `modelProgram`. */
|
|
4669
5127
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
4670
|
-
const modelled =
|
|
4671
|
-
|
|
4672
|
-
|
|
5128
|
+
const modelled =
|
|
5129
|
+
modelKind === null
|
|
5130
|
+
? null
|
|
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;
|
|
5134
|
+
const program =
|
|
5135
|
+
modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
5136
|
+
const u =
|
|
5137
|
+
modelled?.uniforms ??
|
|
5138
|
+
(clip === null ? this.flatInstancedUniforms : this.flatAnimatedUniforms);
|
|
4673
5139
|
if (program === null || u === null) return;
|
|
4674
5140
|
|
|
4675
5141
|
const { gl } = this;
|
|
4676
5142
|
gl.useProgram(program);
|
|
4677
5143
|
gl.uniform1i(u['uHasTangents'] ?? null, mesh.hasTangents ? 1 : 0);
|
|
5144
|
+
if (clip !== null) this.bindAnimation(u, batch);
|
|
4678
5145
|
|
|
4679
5146
|
const lit = options.lit ?? true;
|
|
4680
5147
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
@@ -4737,8 +5204,13 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4737
5204
|
gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
|
|
4738
5205
|
const keeps = !blend && this.reflectionKeeps();
|
|
4739
5206
|
if (keeps) this.reflectionTargets?.on();
|
|
4740
|
-
|
|
4741
|
-
|
|
5207
|
+
/* An animated batch reads its clocks by instance index, which the culled copy would reorder:
|
|
5208
|
+
it is culled whole and never instance by instance. */
|
|
5209
|
+
if (culling && clip === null && cullsInstances(count, mesh.indexCount)) {
|
|
5210
|
+
batch.drawCulled(gl, data, this.frustum);
|
|
5211
|
+
} else {
|
|
5212
|
+
batch.draw(gl, count);
|
|
5213
|
+
}
|
|
4742
5214
|
if (keeps) this.reflectionTargets?.off();
|
|
4743
5215
|
if (coverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
4744
5216
|
if (ditheredBlend)
|
|
@@ -4800,8 +5272,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
4800
5272
|
glass.transmission,
|
|
4801
5273
|
glass.frost,
|
|
4802
5274
|
);
|
|
4803
|
-
|
|
4804
|
-
|
|
5275
|
+
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
5276
|
+
const tint = options.refractTint ?? WHITE_TINT;
|
|
5277
|
+
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
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
|
+
);
|
|
4805
5286
|
return true;
|
|
4806
5287
|
}
|
|
4807
5288
|
|
|
@@ -5949,6 +6430,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
5949
6430
|
this.shadowPassActive = true;
|
|
5950
6431
|
this.activeSunLayer = layer;
|
|
5951
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
|
+
}
|
|
5952
6438
|
/* The static layer opens first, so a frame's peel counts from here. See `peelFilled`. */
|
|
5953
6439
|
if (layer === 'static') this.peelFilled = false;
|
|
5954
6440
|
/*
|
|
@@ -6111,6 +6597,71 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6111
6597
|
casters(this.casterSink);
|
|
6112
6598
|
}
|
|
6113
6599
|
|
|
6600
|
+
/**
|
|
6601
|
+
* Draws that do not change between frames, enumerated once and kept: what a stage, a set or any
|
|
6602
|
+
* world that stands still draws, recorded so a frame replays it rather than issuing it.
|
|
6603
|
+
*
|
|
6604
|
+
* **The same enumeration `drawSceneCasters` takes**, called once, here: rigid meshes and instanced
|
|
6605
|
+
* batches, each with its material. A skinned mesh or a scatter batch is refused by name, since
|
|
6606
|
+
* both change every frame. What it captures is what the enumeration said at the time — the
|
|
6607
|
+
* matrices and the instance counts are copied, so a caller's scratch is free to move on — while
|
|
6608
|
+
* the meshes, batches, textures and materials stay the caller's: a list must be disposed, or made
|
|
6609
|
+
* again, before anything it holds is, and made again when a material it holds is changed.
|
|
6610
|
+
*
|
|
6611
|
+
* **What it is for is the frame's processor time.** WebGPU records the list into a render bundle
|
|
6612
|
+
* per view and replays it with one call, where a draw issued a frame at a time costs its encoding
|
|
6613
|
+
* every frame and again for every view that draws it. This backend has no bundles and replays the
|
|
6614
|
+
* entries through the scene-caster path every frame, at the cost a frame drawing them itself pays;
|
|
6615
|
+
* the picture is the same.
|
|
6616
|
+
*
|
|
6617
|
+
* What a list gives up against drawing each frame is culling: every entry is drawn in every view,
|
|
6618
|
+
* because a bundle cannot leave one out. A world split into lists by region culls by list.
|
|
6619
|
+
*/
|
|
6620
|
+
createStaticDraws(casters: ShadowCasters): StaticDrawsHandle {
|
|
6621
|
+
return captureStaticDraws(casters);
|
|
6622
|
+
}
|
|
6623
|
+
|
|
6624
|
+
/**
|
|
6625
|
+
* Draw a list into the open mesh pass, under the camera, the lights and the fog `bindMeshPass`
|
|
6626
|
+
* set — the frame's, a mirror's, a capture's. Opaque draws, through each entry's material.
|
|
6627
|
+
*
|
|
6628
|
+
* **Its draws are rigid and wear no overlay**, so a skin palette, morph weights, cloth or a surface
|
|
6629
|
+
* overlay set before it is put back to none, as `bindMeshPass` puts an overlay back; and no
|
|
6630
|
+
* material is left set after it, which is what `setMaterial(null)` leaves. The state a pass set —
|
|
6631
|
+
* the grain, the relief, the fog, the grade — is the list's to draw with, as it is any draw's, and
|
|
6632
|
+
* a list drawn under a different one is recorded again for it on WebGPU.
|
|
6633
|
+
*/
|
|
6634
|
+
drawStaticDraws(draws: StaticDrawsHandle): void {
|
|
6635
|
+
if (this.contextLost) return;
|
|
6636
|
+
const list = draws as StaticDrawsList;
|
|
6637
|
+
if (list.disposed) {
|
|
6638
|
+
this.warnStaticDisposed();
|
|
6639
|
+
return;
|
|
6640
|
+
}
|
|
6641
|
+
this.setSkinPalette(null);
|
|
6642
|
+
this.setMorphWeights(null);
|
|
6643
|
+
this.setCloth(null);
|
|
6644
|
+
this.setSurfaceOverlay(null);
|
|
6645
|
+
this.setMaterial(null);
|
|
6646
|
+
this.sceneCasterMaterial = undefined;
|
|
6647
|
+
replayStaticDraws(list, this.sceneCasterSink);
|
|
6648
|
+
this.setMaterial(null);
|
|
6649
|
+
this.sceneCasterMaterial = undefined;
|
|
6650
|
+
}
|
|
6651
|
+
|
|
6652
|
+
/** Release a list. Its meshes, batches and materials are the caller's and are not released. */
|
|
6653
|
+
disposeStaticDraws(draws: StaticDrawsHandle): void {
|
|
6654
|
+
(draws as StaticDrawsList).disposed = true;
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
/** A disposed list drawn, said once: the frame loop does not throw. */
|
|
6658
|
+
private warnStaticDisposed(): void {
|
|
6659
|
+
if (this.warnedStaticDisposed) return;
|
|
6660
|
+
this.warnedStaticDisposed = true;
|
|
6661
|
+
console.warn('drawStaticDraws: this list was disposed, and draws nothing.');
|
|
6662
|
+
}
|
|
6663
|
+
private warnedStaticDisposed = false;
|
|
6664
|
+
|
|
6114
6665
|
/**
|
|
6115
6666
|
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
6116
6667
|
*
|
|
@@ -6300,7 +6851,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6300
6851
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
6301
6852
|
*/
|
|
6302
6853
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
6303
|
-
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 {
|
|
6304
6859
|
return flatFrag({
|
|
6305
6860
|
pointShadows: this.quality.pointShadows,
|
|
6306
6861
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -6313,8 +6868,11 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6313
6868
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
6314
6869
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
6315
6870
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
6316
|
-
surfaceOverlay:
|
|
6871
|
+
surfaceOverlay: asIf.SURFACE_OVERLAY ?? this.litOn.SURFACE_OVERLAY,
|
|
6317
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,
|
|
6318
6876
|
maxLights: budget.maxLights,
|
|
6319
6877
|
maxAreaLights: budget.maxAreaLights,
|
|
6320
6878
|
});
|
|
@@ -6339,9 +6897,10 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6339
6897
|
instanced: boolean,
|
|
6340
6898
|
label: string,
|
|
6341
6899
|
eight = false,
|
|
6900
|
+
animated = false,
|
|
6342
6901
|
): { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation> } | null => {
|
|
6343
6902
|
if (old === null) return null;
|
|
6344
|
-
const vertex = flatVert({ skinned, morphed, instanced });
|
|
6903
|
+
const vertex = flatVert({ skinned, morphed, instanced, animated });
|
|
6345
6904
|
const program = compileProgram(
|
|
6346
6905
|
gl,
|
|
6347
6906
|
eight ? skinEight(vertex) : vertex,
|
|
@@ -6374,6 +6933,17 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6374
6933
|
const instanced = recompile(this.flatInstancedProgram, false, false, true, 'flat.instanced');
|
|
6375
6934
|
this.flatInstancedProgram = instanced?.program ?? null;
|
|
6376
6935
|
this.flatInstancedUniforms = instanced?.uniforms ?? null;
|
|
6936
|
+
const animated = recompile(
|
|
6937
|
+
this.flatAnimatedProgram,
|
|
6938
|
+
false,
|
|
6939
|
+
false,
|
|
6940
|
+
true,
|
|
6941
|
+
'flat.animated',
|
|
6942
|
+
false,
|
|
6943
|
+
true,
|
|
6944
|
+
);
|
|
6945
|
+
this.flatAnimatedProgram = animated?.program ?? null;
|
|
6946
|
+
this.flatAnimatedUniforms = animated?.uniforms ?? null;
|
|
6377
6947
|
|
|
6378
6948
|
for (const [key, entry] of this.clothFlat) {
|
|
6379
6949
|
const eight = key.startsWith('8');
|
|
@@ -6390,6 +6960,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6390
6960
|
this.clothFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
6391
6961
|
}
|
|
6392
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();
|
|
6393
6966
|
for (const [key, entry] of this.modelFlat) {
|
|
6394
6967
|
const [variant, kind, half] = key.split('|') as [string, SurfaceModelKind, SkinHalf];
|
|
6395
6968
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
@@ -6404,7 +6977,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
6404
6977
|
}
|
|
6405
6978
|
|
|
6406
6979
|
this.flatTargets.length = 0;
|
|
6407
|
-
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced]) {
|
|
6980
|
+
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced, animated]) {
|
|
6408
6981
|
if (target !== null) this.flatTargets.push(target);
|
|
6409
6982
|
}
|
|
6410
6983
|
for (const target of this.clothFlat.values()) this.flatTargets.push(target);
|
|
@@ -8178,6 +8751,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8178
8751
|
ambient: env?.ambient ?? [0, 0, 0],
|
|
8179
8752
|
sunShadow,
|
|
8180
8753
|
lightViewProj: env?.lightViewProj ?? IDENTITY_MAT4,
|
|
8754
|
+
movingLightViewProj: env === null ? IDENTITY_MAT4 : this.movingLightFor(env),
|
|
8181
8755
|
/* The placeholder rather than null where a map is merely absent, for the reason
|
|
8182
8756
|
`emptyTexture.ts` gives. It is black, which as a depth is an occluder at the light,
|
|
8183
8757
|
so it is safe only because `sunShadow` is 0 whenever these maps do not exist. */
|
|
@@ -8433,15 +9007,31 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8433
9007
|
this.currentSurfaceTexture = null;
|
|
8434
9008
|
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
8435
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);
|
|
8436
9016
|
this.overlayMaps = null;
|
|
8437
9017
|
this.overlayTexture = null;
|
|
8438
9018
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
8439
9019
|
this.ambientSH.fill(0);
|
|
8440
9020
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
9021
|
+
this.passReflectivity = 0;
|
|
9022
|
+
this.passEnvironmentGain = 1;
|
|
9023
|
+
this.materialReflectivity = null;
|
|
9024
|
+
this.materialEnvironmentGain = null;
|
|
8441
9025
|
this.environmentDials[1] = 0;
|
|
8442
9026
|
this.environmentDials[2] = 1;
|
|
8443
9027
|
this.emission[0] = env.emissiveGain;
|
|
8444
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;
|
|
8445
9035
|
for (const target of this.flatTargets) {
|
|
8446
9036
|
this.gl.useProgram(target.program);
|
|
8447
9037
|
this.writeMeshPassState(target.uniforms, camera, env);
|
|
@@ -8595,11 +9185,14 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8595
9185
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
8596
9186
|
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
8597
9187
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
9188
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
9189
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
8598
9190
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
8599
9191
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
8600
9192
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
8601
|
-
gl.
|
|
8602
|
-
|
|
9193
|
+
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
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);
|
|
8603
9196
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
8604
9197
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
8605
9198
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -8666,10 +9259,9 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8666
9259
|
this.surfaceFog = FOG_RECEDE;
|
|
8667
9260
|
/* Grain on by default, so every scene written before it was a choice looks unchanged. */
|
|
8668
9261
|
gl.uniform1f(u['uGrain'] ?? null, 1);
|
|
8669
|
-
/*
|
|
8670
|
-
|
|
8671
|
-
this.
|
|
8672
|
-
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);
|
|
8673
9265
|
/* The draw's own ambient as it stands, which `bindMeshPass` has just cleared: a program built
|
|
8674
9266
|
in the middle of a pass takes what the caller set. See `setAmbientSH`. */
|
|
8675
9267
|
gl.uniform4fv(u['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
@@ -8717,11 +9309,24 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
8717
9309
|
gl.uniform1f(u['uShadowStrength'] ?? null, this.sunShadows === null ? 0 : env.shadowStrength);
|
|
8718
9310
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
8719
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));
|
|
8720
9316
|
gl.uniform1f(u['uShadowMaxDistance'] ?? null, this.quality.directionalShadowMaxDistance);
|
|
8721
9317
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
8722
9318
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
8723
9319
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
8724
|
-
|
|
9320
|
+
/* The near corner, and in its w the frame the cutout dither moves with. See `uHighlightMin`.
|
|
9321
|
+
An environment written by hand may leave the box out, as the far corner says below. */
|
|
9322
|
+
const min = env.highlightMin as ArrayLike<number> | undefined;
|
|
9323
|
+
gl.uniform4f(
|
|
9324
|
+
u['uHighlightMin'] ?? null,
|
|
9325
|
+
min?.[0] ?? 0,
|
|
9326
|
+
min?.[1] ?? 0,
|
|
9327
|
+
min?.[2] ?? 0,
|
|
9328
|
+
this.cutoutFrame,
|
|
9329
|
+
);
|
|
8725
9330
|
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
8726
9331
|
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
8727
9332
|
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
@@ -9045,6 +9650,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9045
9650
|
* light and its colour, each into the blur's own target.
|
|
9046
9651
|
*/
|
|
9047
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;
|
|
9048
9655
|
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
9049
9656
|
const keeps = this.reflectionKeeps();
|
|
9050
9657
|
if (keeps) this.reflectionTargets?.on();
|
|
@@ -9125,14 +9732,25 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9125
9732
|
}
|
|
9126
9733
|
|
|
9127
9734
|
/** One draw of a mesh, as `drawMesh` describes, of the half of a skin `half` names. */
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
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;
|
|
9136
9754
|
/*
|
|
9137
9755
|
* A rigged mesh takes the skinned program, an unrigged one the plain program.
|
|
9138
9756
|
*
|
|
@@ -9160,10 +9778,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9160
9778
|
* when none were asked for — an unbound sampler would default to a shadow map's unit.
|
|
9161
9779
|
*/
|
|
9162
9780
|
const cloth = wantSkin ? this.cloth : null;
|
|
9163
|
-
const
|
|
9164
|
-
|
|
9165
|
-
? undefined
|
|
9166
|
-
: 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);
|
|
9167
9783
|
const bothUniforms = eight ? this.flatBoth8Uniforms : this.flatBothUniforms;
|
|
9168
9784
|
const skinnedUniforms = eight ? this.flatSkinned8Uniforms : this.flatSkinnedUniforms;
|
|
9169
9785
|
const both = wantSkin && wantMorph && bothUniforms !== null;
|
|
@@ -9177,8 +9793,12 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9177
9793
|
: morphed
|
|
9178
9794
|
? this.flatMorphedUniforms
|
|
9179
9795
|
: null;
|
|
9180
|
-
|
|
9181
|
-
|
|
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 =
|
|
9182
9802
|
clothed !== undefined
|
|
9183
9803
|
? clothed.program
|
|
9184
9804
|
: skinned && morphed
|
|
@@ -9192,26 +9812,41 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9192
9812
|
: morphed
|
|
9193
9813
|
? this.flatMorphedProgram
|
|
9194
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;
|
|
9195
9844
|
/* A material with a model takes that model's program for the same vertex variant. */
|
|
9196
9845
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
9197
9846
|
if (modelKind !== null) {
|
|
9198
|
-
const modelled = this.modelProgram(
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
: skinned && morphed
|
|
9202
|
-
? eight
|
|
9203
|
-
? 'both8'
|
|
9204
|
-
: 'both'
|
|
9205
|
-
: skinned
|
|
9206
|
-
? eight
|
|
9207
|
-
? 'skinned8'
|
|
9208
|
-
: 'skinned'
|
|
9209
|
-
: morphed
|
|
9210
|
-
? 'morphed'
|
|
9211
|
-
: 'plain',
|
|
9212
|
-
modelKind,
|
|
9213
|
-
half,
|
|
9214
|
-
);
|
|
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;
|
|
9215
9850
|
program = modelled.program;
|
|
9216
9851
|
u = modelled.uniforms;
|
|
9217
9852
|
}
|
|
@@ -9379,6 +10014,8 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9379
10014
|
/* A material with a model takes that model's plain program; see `modelProgram`. */
|
|
9380
10015
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
9381
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;
|
|
9382
10019
|
const u = modelled?.uniforms ?? this.flatUniforms;
|
|
9383
10020
|
if (modelled === null) this.useFlatProgram();
|
|
9384
10021
|
else gl.useProgram(modelled.program);
|
|
@@ -9910,6 +10547,7 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
9910
10547
|
/* Uncorrected, for the reason the lit program's copy of this line carries: the shaft's
|
|
9911
10548
|
lookup does its own `* 0.5 + 0.5` and correcting the matrix as well doubles it. */
|
|
9912
10549
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, env.lightViewProj);
|
|
10550
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, this.movingLightFor(env));
|
|
9913
10551
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
9914
10552
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
9915
10553
|
|
|
@@ -10183,6 +10821,12 @@ function litVertexOf(variant: string): string {
|
|
|
10183
10821
|
}
|
|
10184
10822
|
const skinned = variant.startsWith('skinned') || variant.startsWith('both');
|
|
10185
10823
|
const morphed = variant === 'morphed' || variant.startsWith('both');
|
|
10186
|
-
const
|
|
10824
|
+
const animated = variant === 'animated';
|
|
10825
|
+
const vertex = flatVert({
|
|
10826
|
+
skinned,
|
|
10827
|
+
morphed,
|
|
10828
|
+
instanced: variant === 'instanced' || animated,
|
|
10829
|
+
animated,
|
|
10830
|
+
});
|
|
10187
10831
|
return variant.endsWith('8') ? skinEight(vertex) : vertex;
|
|
10188
10832
|
}
|