@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
|
@@ -33,9 +33,12 @@ import { clamp } from '../../../math/scalar.js';
|
|
|
33
33
|
import { attachContextLoss } from '../../contextLoss.js';
|
|
34
34
|
import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.js';
|
|
35
35
|
import { GpuTimer } from '../../gpuTimer.js';
|
|
36
|
-
import { compileProgram, uniformLocations } from '../../shader.js';
|
|
36
|
+
import { compileProgram, finishProgram, startProgram, uniformLocations } from '../../shader.js';
|
|
37
37
|
import { countUniformVectors, FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
|
|
38
38
|
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
39
|
+
import { PROJECTION_FLOATS, packSurfaceProjection, projects } from '../../surfaceProjection.js';
|
|
40
|
+
import { orthographicDepthSpan } from '../../lightMatrix.js';
|
|
41
|
+
import { LAYER_FLOATS, packSurfaceLayers } from '../../surfaceLayers.js';
|
|
39
42
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
40
43
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
41
44
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
@@ -77,7 +80,7 @@ import { MAX_AREA_LIGHTS } from '../../areaLights.js';
|
|
|
77
80
|
import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAreaShadowLayer, AreaShadowSet, } from '../../areaShadowSet.js';
|
|
78
81
|
import { equirectToCubeFaces } from '../../equirectToCube.js';
|
|
79
82
|
import { SKY_FRAG, skyVertFor } from '../../shaders/sky.js';
|
|
80
|
-
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
83
|
+
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT, DEPTH_INSTANCED_ANIMATED_VERT, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
81
84
|
import { OCTAHEDRAL_EDGE, PointShadowArray, strideLayers } from '../../pointShadowArray.js';
|
|
82
85
|
import { SunShadowArray } from '../../shadowMap.js';
|
|
83
86
|
import { POINT_SHADOW_SAMPLER, PointShadowSystem } from '../../pointShadowSystem.js';
|
|
@@ -85,6 +88,7 @@ import { castsDepth, glassOf } from '../../shadowCasters.js';
|
|
|
85
88
|
import { GlassCasterList } from '../../glassCasters.js';
|
|
86
89
|
import { SunGlassTint } from '../../sunGlassTint.js';
|
|
87
90
|
import { GlassTintPrograms } from './glassTintPrograms.js';
|
|
91
|
+
import { captureStaticDraws, replayStaticDraws } from '../../staticDraws.js';
|
|
88
92
|
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.js';
|
|
89
93
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.js';
|
|
90
94
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
@@ -117,6 +121,9 @@ import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_
|
|
|
117
121
|
import { deviceSnappedCellSize } from '../../textLayout.js';
|
|
118
122
|
import { WindStreakRenderer } from '../../windStreakRenderer.js';
|
|
119
123
|
import { InstancedBatch } from './instanced.js';
|
|
124
|
+
import { GlBoneAnimation } from './boneAnimations.js';
|
|
125
|
+
import { packBoneAnimation } from '../../boneAnimation.js';
|
|
126
|
+
import { BONE_PLACES_TEXTURE_UNIT, BONE_TURNS_TEXTURE_UNIT, INSTANCE_CLOCKS_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
120
127
|
import { cutoutOf } from '../../cutoutCaster.js';
|
|
121
128
|
import { CUTOUT_RESOLVE_CODE, resolveCutout } from '../../cutoutDither.js';
|
|
122
129
|
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.js';
|
|
@@ -287,6 +294,18 @@ const NO_CLIP_PLANE = new Float32Array(4);
|
|
|
287
294
|
const IDENTITY_CLIP = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
288
295
|
/** Standing still, for a caller that asked for dust and not for wind. */
|
|
289
296
|
const NO_DRIFT = [0, 0, 0];
|
|
297
|
+
/** Whether two matrices hold the same sixteen numbers. */
|
|
298
|
+
function sameMatrix(a, b) {
|
|
299
|
+
for (let k = 0; k < 16; k++)
|
|
300
|
+
if (a[k] !== b[k])
|
|
301
|
+
return false;
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
/** How often `ready` asks the driver whether a program is done, and how many times before waiting. */
|
|
305
|
+
const READY_POLL_MS = 4;
|
|
306
|
+
const READY_MAX_POLLS = 500;
|
|
307
|
+
/** The halves a skin draws under the screen-space blur, each a program of its own. */
|
|
308
|
+
const SPLIT_SKIN_HALVES = ['scene', 'diffuse', 'albedo'];
|
|
290
309
|
export class WebGL2Renderer {
|
|
291
310
|
/**
|
|
292
311
|
* Private on purpose. The GL context never leaves this directory, so a
|
|
@@ -564,6 +583,23 @@ export class WebGL2Renderer {
|
|
|
564
583
|
* compiled the first time a draw asks for one. See `modelProgram`.
|
|
565
584
|
*/
|
|
566
585
|
modelFlat = new Map();
|
|
586
|
+
/**
|
|
587
|
+
* Model programs still compiling, where draws skip a compile (`pipelineCompile: 'skip'`) and the
|
|
588
|
+
* context offers `KHR_parallel_shader_compile`; `parallelCompile` is its completion query then,
|
|
589
|
+
* and null where draws wait for a program here instead. See `modelProgram`.
|
|
590
|
+
*/
|
|
591
|
+
modelPending = new Map();
|
|
592
|
+
/** What `drawVariantOf` answers, rewritten each draw rather than made. */
|
|
593
|
+
drawVariant = {
|
|
594
|
+
name: 'plain',
|
|
595
|
+
skinned: false,
|
|
596
|
+
morphed: false,
|
|
597
|
+
cloth: null,
|
|
598
|
+
clothed: undefined,
|
|
599
|
+
program: null,
|
|
600
|
+
uniforms: {},
|
|
601
|
+
};
|
|
602
|
+
parallelCompile;
|
|
567
603
|
/** The last palette chosen, which an eye's axis is turned by. See `setSkinPalette`. */
|
|
568
604
|
paletteData = null;
|
|
569
605
|
emptyTexture2D;
|
|
@@ -626,7 +662,7 @@ export class WebGL2Renderer {
|
|
|
626
662
|
oitReplaying = false;
|
|
627
663
|
/** `uSurfaceScene`, resolved once a pass. See `surfaceScene.ts`. */
|
|
628
664
|
surfaceScene = new Float32Array(4);
|
|
629
|
-
/** The
|
|
665
|
+
/** The three lanes of `uWriteMode`, kept here because a `vec3` is written whole. */
|
|
630
666
|
oitWeight = 0;
|
|
631
667
|
/**
|
|
632
668
|
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
@@ -634,6 +670,7 @@ export class WebGL2Renderer {
|
|
|
634
670
|
*/
|
|
635
671
|
surfaceFog = FOG_RECEDE;
|
|
636
672
|
ditherFade = 0;
|
|
673
|
+
ditherOpacity = 0;
|
|
637
674
|
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
638
675
|
oitRefused = false;
|
|
639
676
|
/** Said once for the whole renderer. See `refuseOitMultisampled`. */
|
|
@@ -802,6 +839,11 @@ export class WebGL2Renderer {
|
|
|
802
839
|
depthCutoutUniforms;
|
|
803
840
|
depthInstancedCutoutProgram;
|
|
804
841
|
depthInstancedCutoutUniforms;
|
|
842
|
+
/** A crowd's casters: the instanced depth programs that play a bone animation. */
|
|
843
|
+
depthAnimatedProgram;
|
|
844
|
+
depthAnimatedUniforms;
|
|
845
|
+
depthAnimatedCutoutProgram;
|
|
846
|
+
depthAnimatedCutoutUniforms;
|
|
805
847
|
scatterDepthProgram;
|
|
806
848
|
scatterDepthUniforms;
|
|
807
849
|
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
@@ -896,6 +938,28 @@ export class WebGL2Renderer {
|
|
|
896
938
|
overlayTexture = null;
|
|
897
939
|
/** Whether this device's lit stage has room for the overlay's vectors: asked once. */
|
|
898
940
|
overlayRoom = null;
|
|
941
|
+
/**
|
|
942
|
+
* The matrix the sun's dynamic shadow pass was last drawn with, uncorrected, and whether one has
|
|
943
|
+
* been: what the moving layer is read through. See `movingLightFor`.
|
|
944
|
+
*/
|
|
945
|
+
movingLightViewProj = new Float32Array(16);
|
|
946
|
+
movingLightKnown = false;
|
|
947
|
+
/** Whether the lit stage has room for the moving layer's matrix, and whether its refusal was said. */
|
|
948
|
+
movingRoom = null;
|
|
949
|
+
warnedMovingRoom = false;
|
|
950
|
+
/** The same answer for a material's projection, and whether its refusal was said. */
|
|
951
|
+
projectionRoom = null;
|
|
952
|
+
warnedProjectionRoom = false;
|
|
953
|
+
/** `uWorldUv`, the current material's projection packed. See `surfaceProjection.ts`. */
|
|
954
|
+
projection = new Float32Array(PROJECTION_FLOATS);
|
|
955
|
+
/** The current material's `diffuseTransmission`, `uGlassTint.w`, 0 to 1. */
|
|
956
|
+
diffuseTransmission = 0;
|
|
957
|
+
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
958
|
+
layerFloats = new Float32Array(LAYER_FLOATS);
|
|
959
|
+
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
960
|
+
layersRoom = null;
|
|
961
|
+
warnedLayersRoom = false;
|
|
962
|
+
warnedLayersModelMap = false;
|
|
899
963
|
warnedOverlayRoom = false;
|
|
900
964
|
warnedOverlayBlocks = false;
|
|
901
965
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -1205,14 +1269,21 @@ export class WebGL2Renderer {
|
|
|
1205
1269
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
1206
1270
|
if (count === 0)
|
|
1207
1271
|
return;
|
|
1272
|
+
const clip = gpuBatch.animation;
|
|
1273
|
+
if (clip !== null && clip.disposed)
|
|
1274
|
+
return;
|
|
1208
1275
|
this.shadowDrawBudget.ask();
|
|
1209
1276
|
const twoSided = material?.doubleSided === true;
|
|
1210
1277
|
if (twoSided)
|
|
1211
1278
|
gl.disable(gl.CULL_FACE);
|
|
1212
1279
|
const cutout = cutoutOf(material);
|
|
1213
1280
|
if (cutout !== null) {
|
|
1214
|
-
|
|
1215
|
-
this.
|
|
1281
|
+
/* A crowd casts where its clip has moved it, through the animated variant. */
|
|
1282
|
+
const uniforms = clip === null ? this.depthInstancedCutoutUniforms : this.depthAnimatedCutoutUniforms;
|
|
1283
|
+
gl.useProgram(clip === null ? this.depthInstancedCutoutProgram : this.depthAnimatedCutoutProgram);
|
|
1284
|
+
this.bindCutoutDepth(uniforms, cutout);
|
|
1285
|
+
if (clip !== null)
|
|
1286
|
+
this.bindAnimation(uniforms, gpuBatch);
|
|
1216
1287
|
gpuBatch.draw(gl, count);
|
|
1217
1288
|
if (twoSided && this.depthPassCullsFaces)
|
|
1218
1289
|
gl.enable(gl.CULL_FACE);
|
|
@@ -1220,12 +1291,14 @@ export class WebGL2Renderer {
|
|
|
1220
1291
|
this.restoreSurfaceTextureUnit();
|
|
1221
1292
|
return;
|
|
1222
1293
|
}
|
|
1223
|
-
const u = this.depthInstancedUniforms;
|
|
1224
|
-
gl.useProgram(this.depthInstancedProgram);
|
|
1294
|
+
const u = clip === null ? this.depthInstancedUniforms : this.depthAnimatedUniforms;
|
|
1295
|
+
gl.useProgram(clip === null ? this.depthInstancedProgram : this.depthAnimatedProgram);
|
|
1225
1296
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, this.activeDepthViewProj);
|
|
1226
1297
|
gl.uniform1i(u['uPeelShadowLayer'] ?? null, this.activeDepthPeel ? 1 : 0);
|
|
1227
1298
|
if (this.activeDepthPeel)
|
|
1228
1299
|
gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
1300
|
+
if (clip !== null)
|
|
1301
|
+
this.bindAnimation(u, gpuBatch);
|
|
1229
1302
|
gpuBatch.draw(gl, count);
|
|
1230
1303
|
if (twoSided && this.depthPassCullsFaces)
|
|
1231
1304
|
gl.enable(gl.CULL_FACE);
|
|
@@ -2091,6 +2164,14 @@ export class WebGL2Renderer {
|
|
|
2091
2164
|
* Measured on an RX 9070 XT through ANGLE/Vulkan: offered. `scripts/depth-survey.mjs` is what
|
|
2092
2165
|
* asks, and it also reports what WebGPU can do.
|
|
2093
2166
|
*/
|
|
2167
|
+
/*
|
|
2168
|
+
* Where draws skip a compile, the query that says a program is done without waiting for it;
|
|
2169
|
+
* where the context has none, a program is waited for as it always was. See `modelProgram`.
|
|
2170
|
+
*/
|
|
2171
|
+
this.parallelCompile =
|
|
2172
|
+
this.quality.pipelineCompile === 'skip'
|
|
2173
|
+
? gl.getExtension('KHR_parallel_shader_compile')
|
|
2174
|
+
: null;
|
|
2094
2175
|
this.clipControl = gl.getExtension('EXT_clip_control');
|
|
2095
2176
|
if (REVERSED_DEPTH && this.clipControl !== null) {
|
|
2096
2177
|
this.clipControl.clipControlEXT(this.clipControl.LOWER_LEFT_EXT, this.clipControl.ZERO_TO_ONE_EXT);
|
|
@@ -2230,10 +2311,15 @@ export class WebGL2Renderer {
|
|
|
2230
2311
|
this.depthCutoutUniforms = uniformLocations(gl, this.depthCutoutProgram, 'depth-cutout');
|
|
2231
2312
|
this.depthInstancedCutoutProgram = compileProgram(gl, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_CUTOUT_FRAG, 'depth-instanced-cutout');
|
|
2232
2313
|
this.depthInstancedCutoutUniforms = uniformLocations(gl, this.depthInstancedCutoutProgram, 'depth-instanced-cutout');
|
|
2314
|
+
this.depthAnimatedProgram = compileProgram(gl, DEPTH_INSTANCED_ANIMATED_VERT, DEPTH_FRAG, 'depth-instanced-animated');
|
|
2315
|
+
this.depthAnimatedUniforms = uniformLocations(gl, this.depthAnimatedProgram, 'depth-instanced-animated');
|
|
2316
|
+
this.depthAnimatedCutoutProgram = compileProgram(gl, DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT, DEPTH_CUTOUT_FRAG, 'depth-instanced-animated-cutout');
|
|
2317
|
+
this.depthAnimatedCutoutUniforms = uniformLocations(gl, this.depthAnimatedCutoutProgram, 'depth-instanced-animated-cutout');
|
|
2233
2318
|
/* The cutout map is always read from the surface unit, which the draw borrows and gives back. */
|
|
2234
2319
|
for (const [program, uniforms] of [
|
|
2235
2320
|
[this.depthCutoutProgram, this.depthCutoutUniforms],
|
|
2236
2321
|
[this.depthInstancedCutoutProgram, this.depthInstancedCutoutUniforms],
|
|
2322
|
+
[this.depthAnimatedCutoutProgram, this.depthAnimatedCutoutUniforms],
|
|
2237
2323
|
]) {
|
|
2238
2324
|
gl.useProgram(program);
|
|
2239
2325
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
@@ -2330,7 +2416,33 @@ export class WebGL2Renderer {
|
|
|
2330
2416
|
* this before its first frame gets the right behaviour on both without asking which backend
|
|
2331
2417
|
* it has, which is the contract `RendererApi` exists to keep.
|
|
2332
2418
|
*/
|
|
2333
|
-
|
|
2419
|
+
/**
|
|
2420
|
+
* Resolve once every program a draw has started compiling has finished, adopting each: what a
|
|
2421
|
+
* loading screen awaits after drawing what its scene will use, where draws skip a compile. See
|
|
2422
|
+
* `RenderQuality.pipelineCompile`; nothing is started anywhere else here, so otherwise it is now.
|
|
2423
|
+
*
|
|
2424
|
+
* **It waits without holding the thread**, as the other backend's does: it asks the driver
|
|
2425
|
+
* whether each is done, which does not wait, and yields between asks. A driver that never says
|
|
2426
|
+
* done is asked the way that waits after `READY_MAX_POLLS`, so this cannot hang a loading screen.
|
|
2427
|
+
*/
|
|
2428
|
+
async ready() {
|
|
2429
|
+
for (let polls = 0; this.modelPending.size > 0 && !this.contextLost; polls++) {
|
|
2430
|
+
for (const [key, started] of this.modelPending) {
|
|
2431
|
+
const done = polls >= READY_MAX_POLLS ||
|
|
2432
|
+
this.parallelCompile === null ||
|
|
2433
|
+
this.gl.getProgramParameter(started.program, this.parallelCompile.COMPLETION_STATUS_KHR) === true;
|
|
2434
|
+
if (!done)
|
|
2435
|
+
continue;
|
|
2436
|
+
this.modelPending.delete(key);
|
|
2437
|
+
this.adoptModel(key, finishProgram(this.gl, started), started.label);
|
|
2438
|
+
}
|
|
2439
|
+
this.useFlatProgram();
|
|
2440
|
+
if (this.modelPending.size > 0) {
|
|
2441
|
+
// platform: browser default — a yield between two asks of the driver, in a browser module
|
|
2442
|
+
await new Promise((resolve) => setTimeout(resolve, READY_POLL_MS));
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2334
2446
|
dispose(options = {}) {
|
|
2335
2447
|
if (this.disposed)
|
|
2336
2448
|
return;
|
|
@@ -2401,6 +2513,8 @@ export class WebGL2Renderer {
|
|
|
2401
2513
|
this.lightVolumeProgram,
|
|
2402
2514
|
this.depthCutoutProgram,
|
|
2403
2515
|
this.depthInstancedCutoutProgram,
|
|
2516
|
+
this.depthAnimatedProgram,
|
|
2517
|
+
this.depthAnimatedCutoutProgram,
|
|
2404
2518
|
]) {
|
|
2405
2519
|
gl.deleteProgram(program);
|
|
2406
2520
|
}
|
|
@@ -2718,7 +2832,7 @@ export class WebGL2Renderer {
|
|
|
2718
2832
|
updateSurfaceTexture(texture, source) {
|
|
2719
2833
|
if (this.contextLost)
|
|
2720
2834
|
return;
|
|
2721
|
-
texture.update(this.gl, source);
|
|
2835
|
+
texture.update(this.gl, source, this.compressedFormats);
|
|
2722
2836
|
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
2723
2837
|
if (texture === this.overlayMaps)
|
|
2724
2838
|
this.bindOverlayMaps(texture);
|
|
@@ -2783,7 +2897,30 @@ export class WebGL2Renderer {
|
|
|
2783
2897
|
return held;
|
|
2784
2898
|
const { gl } = this;
|
|
2785
2899
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
2786
|
-
const
|
|
2900
|
+
const vertex = modelVertexOf(variant, kind);
|
|
2901
|
+
const fragment = skinHalfBound(modelBound(this.flatFragSource, kind), half);
|
|
2902
|
+
/*
|
|
2903
|
+
* **Where draws skip a compile, none until the driver says asking will not wait**: the program
|
|
2904
|
+
* is started at the first draw that wants it and that draw, and every one before it is ready,
|
|
2905
|
+
* is left out. See `RenderQuality.pipelineCompile`.
|
|
2906
|
+
*/
|
|
2907
|
+
if (this.parallelCompile !== null) {
|
|
2908
|
+
const started = this.modelPending.get(key);
|
|
2909
|
+
if (started === undefined) {
|
|
2910
|
+
this.modelPending.set(key, startProgram(gl, vertex, fragment, label));
|
|
2911
|
+
return null;
|
|
2912
|
+
}
|
|
2913
|
+
if (gl.getProgramParameter(started.program, this.parallelCompile.COMPLETION_STATUS_KHR) !== true) {
|
|
2914
|
+
return null;
|
|
2915
|
+
}
|
|
2916
|
+
this.modelPending.delete(key);
|
|
2917
|
+
return this.adoptModel(key, finishProgram(gl, started), label);
|
|
2918
|
+
}
|
|
2919
|
+
return this.adoptModel(key, compileProgram(gl, vertex, fragment, label), label);
|
|
2920
|
+
}
|
|
2921
|
+
/** A model program compiled: kept, written to by every material setter, and fed the pass. */
|
|
2922
|
+
adoptModel(key, program, label) {
|
|
2923
|
+
const { gl } = this;
|
|
2787
2924
|
const made = { program, uniforms: uniformLocations(gl, program, label) };
|
|
2788
2925
|
this.modelFlat.set(key, made);
|
|
2789
2926
|
this.flatTargets.push(made);
|
|
@@ -2929,6 +3066,13 @@ export class WebGL2Renderer {
|
|
|
2929
3066
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2930
3067
|
this.materialHasMaps = materialHasMaps(material);
|
|
2931
3068
|
this.currentMaterial = material;
|
|
3069
|
+
const reflectivity = material?.reflectivity;
|
|
3070
|
+
const environmentGain = material?.environmentGain;
|
|
3071
|
+
this.materialReflectivity =
|
|
3072
|
+
reflectivity === undefined ? null : Math.min(1, Math.max(0, reflectivity));
|
|
3073
|
+
this.materialEnvironmentGain =
|
|
3074
|
+
environmentGain === undefined ? null : Math.max(0, environmentGain);
|
|
3075
|
+
this.writeEnvironmentDials();
|
|
2932
3076
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
2933
3077
|
const model = material?.model;
|
|
2934
3078
|
warnUnpagedLightmap(model, material?.modelMap);
|
|
@@ -2966,7 +3110,8 @@ export class WebGL2Renderer {
|
|
|
2966
3110
|
const v = this.cutoutVector;
|
|
2967
3111
|
v[0] = material?.cutout ?? 0;
|
|
2968
3112
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
2969
|
-
|
|
3113
|
+
/* z unused: the dither's frame is the pass's, in `uHighlightMin.w`. See `uCutout`. */
|
|
3114
|
+
v[2] = 0;
|
|
2970
3115
|
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
2971
3116
|
v[3] = lightChannelsOf(material?.lightChannels);
|
|
2972
3117
|
return v;
|
|
@@ -3052,10 +3197,17 @@ export class WebGL2Renderer {
|
|
|
3052
3197
|
* and read neither — one decision for every program, as `setMaterial` says.
|
|
3053
3198
|
*/
|
|
3054
3199
|
const modelMap = material?.modelMap ?? null;
|
|
3200
|
+
/* A layered material's mask goes where a model's map would: see `SurfaceMaterial.layers`. */
|
|
3201
|
+
const layers = this.layersOf(material);
|
|
3202
|
+
const boundMap = modelMap ?? layers?.mask ?? null;
|
|
3055
3203
|
gl.activeTexture(gl.TEXTURE0 + MODEL_MAP_TEXTURE_UNIT);
|
|
3056
3204
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
3057
|
-
if (
|
|
3058
|
-
|
|
3205
|
+
if (boundMap !== null)
|
|
3206
|
+
boundMap.bind(gl, MODEL_MAP_TEXTURE_UNIT);
|
|
3207
|
+
packSurfaceLayers(layers, this.layerFloats);
|
|
3208
|
+
if (layers !== null)
|
|
3209
|
+
this.askLit('LAYERED');
|
|
3210
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
3059
3211
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
3060
3212
|
packModel(material?.model ?? null, modelMap !== null, this.modelParams, material?.physicalSpecular === true);
|
|
3061
3213
|
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
@@ -3064,6 +3216,16 @@ export class WebGL2Renderer {
|
|
|
3064
3216
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
3065
3217
|
/* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
|
|
3066
3218
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
3219
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
3220
|
+
/* How much light from behind the surface lets through, in the glass tint's spare lane. */
|
|
3221
|
+
this.diffuseTransmission = Math.min(Math.max(material?.diffuseTransmission ?? 0, 0), 1) || 0;
|
|
3222
|
+
gl.uniform4f(u['uGlassTint'] ?? null, 1, 1, 1, this.diffuseTransmission);
|
|
3223
|
+
/* Maps placed by the world, where the material asks and the lit stage has room: worldUv.ts. */
|
|
3224
|
+
const projection = material?.projection ?? null;
|
|
3225
|
+
packSurfaceProjection(this.projectionFits(projection) ? projection : null, this.projection);
|
|
3226
|
+
if (this.projection[0] > 0)
|
|
3227
|
+
this.askLit('WORLD_UVS');
|
|
3228
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
3067
3229
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
3068
3230
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
3069
3231
|
if (albedo === null) {
|
|
@@ -3157,8 +3319,22 @@ export class WebGL2Renderer {
|
|
|
3157
3319
|
setSurfaceReflectivity(amount) {
|
|
3158
3320
|
if (this.contextLost)
|
|
3159
3321
|
return;
|
|
3322
|
+
this.passReflectivity = Math.min(1, Math.max(0, amount));
|
|
3323
|
+
this.writeEnvironmentDials();
|
|
3324
|
+
}
|
|
3325
|
+
/**
|
|
3326
|
+
* The reflectivity and environment gain the setters gave the pass, and the ones the material
|
|
3327
|
+
* set now states, or null where it states none (`SurfaceMaterial.reflectivity`). A draw wears the
|
|
3328
|
+
* material's where it has one and the pass's otherwise.
|
|
3329
|
+
*/
|
|
3330
|
+
passReflectivity = 0;
|
|
3331
|
+
passEnvironmentGain = 1;
|
|
3332
|
+
materialReflectivity = null;
|
|
3333
|
+
materialEnvironmentGain = null;
|
|
3334
|
+
writeEnvironmentDials() {
|
|
3160
3335
|
this.materials.dirty();
|
|
3161
|
-
this.environmentDials[1] =
|
|
3336
|
+
this.environmentDials[1] = this.materialReflectivity ?? this.passReflectivity;
|
|
3337
|
+
this.environmentDials[2] = this.materialEnvironmentGain ?? this.passEnvironmentGain;
|
|
3162
3338
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3163
3339
|
}
|
|
3164
3340
|
/**
|
|
@@ -3214,14 +3390,98 @@ export class WebGL2Renderer {
|
|
|
3214
3390
|
}
|
|
3215
3391
|
/** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
|
|
3216
3392
|
overlayRoomLeft() {
|
|
3217
|
-
return (this.fragmentVectorLimit() -
|
|
3393
|
+
return (this.fragmentVectorLimit() -
|
|
3394
|
+
countUniformVectors(this.flatFragAt(this.lightBudget, { SURFACE_OVERLAY: false })));
|
|
3218
3395
|
}
|
|
3219
3396
|
/** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
|
|
3220
3397
|
overlayFits() {
|
|
3221
|
-
this.overlayRoom ??=
|
|
3222
|
-
countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
|
|
3398
|
+
this.overlayRoom ??= this.switchFits('SURFACE_OVERLAY');
|
|
3223
3399
|
return this.overlayRoom;
|
|
3224
3400
|
}
|
|
3401
|
+
/**
|
|
3402
|
+
* Whether a material's projection can be drawn: none asked, or the lit stage has the room for its
|
|
3403
|
+
* one vector. **Refused, said once, where it has not**, as the overlay is, and the material's
|
|
3404
|
+
* maps are then laid by the mesh's coordinates, as they were before it existed.
|
|
3405
|
+
*/
|
|
3406
|
+
projectionFits(projection) {
|
|
3407
|
+
if (!projects(projection))
|
|
3408
|
+
return true;
|
|
3409
|
+
this.projectionRoom ??= this.switchFits('WORLD_UVS');
|
|
3410
|
+
if (!this.projectionRoom && !this.warnedProjectionRoom) {
|
|
3411
|
+
this.warnedProjectionRoom = true;
|
|
3412
|
+
console.warn("Renderer: a material's projection is refused on this device. It takes one fragment " +
|
|
3413
|
+
'uniform vector and the lit shader already uses all ' +
|
|
3414
|
+
`${this.fragmentVectorLimit()} this GPU offers; its maps follow the mesh's coordinates. ` +
|
|
3415
|
+
'A lower `maxLights` makes the room, or WebGPU has it.');
|
|
3416
|
+
}
|
|
3417
|
+
return this.projectionRoom;
|
|
3418
|
+
}
|
|
3419
|
+
/**
|
|
3420
|
+
* What the sun's moving layer is read through: the matrix its shadow pass was last drawn with,
|
|
3421
|
+
* where the lit stage carries one (`MOVING_SUN`); the environment's otherwise, which every caller
|
|
3422
|
+
* drawing both layers with one matrix hands it anyway.
|
|
3423
|
+
*/
|
|
3424
|
+
movingLightFor(env) {
|
|
3425
|
+
return this.movingLightKnown && this.litOn.MOVING_SUN
|
|
3426
|
+
? this.movingLightViewProj
|
|
3427
|
+
: env.lightViewProj;
|
|
3428
|
+
}
|
|
3429
|
+
/**
|
|
3430
|
+
* Turn the moving layer's own matrix on the first time a frame draws that layer with one the
|
|
3431
|
+
* environment's does not share. **Refused, said once, where the lit stage has no room for its five
|
|
3432
|
+
* vectors**: the layer is then read through the environment's matrix, so its shadows land where
|
|
3433
|
+
* that matrix puts them, and a caller there draws both layers with one.
|
|
3434
|
+
*/
|
|
3435
|
+
askMovingSun(env) {
|
|
3436
|
+
if (this.litWanted.MOVING_SUN || !this.movingLightKnown)
|
|
3437
|
+
return;
|
|
3438
|
+
if (sameMatrix(this.movingLightViewProj, env.lightViewProj))
|
|
3439
|
+
return;
|
|
3440
|
+
this.movingRoom ??= this.switchFits('MOVING_SUN');
|
|
3441
|
+
if (this.movingRoom) {
|
|
3442
|
+
this.askLit('MOVING_SUN');
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
if (this.warnedMovingRoom)
|
|
3446
|
+
return;
|
|
3447
|
+
this.warnedMovingRoom = true;
|
|
3448
|
+
console.warn("Renderer: the sun's moving layer cannot be read through a matrix of its own on this " +
|
|
3449
|
+
'device; it takes five fragment uniform vectors the lit shader does not have. It is read ' +
|
|
3450
|
+
"through the environment's `lightViewProj`, so draw the dynamic shadow pass with that " +
|
|
3451
|
+
'matrix here, or lower `maxLights` to make the room. WebGPU has it.');
|
|
3452
|
+
}
|
|
3453
|
+
/**
|
|
3454
|
+
* A material's layers where they can be drawn: none asked, a model's map in the mask's place, or
|
|
3455
|
+
* no room for their two vectors each leave one layer, the last two said once each.
|
|
3456
|
+
*/
|
|
3457
|
+
layersOf(material) {
|
|
3458
|
+
const layers = material?.layers ?? null;
|
|
3459
|
+
if (layers === null || layers.repeats.length === 0)
|
|
3460
|
+
return null;
|
|
3461
|
+
if ((material?.modelMap ?? null) !== null) {
|
|
3462
|
+
if (!this.warnedLayersModelMap) {
|
|
3463
|
+
this.warnedLayersModelMap = true;
|
|
3464
|
+
console.warn("Renderer: a material's layers are drawn as one: its mask is read where a model's map " +
|
|
3465
|
+
'goes, and this material carries a `modelMap` too.');
|
|
3466
|
+
}
|
|
3467
|
+
return null;
|
|
3468
|
+
}
|
|
3469
|
+
this.layersRoom ??= this.switchFits('LAYERED');
|
|
3470
|
+
if (this.layersRoom)
|
|
3471
|
+
return layers;
|
|
3472
|
+
if (!this.warnedLayersRoom) {
|
|
3473
|
+
this.warnedLayersRoom = true;
|
|
3474
|
+
console.warn("Renderer: a material's layers are refused on this device. They take two fragment uniform " +
|
|
3475
|
+
'vectors the lit shader does not have; the material is drawn as its base layer. A lower ' +
|
|
3476
|
+
'`maxLights` makes the room, or WebGPU has it.');
|
|
3477
|
+
}
|
|
3478
|
+
return null;
|
|
3479
|
+
}
|
|
3480
|
+
/** Whether the lit shader with `feature` compiled in fits this GPU's fragment uniform vectors. */
|
|
3481
|
+
switchFits(feature) {
|
|
3482
|
+
const asIf = { [feature]: true };
|
|
3483
|
+
return (countUniformVectors(this.flatFragAt(this.lightBudget, asIf)) <= this.fragmentVectorLimit());
|
|
3484
|
+
}
|
|
3225
3485
|
/**
|
|
3226
3486
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
3227
3487
|
*
|
|
@@ -3237,9 +3497,8 @@ export class WebGL2Renderer {
|
|
|
3237
3497
|
setEnvironmentGain(gain) {
|
|
3238
3498
|
if (this.contextLost)
|
|
3239
3499
|
return;
|
|
3240
|
-
this.
|
|
3241
|
-
this.
|
|
3242
|
-
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3500
|
+
this.passEnvironmentGain = Math.max(0, gain);
|
|
3501
|
+
this.writeEnvironmentDials();
|
|
3243
3502
|
}
|
|
3244
3503
|
/**
|
|
3245
3504
|
* Kept as a no-op that says so once, because it was a stand-in for a term that now exists.
|
|
@@ -3364,9 +3623,37 @@ export class WebGL2Renderer {
|
|
|
3364
3623
|
}
|
|
3365
3624
|
this.useFlatProgram();
|
|
3366
3625
|
}
|
|
3367
|
-
/**
|
|
3626
|
+
/**
|
|
3627
|
+
* Whether the draws that follow spend their own opacity on a screen door rather than on blending:
|
|
3628
|
+
* an instance's `MeshInstances.alphas`, or a vertex's channel alpha, keeps that share of the
|
|
3629
|
+
* pixels `setDitherFade` would and draws the rest not at all. So an opaque or cut-out batch fades
|
|
3630
|
+
* one instance at a time, with no sorting and no blend, and an instance at 1 is drawn whole.
|
|
3631
|
+
*
|
|
3632
|
+
* Off is what `bindMeshPass` restores. It shares the fade's pattern, so a crossfade at `t` and
|
|
3633
|
+
* `-t` of an instance at opacity `a` covers that instance's share once, without holes. Material
|
|
3634
|
+
* state like `setDitherFade`; instanced draws read it, and shadow casters do not.
|
|
3635
|
+
*
|
|
3636
|
+
* What it gives up: a fade reads as grain while it lasts, which a temporal resolve smooths and a
|
|
3637
|
+
* plain frame does not; a blended draw that asks spends its opacity here and blends none of it;
|
|
3638
|
+
* and a lightmapped batch carries regions where the opacities would be, so it has none to fade.
|
|
3639
|
+
*/
|
|
3640
|
+
setDitherOpacity(on) {
|
|
3641
|
+
if (this.contextLost)
|
|
3642
|
+
return;
|
|
3643
|
+
this.materials.dirty();
|
|
3644
|
+
this.ditherOpacity = on ? 1 : 0;
|
|
3645
|
+
for (const target of this.flatTargets) {
|
|
3646
|
+
this.gl.useProgram(target.program);
|
|
3647
|
+
this.writeMode(target.uniforms);
|
|
3648
|
+
}
|
|
3649
|
+
this.useFlatProgram();
|
|
3650
|
+
}
|
|
3651
|
+
/**
|
|
3652
|
+
* `uWriteMode`: the order-independent weight, the fade and whether the opacity is dithered, which
|
|
3653
|
+
* share one uniform row.
|
|
3654
|
+
*/
|
|
3368
3655
|
writeMode(u) {
|
|
3369
|
-
this.gl.
|
|
3656
|
+
this.gl.uniform3f(u['uWriteMode'] ?? null, this.oitWeight, this.ditherFade, this.ditherOpacity);
|
|
3370
3657
|
}
|
|
3371
3658
|
/**
|
|
3372
3659
|
* How strong the microscopic relief on the surfaces drawn next is, and how coarse.
|
|
@@ -3508,8 +3795,95 @@ export class WebGL2Renderer {
|
|
|
3508
3795
|
throw new Error('WebGL2: a skinned mesh cannot be instanced — an instanced draw binds the two attribute ' +
|
|
3509
3796
|
'locations the joint indices and weights occupy, and this mesh supplies them.');
|
|
3510
3797
|
}
|
|
3798
|
+
const animation = (options.animation ?? null);
|
|
3799
|
+
/* The bone a vertex follows is its second coordinates' u, which a mesh carries in its grain
|
|
3800
|
+
lane; a mesh with none has nothing to name a bone by. See `boneAnimation.ts`. */
|
|
3801
|
+
if (animation !== null && !mesh.hasGrain) {
|
|
3802
|
+
throw new Error('WebGL2: a bone animation reads each vertex’s bone from the mesh’s second coordinates ' +
|
|
3803
|
+
'(`MeshData.lightmapUvs`), and this mesh has none.');
|
|
3804
|
+
}
|
|
3511
3805
|
this.ensureInstancedProgram();
|
|
3512
|
-
|
|
3806
|
+
if (animation !== null)
|
|
3807
|
+
this.ensureAnimatedProgram();
|
|
3808
|
+
return new InstancedBatch(this.gl, mesh, capacity, options.cull ?? false, animation);
|
|
3809
|
+
}
|
|
3810
|
+
/**
|
|
3811
|
+
* A bone animation an instanced batch can play (`InstancedOptions.animation`): per frame, a turn
|
|
3812
|
+
* and a place for each bone, which every vertex of the batch's mesh follows — the bone its second
|
|
3813
|
+
* coordinates' u names, times `boneScale` — at each instance's own moment of the clip. What a
|
|
3814
|
+
* crowd is made of: every instance moves at the frame's rate on its own clock, in the colour pass
|
|
3815
|
+
* and in every shadow, with nothing on the processor after this. See `boneAnimation.ts` for the
|
|
3816
|
+
* clip's layout and the arithmetic, which both backends run.
|
|
3817
|
+
*
|
|
3818
|
+
* What it gives up against skinning is the blend: a vertex follows one bone. Refused, with what
|
|
3819
|
+
* was wrong, for a clip whose numbers are not its size, or past 2,048 bones or frames.
|
|
3820
|
+
*/
|
|
3821
|
+
createBoneAnimation(clip) {
|
|
3822
|
+
return new GlBoneAnimation(this.gl, packBoneAnimation(clip));
|
|
3823
|
+
}
|
|
3824
|
+
/** Release a clip. A batch still playing it draws nothing, and casts nothing, after. */
|
|
3825
|
+
disposeBoneAnimation(animation) {
|
|
3826
|
+
const clip = animation;
|
|
3827
|
+
if (this.contextLost || clip.disposed)
|
|
3828
|
+
return;
|
|
3829
|
+
clip.dispose(this.gl);
|
|
3830
|
+
}
|
|
3831
|
+
/**
|
|
3832
|
+
* The clock a bone animation plays by this frame, in seconds on the caller's clock: an instance's
|
|
3833
|
+
* moment is this times its rate plus its phase (`MeshInstances.clocks`). **Set it before the
|
|
3834
|
+
* shadows are drawn**, once a frame, as the wind is set: a shadow drawn under one clock and its
|
|
3835
|
+
* caster under another come apart at the frame's rate. 0 until set, which holds every crowd at
|
|
3836
|
+
* the moment its phases name.
|
|
3837
|
+
*/
|
|
3838
|
+
setAnimationTime(seconds) {
|
|
3839
|
+
if (Number.isFinite(seconds))
|
|
3840
|
+
this.animationTime = seconds;
|
|
3841
|
+
}
|
|
3842
|
+
/** The clock `setAnimationTime` set. */
|
|
3843
|
+
animationTime = 0;
|
|
3844
|
+
/**
|
|
3845
|
+
* An animated batch's clip, clocks and numbers into whichever program is bound: the three vertex
|
|
3846
|
+
* textures on units of their own (`lightBudget.ts`), the clip's speed and bone scale, the clock.
|
|
3847
|
+
*/
|
|
3848
|
+
bindAnimation(u, batch) {
|
|
3849
|
+
const clip = batch.animation;
|
|
3850
|
+
const clocks = batch.clocks;
|
|
3851
|
+
if (clip === null || clocks === null)
|
|
3852
|
+
return;
|
|
3853
|
+
const { gl } = this;
|
|
3854
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_PLACES_TEXTURE_UNIT);
|
|
3855
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.places);
|
|
3856
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_TURNS_TEXTURE_UNIT);
|
|
3857
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.turns);
|
|
3858
|
+
gl.activeTexture(gl.TEXTURE0 + INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
3859
|
+
gl.bindTexture(gl.TEXTURE_2D, clocks.texture);
|
|
3860
|
+
gl.uniform1i(u['uBonePlaces'] ?? null, BONE_PLACES_TEXTURE_UNIT);
|
|
3861
|
+
gl.uniform1i(u['uBoneTurns'] ?? null, BONE_TURNS_TEXTURE_UNIT);
|
|
3862
|
+
gl.uniform1i(u['uInstanceClocks'] ?? null, INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
3863
|
+
gl.uniform2f(u['uBoneClip'] ?? null, clip.framesPerSecond, clip.boneScale);
|
|
3864
|
+
gl.uniform1f(u['uSceneTime'] ?? null, this.animationTime);
|
|
3865
|
+
}
|
|
3866
|
+
/** The animated instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
3867
|
+
flatAnimatedProgram = null;
|
|
3868
|
+
flatAnimatedUniforms = null;
|
|
3869
|
+
/** Compile the animated variant, once, and feed it this frame's pass state. */
|
|
3870
|
+
ensureAnimatedProgram() {
|
|
3871
|
+
if (this.contextLost || this.flatAnimatedProgram !== null)
|
|
3872
|
+
return;
|
|
3873
|
+
const { gl } = this;
|
|
3874
|
+
const program = compileProgram(gl, flatVert({ skinned: false, morphed: false, instanced: true, animated: true }), this.flatFragSource, 'flat.animated');
|
|
3875
|
+
const uniforms = uniformLocations(gl, program, 'flat.animated');
|
|
3876
|
+
this.flatAnimatedProgram = program;
|
|
3877
|
+
this.flatAnimatedUniforms = uniforms;
|
|
3878
|
+
this.flatTargets.push({ program, uniforms });
|
|
3879
|
+
const camera = this.lastPassCamera;
|
|
3880
|
+
const env = this.lastPassEnv;
|
|
3881
|
+
if (camera !== null && env !== null) {
|
|
3882
|
+
gl.useProgram(program);
|
|
3883
|
+
this.writeMeshPassState(uniforms, camera, env);
|
|
3884
|
+
this.writeMaterialState(uniforms, this.currentMaterial);
|
|
3885
|
+
this.useFlatProgram();
|
|
3886
|
+
}
|
|
3513
3887
|
}
|
|
3514
3888
|
/** Push placement and colour. Only the live prefix. */
|
|
3515
3889
|
uploadInstanced(batch, data) {
|
|
@@ -3562,18 +3936,32 @@ export class WebGL2Renderer {
|
|
|
3562
3936
|
const culling = !blend && batch.cull;
|
|
3563
3937
|
if (culling && !batchBoxVisible(batch.box, this.frustum, this.occlusion))
|
|
3564
3938
|
return;
|
|
3939
|
+
/* A batch playing a bone animation takes the animated variant; a disposed clip draws nothing. */
|
|
3940
|
+
const clip = batch.animation;
|
|
3941
|
+
if (clip !== null && clip.disposed)
|
|
3942
|
+
return;
|
|
3565
3943
|
this.drawBudget.ask();
|
|
3566
3944
|
this.ensureInstancedProgram();
|
|
3945
|
+
if (clip !== null)
|
|
3946
|
+
this.ensureAnimatedProgram();
|
|
3567
3947
|
/* A material with a model takes that model's instanced program; see `modelProgram`. */
|
|
3568
3948
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
3569
|
-
const modelled = modelKind === null
|
|
3570
|
-
|
|
3571
|
-
|
|
3949
|
+
const modelled = modelKind === null
|
|
3950
|
+
? null
|
|
3951
|
+
: this.modelProgram(clip === null ? 'instanced' : 'animated', modelKind);
|
|
3952
|
+
/* Its model's program still compiling, where draws skip a compile: left out until it lands. */
|
|
3953
|
+
if (modelKind !== null && modelled === null)
|
|
3954
|
+
return;
|
|
3955
|
+
const program = modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
3956
|
+
const u = modelled?.uniforms ??
|
|
3957
|
+
(clip === null ? this.flatInstancedUniforms : this.flatAnimatedUniforms);
|
|
3572
3958
|
if (program === null || u === null)
|
|
3573
3959
|
return;
|
|
3574
3960
|
const { gl } = this;
|
|
3575
3961
|
gl.useProgram(program);
|
|
3576
3962
|
gl.uniform1i(u['uHasTangents'] ?? null, mesh.hasTangents ? 1 : 0);
|
|
3963
|
+
if (clip !== null)
|
|
3964
|
+
this.bindAnimation(u, batch);
|
|
3577
3965
|
const lit = options.lit ?? true;
|
|
3578
3966
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
3579
3967
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
@@ -3643,10 +4031,14 @@ export class WebGL2Renderer {
|
|
|
3643
4031
|
const keeps = !blend && this.reflectionKeeps();
|
|
3644
4032
|
if (keeps)
|
|
3645
4033
|
this.reflectionTargets?.on();
|
|
3646
|
-
|
|
4034
|
+
/* An animated batch reads its clocks by instance index, which the culled copy would reorder:
|
|
4035
|
+
it is culled whole and never instance by instance. */
|
|
4036
|
+
if (culling && clip === null && cullsInstances(count, mesh.indexCount)) {
|
|
3647
4037
|
batch.drawCulled(gl, data, this.frustum);
|
|
3648
|
-
|
|
4038
|
+
}
|
|
4039
|
+
else {
|
|
3649
4040
|
batch.draw(gl, count);
|
|
4041
|
+
}
|
|
3650
4042
|
if (keeps)
|
|
3651
4043
|
this.reflectionTargets?.off();
|
|
3652
4044
|
if (coverage)
|
|
@@ -3710,8 +4102,11 @@ export class WebGL2Renderer {
|
|
|
3710
4102
|
const glass = this.glassScratch;
|
|
3711
4103
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
3712
4104
|
gl.uniform4f(u['uSeeThrough'] ?? null, Math.max(refraction, 0), options.thicknessM ?? 0, glass.transmission, glass.frost);
|
|
3713
|
-
|
|
3714
|
-
|
|
4105
|
+
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
4106
|
+
const tint = options.refractTint ?? WHITE_TINT;
|
|
4107
|
+
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
4108
|
+
/* The pane's colour; w is the material's light through from behind, which glass never reads. */
|
|
4109
|
+
gl.uniform4f(u['uGlassTint'] ?? null, glass.tint[0] ?? 1, glass.tint[1] ?? 1, glass.tint[2] ?? 1, this.diffuseTransmission);
|
|
3715
4110
|
return true;
|
|
3716
4111
|
}
|
|
3717
4112
|
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
@@ -4599,6 +4994,11 @@ export class WebGL2Renderer {
|
|
|
4599
4994
|
this.shadowPassActive = true;
|
|
4600
4995
|
this.activeSunLayer = layer;
|
|
4601
4996
|
this.glassCasters.clear();
|
|
4997
|
+
/* The moving layer is read through the matrix it is drawn with: see `movingLightFor`. */
|
|
4998
|
+
if (layer === 'dynamic') {
|
|
4999
|
+
this.movingLightViewProj.set(lightViewProj);
|
|
5000
|
+
this.movingLightKnown = true;
|
|
5001
|
+
}
|
|
4602
5002
|
/* The static layer opens first, so a frame's peel counts from here. See `peelFilled`. */
|
|
4603
5003
|
if (layer === 'static')
|
|
4604
5004
|
this.peelFilled = false;
|
|
@@ -4753,6 +5153,69 @@ export class WebGL2Renderer {
|
|
|
4753
5153
|
return;
|
|
4754
5154
|
casters(this.casterSink);
|
|
4755
5155
|
}
|
|
5156
|
+
/**
|
|
5157
|
+
* Draws that do not change between frames, enumerated once and kept: what a stage, a set or any
|
|
5158
|
+
* world that stands still draws, recorded so a frame replays it rather than issuing it.
|
|
5159
|
+
*
|
|
5160
|
+
* **The same enumeration `drawSceneCasters` takes**, called once, here: rigid meshes and instanced
|
|
5161
|
+
* batches, each with its material. A skinned mesh or a scatter batch is refused by name, since
|
|
5162
|
+
* both change every frame. What it captures is what the enumeration said at the time — the
|
|
5163
|
+
* matrices and the instance counts are copied, so a caller's scratch is free to move on — while
|
|
5164
|
+
* the meshes, batches, textures and materials stay the caller's: a list must be disposed, or made
|
|
5165
|
+
* again, before anything it holds is, and made again when a material it holds is changed.
|
|
5166
|
+
*
|
|
5167
|
+
* **What it is for is the frame's processor time.** WebGPU records the list into a render bundle
|
|
5168
|
+
* per view and replays it with one call, where a draw issued a frame at a time costs its encoding
|
|
5169
|
+
* every frame and again for every view that draws it. This backend has no bundles and replays the
|
|
5170
|
+
* entries through the scene-caster path every frame, at the cost a frame drawing them itself pays;
|
|
5171
|
+
* the picture is the same.
|
|
5172
|
+
*
|
|
5173
|
+
* What a list gives up against drawing each frame is culling: every entry is drawn in every view,
|
|
5174
|
+
* because a bundle cannot leave one out. A world split into lists by region culls by list.
|
|
5175
|
+
*/
|
|
5176
|
+
createStaticDraws(casters) {
|
|
5177
|
+
return captureStaticDraws(casters);
|
|
5178
|
+
}
|
|
5179
|
+
/**
|
|
5180
|
+
* Draw a list into the open mesh pass, under the camera, the lights and the fog `bindMeshPass`
|
|
5181
|
+
* set — the frame's, a mirror's, a capture's. Opaque draws, through each entry's material.
|
|
5182
|
+
*
|
|
5183
|
+
* **Its draws are rigid and wear no overlay**, so a skin palette, morph weights, cloth or a surface
|
|
5184
|
+
* overlay set before it is put back to none, as `bindMeshPass` puts an overlay back; and no
|
|
5185
|
+
* material is left set after it, which is what `setMaterial(null)` leaves. The state a pass set —
|
|
5186
|
+
* the grain, the relief, the fog, the grade — is the list's to draw with, as it is any draw's, and
|
|
5187
|
+
* a list drawn under a different one is recorded again for it on WebGPU.
|
|
5188
|
+
*/
|
|
5189
|
+
drawStaticDraws(draws) {
|
|
5190
|
+
if (this.contextLost)
|
|
5191
|
+
return;
|
|
5192
|
+
const list = draws;
|
|
5193
|
+
if (list.disposed) {
|
|
5194
|
+
this.warnStaticDisposed();
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
5197
|
+
this.setSkinPalette(null);
|
|
5198
|
+
this.setMorphWeights(null);
|
|
5199
|
+
this.setCloth(null);
|
|
5200
|
+
this.setSurfaceOverlay(null);
|
|
5201
|
+
this.setMaterial(null);
|
|
5202
|
+
this.sceneCasterMaterial = undefined;
|
|
5203
|
+
replayStaticDraws(list, this.sceneCasterSink);
|
|
5204
|
+
this.setMaterial(null);
|
|
5205
|
+
this.sceneCasterMaterial = undefined;
|
|
5206
|
+
}
|
|
5207
|
+
/** Release a list. Its meshes, batches and materials are the caller's and are not released. */
|
|
5208
|
+
disposeStaticDraws(draws) {
|
|
5209
|
+
draws.disposed = true;
|
|
5210
|
+
}
|
|
5211
|
+
/** A disposed list drawn, said once: the frame loop does not throw. */
|
|
5212
|
+
warnStaticDisposed() {
|
|
5213
|
+
if (this.warnedStaticDisposed)
|
|
5214
|
+
return;
|
|
5215
|
+
this.warnedStaticDisposed = true;
|
|
5216
|
+
console.warn('drawStaticDraws: this list was disposed, and draws nothing.');
|
|
5217
|
+
}
|
|
5218
|
+
warnedStaticDisposed = false;
|
|
4756
5219
|
/**
|
|
4757
5220
|
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
4758
5221
|
*
|
|
@@ -4934,7 +5397,9 @@ export class WebGL2Renderer {
|
|
|
4934
5397
|
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
4935
5398
|
*/
|
|
4936
5399
|
/** The lit fragment source this renderer's profile compiles, at a light budget. */
|
|
4937
|
-
flatFragAt(budget,
|
|
5400
|
+
flatFragAt(budget,
|
|
5401
|
+
/* A switch counted as it would be, for asking whether it fits: see `switchFits`. */
|
|
5402
|
+
asIf = {}) {
|
|
4938
5403
|
return flatFrag({
|
|
4939
5404
|
pointShadows: this.quality.pointShadows,
|
|
4940
5405
|
directionalShadows: this.quality.directionalShadows,
|
|
@@ -4947,8 +5412,11 @@ export class WebGL2Renderer {
|
|
|
4947
5412
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
4948
5413
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
4949
5414
|
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
4950
|
-
surfaceOverlay:
|
|
5415
|
+
surfaceOverlay: asIf.SURFACE_OVERLAY ?? this.litOn.SURFACE_OVERLAY,
|
|
4951
5416
|
reflectionMaps: this.litOn.REFLECTION_MAPS,
|
|
5417
|
+
worldUvs: asIf.WORLD_UVS ?? this.litOn.WORLD_UVS,
|
|
5418
|
+
movingSun: asIf.MOVING_SUN ?? this.litOn.MOVING_SUN,
|
|
5419
|
+
layered: asIf.LAYERED ?? this.litOn.LAYERED,
|
|
4952
5420
|
maxLights: budget.maxLights,
|
|
4953
5421
|
maxAreaLights: budget.maxAreaLights,
|
|
4954
5422
|
});
|
|
@@ -4965,10 +5433,10 @@ export class WebGL2Renderer {
|
|
|
4965
5433
|
const { gl } = this;
|
|
4966
5434
|
Object.assign(this.litOn, this.litWanted);
|
|
4967
5435
|
this.flatFragSource = this.flatFragAt(this.lightBudget);
|
|
4968
|
-
const recompile = (old, skinned, morphed, instanced, label, eight = false) => {
|
|
5436
|
+
const recompile = (old, skinned, morphed, instanced, label, eight = false, animated = false) => {
|
|
4969
5437
|
if (old === null)
|
|
4970
5438
|
return null;
|
|
4971
|
-
const vertex = flatVert({ skinned, morphed, instanced });
|
|
5439
|
+
const vertex = flatVert({ skinned, morphed, instanced, animated });
|
|
4972
5440
|
const program = compileProgram(gl, eight ? skinEight(vertex) : vertex, this.flatFragSource, label);
|
|
4973
5441
|
gl.deleteProgram(old);
|
|
4974
5442
|
return { program, uniforms: uniformLocations(gl, program, label) };
|
|
@@ -4996,6 +5464,9 @@ export class WebGL2Renderer {
|
|
|
4996
5464
|
const instanced = recompile(this.flatInstancedProgram, false, false, true, 'flat.instanced');
|
|
4997
5465
|
this.flatInstancedProgram = instanced?.program ?? null;
|
|
4998
5466
|
this.flatInstancedUniforms = instanced?.uniforms ?? null;
|
|
5467
|
+
const animated = recompile(this.flatAnimatedProgram, false, false, true, 'flat.animated', false, true);
|
|
5468
|
+
this.flatAnimatedProgram = animated?.program ?? null;
|
|
5469
|
+
this.flatAnimatedUniforms = animated?.uniforms ?? null;
|
|
4999
5470
|
for (const [key, entry] of this.clothFlat) {
|
|
5000
5471
|
const eight = key.startsWith('8');
|
|
5001
5472
|
const withMorph = key.endsWith('m');
|
|
@@ -5005,6 +5476,10 @@ export class WebGL2Renderer {
|
|
|
5005
5476
|
gl.deleteProgram(entry.program);
|
|
5006
5477
|
this.clothFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
5007
5478
|
}
|
|
5479
|
+
/* Started against the source before this switch: begun again at the next draw that asks. */
|
|
5480
|
+
for (const started of this.modelPending.values())
|
|
5481
|
+
gl.deleteProgram(started.program);
|
|
5482
|
+
this.modelPending.clear();
|
|
5008
5483
|
for (const [key, entry] of this.modelFlat) {
|
|
5009
5484
|
const [variant, kind, half] = key.split('|');
|
|
5010
5485
|
const label = `flat.${variant}.${kind}.${half}`;
|
|
@@ -5013,7 +5488,7 @@ export class WebGL2Renderer {
|
|
|
5013
5488
|
this.modelFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
5014
5489
|
}
|
|
5015
5490
|
this.flatTargets.length = 0;
|
|
5016
|
-
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced]) {
|
|
5491
|
+
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced, animated]) {
|
|
5017
5492
|
if (target !== null)
|
|
5018
5493
|
this.flatTargets.push(target);
|
|
5019
5494
|
}
|
|
@@ -6544,6 +7019,7 @@ export class WebGL2Renderer {
|
|
|
6544
7019
|
ambient: env?.ambient ?? [0, 0, 0],
|
|
6545
7020
|
sunShadow,
|
|
6546
7021
|
lightViewProj: env?.lightViewProj ?? IDENTITY_MAT4,
|
|
7022
|
+
movingLightViewProj: env === null ? IDENTITY_MAT4 : this.movingLightFor(env),
|
|
6547
7023
|
/* The placeholder rather than null where a map is merely absent, for the reason
|
|
6548
7024
|
`emptyTexture.ts` gives. It is black, which as a depth is an occluder at the light,
|
|
6549
7025
|
so it is safe only because `sunShadow` is 0 whenever these maps do not exist. */
|
|
@@ -6777,15 +7253,31 @@ export class WebGL2Renderer {
|
|
|
6777
7253
|
this.currentSurfaceTexture = null;
|
|
6778
7254
|
/* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
|
|
6779
7255
|
this.overlayFloats.fill(0);
|
|
7256
|
+
/* Nor a projection: maps follow the mesh's coordinates until a material asks otherwise. */
|
|
7257
|
+
this.projection.fill(0);
|
|
7258
|
+
/* Nor light through from behind. See `SurfaceMaterial.diffuseTransmission`. */
|
|
7259
|
+
this.diffuseTransmission = 0;
|
|
7260
|
+
/* Nor layers: one layer until a material asks for more. */
|
|
7261
|
+
this.layerFloats.fill(0);
|
|
6780
7262
|
this.overlayMaps = null;
|
|
6781
7263
|
this.overlayTexture = null;
|
|
6782
7264
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
6783
7265
|
this.ambientSH.fill(0);
|
|
6784
7266
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
7267
|
+
this.passReflectivity = 0;
|
|
7268
|
+
this.passEnvironmentGain = 1;
|
|
7269
|
+
this.materialReflectivity = null;
|
|
7270
|
+
this.materialEnvironmentGain = null;
|
|
6785
7271
|
this.environmentDials[1] = 0;
|
|
6786
7272
|
this.environmentDials[2] = 1;
|
|
6787
7273
|
this.emission[0] = env.emissiveGain;
|
|
6788
7274
|
this.emission[1] = env.nightFactor;
|
|
7275
|
+
/* The moving layer's own matrix, the first time a frame draws that layer with one. */
|
|
7276
|
+
this.askMovingSun(env);
|
|
7277
|
+
/* No dither and no weighting: every draw whole, until a crossfade or an opacity asks. */
|
|
7278
|
+
this.oitWeight = 0;
|
|
7279
|
+
this.ditherFade = 0;
|
|
7280
|
+
this.ditherOpacity = 0;
|
|
6789
7281
|
for (const target of this.flatTargets) {
|
|
6790
7282
|
this.gl.useProgram(target.program);
|
|
6791
7283
|
this.writeMeshPassState(target.uniforms, camera, env);
|
|
@@ -6908,11 +7400,14 @@ export class WebGL2Renderer {
|
|
|
6908
7400
|
gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
|
|
6909
7401
|
/* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
|
|
6910
7402
|
gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
|
|
7403
|
+
gl.uniform4fv(u['uWorldUv'] ?? null, this.projection);
|
|
7404
|
+
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
6911
7405
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
6912
7406
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
6913
7407
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
6914
|
-
gl.
|
|
6915
|
-
|
|
7408
|
+
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
7409
|
+
/* White, and no light through from behind: no material at a pass's start. */
|
|
7410
|
+
gl.uniform4f(u['uGlassTint'] ?? null, 1, 1, 1, this.diffuseTransmission);
|
|
6916
7411
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
6917
7412
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
6918
7413
|
gl.uniform3fv(u['uAmbient'] ?? null, env.ambient);
|
|
@@ -6979,10 +7474,9 @@ export class WebGL2Renderer {
|
|
|
6979
7474
|
this.surfaceFog = FOG_RECEDE;
|
|
6980
7475
|
/* Grain on by default, so every scene written before it was a choice looks unchanged. */
|
|
6981
7476
|
gl.uniform1f(u['uGrain'] ?? null, 1);
|
|
6982
|
-
/*
|
|
6983
|
-
|
|
6984
|
-
this.
|
|
6985
|
-
gl.uniform2f(u['uWriteMode'] ?? null, 0, 0);
|
|
7477
|
+
/* The weighting and the dither as they stand, which `bindMeshPass` has just cleared: a program
|
|
7478
|
+
built in the middle of a pass takes what the caller set. See `setDitherFade`. */
|
|
7479
|
+
this.writeMode(u);
|
|
6986
7480
|
/* The draw's own ambient as it stands, which `bindMeshPass` has just cleared: a program built
|
|
6987
7481
|
in the middle of a pass takes what the caller set. See `setAmbientSH`. */
|
|
6988
7482
|
gl.uniform4fv(u['uAmbientSH[0]'] ?? null, this.ambientSH);
|
|
@@ -7021,11 +7515,18 @@ export class WebGL2Renderer {
|
|
|
7021
7515
|
gl.uniform1f(u['uShadowStrength'] ?? null, this.sunShadows === null ? 0 : env.shadowStrength);
|
|
7022
7516
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
7023
7517
|
gl.uniform1f(u['uShadowDepthSpan'] ?? null, env.shadowDepthSpan);
|
|
7518
|
+
/* The moving layer's own matrix and span, where the program reads one: see `movingLightFor`. */
|
|
7519
|
+
const moving = this.movingLightFor(env);
|
|
7520
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, moving);
|
|
7521
|
+
gl.uniform1f(u['uMovingShadowDepthSpan'] ?? null, orthographicDepthSpan(moving));
|
|
7024
7522
|
gl.uniform1f(u['uShadowMaxDistance'] ?? null, this.quality.directionalShadowMaxDistance);
|
|
7025
7523
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
7026
7524
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
7027
7525
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
7028
|
-
|
|
7526
|
+
/* The near corner, and in its w the frame the cutout dither moves with. See `uHighlightMin`.
|
|
7527
|
+
An environment written by hand may leave the box out, as the far corner says below. */
|
|
7528
|
+
const min = env.highlightMin;
|
|
7529
|
+
gl.uniform4f(u['uHighlightMin'] ?? null, min?.[0] ?? 0, min?.[1] ?? 0, min?.[2] ?? 0, this.cutoutFrame);
|
|
7029
7530
|
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
7030
7531
|
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
7031
7532
|
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
@@ -7328,6 +7829,9 @@ export class WebGL2Renderer {
|
|
|
7328
7829
|
* light and its colour, each into the blur's own target.
|
|
7329
7830
|
*/
|
|
7330
7831
|
const splits = this.skinSplits();
|
|
7832
|
+
/* A split skin draws its three halves or none, where a half may still be compiling. */
|
|
7833
|
+
if (splits && this.parallelCompile !== null && !this.skinHalvesReady(mesh))
|
|
7834
|
+
return;
|
|
7331
7835
|
/* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
|
|
7332
7836
|
const keeps = this.reflectionKeeps();
|
|
7333
7837
|
if (keeps)
|
|
@@ -7406,8 +7910,25 @@ export class WebGL2Renderer {
|
|
|
7406
7910
|
this.useFlatProgram();
|
|
7407
7911
|
}
|
|
7408
7912
|
/** One draw of a mesh, as `drawMesh` describes, of the half of a skin `half` names. */
|
|
7409
|
-
|
|
7410
|
-
|
|
7913
|
+
/**
|
|
7914
|
+
* Whether every half a split skin draws has its program, starting any that has not: a skin drawn
|
|
7915
|
+
* without its kept halves would lose its diffuse light for the frames they take to compile.
|
|
7916
|
+
*/
|
|
7917
|
+
skinHalvesReady(mesh) {
|
|
7918
|
+
const variant = this.drawVariantOf(mesh).name;
|
|
7919
|
+
let ready = true;
|
|
7920
|
+
for (const half of SPLIT_SKIN_HALVES) {
|
|
7921
|
+
if (this.modelProgram(variant, 'skin', half) === null)
|
|
7922
|
+
ready = false;
|
|
7923
|
+
}
|
|
7924
|
+
return ready;
|
|
7925
|
+
}
|
|
7926
|
+
/**
|
|
7927
|
+
* The vertex variant a draw of `mesh` takes now, and its program and uniforms where it is not the
|
|
7928
|
+
* plain one: what a model's program is keyed by, and what `drawMeshHalf` draws with.
|
|
7929
|
+
*/
|
|
7930
|
+
drawVariantOf(mesh) {
|
|
7931
|
+
const out = this.drawVariant;
|
|
7411
7932
|
/*
|
|
7412
7933
|
* A rigged mesh takes the skinned program, an unrigged one the plain program.
|
|
7413
7934
|
*
|
|
@@ -7435,9 +7956,8 @@ export class WebGL2Renderer {
|
|
|
7435
7956
|
* when none were asked for — an unbound sampler would default to a shadow map's unit.
|
|
7436
7957
|
*/
|
|
7437
7958
|
const cloth = wantSkin ? this.cloth : null;
|
|
7438
|
-
const
|
|
7439
|
-
|
|
7440
|
-
: this.clothFlat.get(`${eight ? 8 : 4}|${mesh.morph !== null ? 'm' : '-'}`);
|
|
7959
|
+
const clothKey = `${eight ? 8 : 4}|${mesh.morph !== null ? 'm' : '-'}`;
|
|
7960
|
+
const clothed = cloth === null ? undefined : this.clothFlat.get(clothKey);
|
|
7441
7961
|
const bothUniforms = eight ? this.flatBoth8Uniforms : this.flatBothUniforms;
|
|
7442
7962
|
const skinnedUniforms = eight ? this.flatSkinned8Uniforms : this.flatSkinnedUniforms;
|
|
7443
7963
|
const both = wantSkin && wantMorph && bothUniforms !== null;
|
|
@@ -7450,24 +7970,27 @@ export class WebGL2Renderer {
|
|
|
7450
7970
|
: morphed
|
|
7451
7971
|
? this.flatMorphedUniforms
|
|
7452
7972
|
: null;
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7973
|
+
out.skinned = skinned;
|
|
7974
|
+
out.morphed = morphed;
|
|
7975
|
+
out.cloth = cloth;
|
|
7976
|
+
out.clothed = clothed;
|
|
7977
|
+
out.uniforms = clothed?.uniforms ?? variant ?? this.flatUniforms;
|
|
7978
|
+
out.program =
|
|
7979
|
+
clothed !== undefined
|
|
7980
|
+
? clothed.program
|
|
7981
|
+
: skinned && morphed
|
|
7461
7982
|
? eight
|
|
7462
|
-
? this.
|
|
7463
|
-
: this.
|
|
7464
|
-
:
|
|
7465
|
-
?
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7983
|
+
? this.flatBoth8Program
|
|
7984
|
+
: this.flatBothProgram
|
|
7985
|
+
: skinned
|
|
7986
|
+
? eight
|
|
7987
|
+
? this.flatSkinned8Program
|
|
7988
|
+
: this.flatSkinnedProgram
|
|
7989
|
+
: morphed
|
|
7990
|
+
? this.flatMorphedProgram
|
|
7991
|
+
: null;
|
|
7992
|
+
out.name =
|
|
7993
|
+
clothed !== undefined
|
|
7471
7994
|
? `cloth${eight ? 8 : 4}${mesh.morph !== null ? 'm' : '-'}`
|
|
7472
7995
|
: skinned && morphed
|
|
7473
7996
|
? eight
|
|
@@ -7479,7 +8002,22 @@ export class WebGL2Renderer {
|
|
|
7479
8002
|
: 'skinned'
|
|
7480
8003
|
: morphed
|
|
7481
8004
|
? 'morphed'
|
|
7482
|
-
: 'plain'
|
|
8005
|
+
: 'plain';
|
|
8006
|
+
return out;
|
|
8007
|
+
}
|
|
8008
|
+
drawMeshHalf(mesh, model, depthLayer, tint, half) {
|
|
8009
|
+
const { gl } = this;
|
|
8010
|
+
const drawn = this.drawVariantOf(mesh);
|
|
8011
|
+
const { skinned, morphed, cloth, clothed } = drawn;
|
|
8012
|
+
let u = drawn.uniforms;
|
|
8013
|
+
let program = drawn.program;
|
|
8014
|
+
/* A material with a model takes that model's program for the same vertex variant. */
|
|
8015
|
+
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
8016
|
+
if (modelKind !== null) {
|
|
8017
|
+
const modelled = this.modelProgram(drawn.name, modelKind, half);
|
|
8018
|
+
/* Still compiling, where draws skip a compile: left out until it lands. */
|
|
8019
|
+
if (modelled === null)
|
|
8020
|
+
return;
|
|
7483
8021
|
program = modelled.program;
|
|
7484
8022
|
u = modelled.uniforms;
|
|
7485
8023
|
}
|
|
@@ -7658,6 +8196,9 @@ export class WebGL2Renderer {
|
|
|
7658
8196
|
/* A material with a model takes that model's plain program; see `modelProgram`. */
|
|
7659
8197
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
7660
8198
|
const modelled = modelKind === null ? null : this.modelProgram('plain', modelKind);
|
|
8199
|
+
/* Still compiling, where draws skip a compile: left out until it lands. */
|
|
8200
|
+
if (modelKind !== null && modelled === null)
|
|
8201
|
+
return;
|
|
7661
8202
|
const u = modelled?.uniforms ?? this.flatUniforms;
|
|
7662
8203
|
if (modelled === null)
|
|
7663
8204
|
this.useFlatProgram();
|
|
@@ -8137,6 +8678,7 @@ export class WebGL2Renderer {
|
|
|
8137
8678
|
/* Uncorrected, for the reason the lit program's copy of this line carries: the shaft's
|
|
8138
8679
|
lookup does its own `* 0.5 + 0.5` and correcting the matrix as well doubles it. */
|
|
8139
8680
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, env.lightViewProj);
|
|
8681
|
+
gl.uniformMatrix4fv(u['uMovingLightViewProj'] ?? null, false, this.movingLightFor(env));
|
|
8140
8682
|
gl.uniform1f(u['uShadowMapSize'] ?? null, this.quality.directionalShadowMapSize);
|
|
8141
8683
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
8142
8684
|
gl.activeTexture(gl.TEXTURE0);
|
|
@@ -8350,6 +8892,12 @@ function litVertexOf(variant) {
|
|
|
8350
8892
|
}
|
|
8351
8893
|
const skinned = variant.startsWith('skinned') || variant.startsWith('both');
|
|
8352
8894
|
const morphed = variant === 'morphed' || variant.startsWith('both');
|
|
8353
|
-
const
|
|
8895
|
+
const animated = variant === 'animated';
|
|
8896
|
+
const vertex = flatVert({
|
|
8897
|
+
skinned,
|
|
8898
|
+
morphed,
|
|
8899
|
+
instanced: variant === 'instanced' || animated,
|
|
8900
|
+
animated,
|
|
8901
|
+
});
|
|
8354
8902
|
return variant.endsWith('8') ? skinEight(vertex) : vertex;
|
|
8355
8903
|
}
|