@driftengine/core 4.13.6 → 4.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +62 -0
- package/README.md +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/render/backend/api.d.ts +19 -3
- package/dist/render/backend/webgl2/motionTarget.d.ts +39 -0
- package/dist/render/backend/webgl2/motionTarget.js +254 -0
- package/dist/render/backend/webgl2/pointSkinFeedback.d.ts +13 -3
- package/dist/render/backend/webgl2/pointSkinFeedback.js +45 -12
- package/dist/render/backend/webgl2/renderer.d.ts +101 -7
- package/dist/render/backend/webgl2/renderer.js +337 -9
- package/dist/render/backend/webgpu/pointSkinPass.d.ts +16 -3
- package/dist/render/backend/webgpu/pointSkinPass.js +79 -21
- package/dist/render/backend/webgpu/postPass.d.ts +6 -1
- package/dist/render/backend/webgpu/renderer.d.ts +41 -0
- package/dist/render/backend/webgpu/renderer.js +143 -5
- package/dist/render/baseDecals.d.ts +48 -0
- package/dist/render/baseDecals.js +71 -0
- package/dist/render/emissiveFlow.d.ts +40 -0
- package/dist/render/emissiveFlow.js +51 -0
- package/dist/render/fixtureAtlas.d.ts +8 -3
- package/dist/render/fixtureAtlas.js +17 -6
- package/dist/render/globalMedium.d.ts +17 -0
- package/dist/render/globalMedium.js +14 -0
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/iridescentOverlay.d.ts +78 -0
- package/dist/render/iridescentOverlay.js +102 -0
- package/dist/render/mesh.d.ts +24 -0
- package/dist/render/mesh.js +79 -0
- package/dist/render/pointSkin.d.ts +18 -0
- package/dist/render/pointSkin.js +50 -5
- package/dist/render/recon/mover.d.ts +3 -2
- package/dist/render/recon/mover.js +3 -2
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shaders/flat/baseDecals.d.ts +17 -0
- package/dist/render/shaders/flat/baseDecals.js +71 -0
- package/dist/render/shaders/flat/emissiveFlow.d.ts +15 -0
- package/dist/render/shaders/flat/emissiveFlow.js +45 -0
- 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 +16 -0
- package/dist/render/shaders/flat/index.js +9 -0
- package/dist/render/shaders/flat/iridescentOverlay.d.ts +15 -0
- package/dist/render/shaders/flat/iridescentOverlay.js +65 -0
- package/dist/render/shaders/flat/layered.js +4 -4
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +12 -1
- package/dist/render/shaders/flat/mapReads.d.ts +1 -1
- package/dist/render/shaders/flat/mapReads.js +7 -3
- package/dist/render/shaders/flat/worldUv.js +6 -5
- package/dist/render/shaders/generated/flat.wgsl.d.ts +480 -16
- package/dist/render/shaders/generated/flat.wgsl.js +519 -55
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +15 -5
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +15 -5
- package/dist/render/shaders/temporalResolve.js +25 -9
- package/dist/render/surfaceTexture.d.ts +32 -0
- package/dist/render/temporalPass.d.ts +3 -1
- package/dist/render/temporalPass.js +9 -1
- package/package.json +6 -6
- package/src/index.ts +3 -0
- package/src/render/backend/api.ts +19 -2
- package/src/render/backend/webgl2/motionTarget.ts +299 -0
- package/src/render/backend/webgl2/pointSkinFeedback.ts +49 -14
- package/src/render/backend/webgl2/renderer.ts +411 -9
- package/src/render/backend/webgpu/pointSkinPass.ts +90 -23
- package/src/render/backend/webgpu/renderer.ts +163 -3
- package/src/render/baseDecals.ts +93 -0
- package/src/render/emissiveFlow.ts +66 -0
- package/src/render/fixtureAtlas.ts +26 -7
- package/src/render/globalMedium.ts +31 -0
- package/src/render/globalMediumPass.ts +3 -7
- package/src/render/iridescentOverlay.ts +177 -0
- package/src/render/mesh.ts +82 -0
- package/src/render/pointSkin.ts +64 -5
- package/src/render/recon/mover.ts +3 -2
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shaders/flat/baseDecals.ts +71 -0
- package/src/render/shaders/flat/emissiveFlow.ts +45 -0
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +25 -0
- package/src/render/shaders/flat/iridescentOverlay.ts +65 -0
- package/src/render/shaders/flat/layered.ts +4 -4
- package/src/render/shaders/flat/main.ts +12 -1
- package/src/render/shaders/flat/mapReads.ts +7 -3
- package/src/render/shaders/flat/worldUv.ts +6 -5
- package/src/render/shaders/generated/flat.wgsl.ts +519 -55
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +15 -5
- package/src/render/shaders/temporalResolve.ts +25 -9
- package/src/render/surfaceTexture.ts +32 -0
- package/src/render/temporalPass.ts +8 -0
|
@@ -8,6 +8,9 @@ import { maskOf } from '../../frame/resources.js';
|
|
|
8
8
|
/** The two copies a registered pass may declare it reads. See `drawPass`. */
|
|
9
9
|
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
10
10
|
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
11
|
+
import { MOVER_MOTION, MOVER_TWICE, advanceMover, claimRendererId, isMover, } from '../../recon/mover.js';
|
|
12
|
+
import { frameOfChange, keepsPrevious } from '../../recon/changeFrames.js';
|
|
13
|
+
import { GlMotionPass } from './motionTarget.js';
|
|
11
14
|
import { resolveGlass } from '../../glass.js';
|
|
12
15
|
import { PickableSet } from '../../pickable.js';
|
|
13
16
|
import { TEMPORAL_HISTORY_BLEND, TemporalHistory, jitterOffset, jitterProjection, } from '../../temporalAa.js';
|
|
@@ -44,6 +47,10 @@ import { orthographicDepthSpan } from '../../lightMatrix.js';
|
|
|
44
47
|
import { LAYER_FLOATS, layerMaskMap, layeredProjection, layersFitBeside, packSurfaceLayers, } from '../../surfaceLayers.js';
|
|
45
48
|
import { DETAIL_FLOATS, detailOf, detailRefusal, detailRefusalWarning, packSurfaceDetail, } from '../../surfaceDetail.js';
|
|
46
49
|
import { LOOK_FLOATS, asksLayerLooks, packLayerLooks } from '../../surfaceLayerLooks.js';
|
|
50
|
+
import { EMISSIVE_FLOW_FLOATS, packEmissiveFlow } from '../../emissiveFlow.js';
|
|
51
|
+
import { sourceSize } from '../../textureSource.js';
|
|
52
|
+
import { BASE_DECAL_FLOATS, BASE_DECAL_TILE, packBaseDecals, } from '../../baseDecals.js';
|
|
53
|
+
import { IRIDESCENT_FLOATS, iridescentRefusal, packIridescentOverlay, } from '../../iridescentOverlay.js';
|
|
47
54
|
import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
|
|
48
55
|
import { SceneTarget } from '../../sceneTarget.js';
|
|
49
56
|
import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
|
|
@@ -79,7 +86,7 @@ import { ProbeSweeps } from '../../probeSweeps.js';
|
|
|
79
86
|
import { octahedralEdgeFor } from '../../prefilterEnvMap.js';
|
|
80
87
|
import { EnvironmentPrefilterPass } from '../../prefilterPass.js';
|
|
81
88
|
import { packIesAtlas } from '../../iesProfile.js';
|
|
82
|
-
import { fixtureAtlasLayout, fixtureAtlasTexels } from '../../fixtureAtlas.js';
|
|
89
|
+
import { decalBandTop, fixtureAtlasLayout, fixtureAtlasTexels } from '../../fixtureAtlas.js';
|
|
83
90
|
import { resolveSurfaceScene } from '../../surfaceScene.js';
|
|
84
91
|
import { MAX_AREA_LIGHTS } from '../../areaLights.js';
|
|
85
92
|
import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAreaShadowLayer, AreaShadowSet, } from '../../areaShadowSet.js';
|
|
@@ -136,7 +143,7 @@ import { CUTOUT_RESOLVE_CODE, resolveCutout } from '../../cutoutDither.js';
|
|
|
136
143
|
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.js';
|
|
137
144
|
import { VIEW_MODEL_DEPTH_SHARE, viewModelDepthRange } from '../../viewModel.js';
|
|
138
145
|
import { noLitSwitches } from '../../shaders/flat/glassTint.js';
|
|
139
|
-
import { poseBounds, validatePointSkin, validatePose, } from '../../pointSkin.js';
|
|
146
|
+
import { poseBounds, validatePointSkin, validatePointSkinRange, validatePose, } from '../../pointSkin.js';
|
|
140
147
|
import { GlPointSkin, createPointSkinPose } from './pointSkinFeedback.js';
|
|
141
148
|
/**
|
|
142
149
|
* How many frames `firstFrameSettled` will ask before it stops asking.
|
|
@@ -900,6 +907,17 @@ export class WebGL2Renderer {
|
|
|
900
907
|
iesAtlas = packIesAtlas([]);
|
|
901
908
|
/** The cookie images, kept so a new set of profiles can rebuild the atlas around them. */
|
|
902
909
|
cookieImages = [];
|
|
910
|
+
/** The images the frame's base decals lay, in the atlas's band of them. See `setBaseDecalImages`. */
|
|
911
|
+
decalImages = [];
|
|
912
|
+
/** The decals as last laid, packed again whenever the atlas moves their band. */
|
|
913
|
+
baseDecalList = [];
|
|
914
|
+
/** `uBaseDecals`, held until the next `setBaseDecals`, across passes. */
|
|
915
|
+
baseDecalFloats = new Float32Array(BASE_DECAL_FLOATS);
|
|
916
|
+
/** Whether the lit stage has room for base decals, asked once. */
|
|
917
|
+
decalRoom = null;
|
|
918
|
+
warnedDecalRoom = false;
|
|
919
|
+
/** The refusals of a decal or its image already said, each once. */
|
|
920
|
+
warnedBaseDecals = new Set();
|
|
903
921
|
/** Rows of profile in the atlas. One until a consumer loads a profile, which is the row of ones. */
|
|
904
922
|
iesRows = 1;
|
|
905
923
|
/** Cookie tiles in the atlas. 0 is none, and the whole off path. */
|
|
@@ -982,6 +1000,18 @@ export class WebGL2Renderer {
|
|
|
982
1000
|
warnedLooksRoom = false;
|
|
983
1001
|
/** `uDetail`, the material's tiled detail packed by `packSurfaceDetail`. */
|
|
984
1002
|
detailFloats = new Float32Array(DETAIL_FLOATS);
|
|
1003
|
+
/** `uEmissiveFlow`, a material's glow moved and masked, packed by `packEmissiveFlow`. */
|
|
1004
|
+
flowFloats = new Float32Array(EMISSIVE_FLOW_FLOATS);
|
|
1005
|
+
/** Whether the lit stage has room for that, asked once. See `emissiveFlowFits`. */
|
|
1006
|
+
flowRoom = null;
|
|
1007
|
+
warnedFlowRoom = false;
|
|
1008
|
+
/** `uIridescence`, a material's iridescent overlay packed by `packIridescentOverlay`. */
|
|
1009
|
+
iridescentFloats = new Float32Array(IRIDESCENT_FLOATS);
|
|
1010
|
+
/** Whether the lit stage has room for it, asked once. See `iridescentOf`. */
|
|
1011
|
+
iridescentRoom = null;
|
|
1012
|
+
warnedIridescentRoom = false;
|
|
1013
|
+
/** The refusals of an overlay already said, each once. */
|
|
1014
|
+
warnedIridescent = new Set();
|
|
985
1015
|
/** Whether the lit stage has room for detail on this part, asked once. See `detailOfMaterial`. */
|
|
986
1016
|
detailRoom = null;
|
|
987
1017
|
warnedDetailRoom = false;
|
|
@@ -1125,6 +1155,19 @@ export class WebGL2Renderer {
|
|
|
1125
1155
|
return this.sceneViewProj;
|
|
1126
1156
|
}
|
|
1127
1157
|
shadowPassActive = false;
|
|
1158
|
+
/**
|
|
1159
|
+
* The motion WebGL2's temporal resolve follows: the frame's movers, recorded as they are drawn and
|
|
1160
|
+
* drawn into a target of their own at `endFrame`. See `motionTarget.ts`.
|
|
1161
|
+
*/
|
|
1162
|
+
motionPass = new GlMotionPass();
|
|
1163
|
+
/** Which renderer a `Mover` was last drawn by, and the frame serial it reads. See `mover.ts`. */
|
|
1164
|
+
moverRenderer = claimRendererId();
|
|
1165
|
+
moverFrame = 0;
|
|
1166
|
+
/** Between `beginFrame` and `endFrame`, which decides which frame a rewrite belongs to. */
|
|
1167
|
+
inFrame = false;
|
|
1168
|
+
motionJittered = new Float32Array(16);
|
|
1169
|
+
warnedMoverTwice = false;
|
|
1170
|
+
warnedMotionUnkept = false;
|
|
1128
1171
|
/** Non-zero while the drawing buffer is pinned; see `lockDrawingBuffer`. */
|
|
1129
1172
|
lockedWidth = 0;
|
|
1130
1173
|
lockedHeight = 0;
|
|
@@ -1810,11 +1853,95 @@ export class WebGL2Renderer {
|
|
|
1810
1853
|
setSpotCookies(images) {
|
|
1811
1854
|
this.cookieImages = images.slice();
|
|
1812
1855
|
this.buildFixtureAtlas();
|
|
1856
|
+
/* The decals' band starts below the cookies, so their rows move with them. */
|
|
1857
|
+
if (this.baseDecalList.length > 0)
|
|
1858
|
+
this.setBaseDecals(this.baseDecalList);
|
|
1813
1859
|
}
|
|
1814
1860
|
setIesProfiles(profiles) {
|
|
1815
1861
|
this.iesAtlas = packIesAtlas(profiles);
|
|
1816
1862
|
this.buildFixtureAtlas();
|
|
1817
1863
|
}
|
|
1864
|
+
/**
|
|
1865
|
+
* The images the frame's base decals lay, each a `BASE_DECAL_TILE` square, into the fixture
|
|
1866
|
+
* atlas's band of them. **Once a frame is what this is for**: the same count again uploads the
|
|
1867
|
+
* squares and nothing else, while a different count builds the atlas again. An image of another
|
|
1868
|
+
* size is refused, said once, and its square left clear. See `baseDecals.ts`.
|
|
1869
|
+
*/
|
|
1870
|
+
setBaseDecalImages(images) {
|
|
1871
|
+
if (this.contextLost)
|
|
1872
|
+
return;
|
|
1873
|
+
if (images.length === this.decalImages.length && this.fixtureTexture !== null) {
|
|
1874
|
+
this.decalImages = images;
|
|
1875
|
+
const { gl } = this;
|
|
1876
|
+
gl.activeTexture(gl.TEXTURE0 + FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
1877
|
+
gl.bindTexture(gl.TEXTURE_2D, this.fixtureTexture);
|
|
1878
|
+
this.uploadDecalImages();
|
|
1879
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1880
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
this.decalImages = images.slice();
|
|
1884
|
+
this.buildFixtureAtlas();
|
|
1885
|
+
this.setBaseDecals(this.baseDecalList);
|
|
1886
|
+
}
|
|
1887
|
+
/** The decal images into their squares of the atlas bound on the fixture unit. */
|
|
1888
|
+
uploadDecalImages() {
|
|
1889
|
+
const { gl } = this;
|
|
1890
|
+
for (let tile = 0; tile < this.decalImages.length; tile++) {
|
|
1891
|
+
const image = this.decalImages[tile];
|
|
1892
|
+
if (image === undefined || !this.decalImageFits(image))
|
|
1893
|
+
continue;
|
|
1894
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, tile * BASE_DECAL_TILE, decalBandTop(this.cookieImages.length), gl.RGBA, gl.HALF_FLOAT, image);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
/** Whether an image is a decal's square, said once where it is not. */
|
|
1898
|
+
decalImageFits(image) {
|
|
1899
|
+
const { width, height } = sourceSize(image);
|
|
1900
|
+
if (width === BASE_DECAL_TILE && height === BASE_DECAL_TILE)
|
|
1901
|
+
return true;
|
|
1902
|
+
const refusal = `a base decal image of ${width} by ${height}; it is a square of ${BASE_DECAL_TILE} ` +
|
|
1903
|
+
'texels, and is left clear';
|
|
1904
|
+
if (!this.warnedBaseDecals.has(refusal)) {
|
|
1905
|
+
this.warnedBaseDecals.add(refusal);
|
|
1906
|
+
console.warn(`Renderer: ${refusal}.`);
|
|
1907
|
+
}
|
|
1908
|
+
return false;
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* The decals laid into the base colours of the draws after it, until the next call, across
|
|
1912
|
+
* passes: each a box, an image of `setBaseDecalImages` and how much of it. **Refused, said once,
|
|
1913
|
+
* where the lit stage has no room** for their thirty-four vectors, and the draws are then marked
|
|
1914
|
+
* by none. See `baseDecals.ts`.
|
|
1915
|
+
*/
|
|
1916
|
+
setBaseDecals(decals) {
|
|
1917
|
+
if (this.contextLost)
|
|
1918
|
+
return;
|
|
1919
|
+
this.baseDecalList = decals;
|
|
1920
|
+
if (decals.length > 0 && !this.baseDecalsFit()) {
|
|
1921
|
+
this.baseDecalFloats.fill(0);
|
|
1922
|
+
}
|
|
1923
|
+
else {
|
|
1924
|
+
const refusal = packBaseDecals(decals, this.decalImages.length, decalBandTop(this.cookieImages.length), this.baseDecalFloats);
|
|
1925
|
+
if (refusal !== null && !this.warnedBaseDecals.has(refusal)) {
|
|
1926
|
+
this.warnedBaseDecals.add(refusal);
|
|
1927
|
+
console.warn(`Renderer: ${refusal}.`);
|
|
1928
|
+
}
|
|
1929
|
+
if (this.baseDecalFloats[0] > 0)
|
|
1930
|
+
this.askLit('BASE_DECALS');
|
|
1931
|
+
}
|
|
1932
|
+
this.materials.dirty();
|
|
1933
|
+
this.writeFlatVector('uBaseDecals', this.baseDecalFloats);
|
|
1934
|
+
}
|
|
1935
|
+
baseDecalsFit() {
|
|
1936
|
+
this.decalRoom ??= this.switchFits('BASE_DECALS');
|
|
1937
|
+
if (!this.decalRoom && !this.warnedDecalRoom) {
|
|
1938
|
+
this.warnedDecalRoom = true;
|
|
1939
|
+
console.warn('Renderer: base decals are refused on this device. They take thirty-four fragment uniform ' +
|
|
1940
|
+
'vectors the lit shader does not have, so nothing is marked. A lower `maxLights` makes ' +
|
|
1941
|
+
'the room, or WebGPU has it.');
|
|
1942
|
+
}
|
|
1943
|
+
return this.decalRoom;
|
|
1944
|
+
}
|
|
1818
1945
|
/**
|
|
1819
1946
|
* The fixture atlas from the profiles and cookies in hand: the profiles' rows at the bottom, the
|
|
1820
1947
|
* cookies' tiles on top, one `RGBA16F` texture. See `fixtureAtlas.ts` for the layout.
|
|
@@ -1826,7 +1953,7 @@ export class WebGL2Renderer {
|
|
|
1826
1953
|
*/
|
|
1827
1954
|
buildFixtureAtlas() {
|
|
1828
1955
|
const { gl } = this;
|
|
1829
|
-
const layout = fixtureAtlasLayout(this.iesAtlas.height, this.cookieImages.length);
|
|
1956
|
+
const layout = fixtureAtlasLayout(this.iesAtlas.height, this.cookieImages.length, this.decalImages.length);
|
|
1830
1957
|
if (this.fixtureTexture === null)
|
|
1831
1958
|
this.fixtureTexture = gl.createTexture();
|
|
1832
1959
|
gl.activeTexture(gl.TEXTURE0 + FIXTURE_ATLAS_TEXTURE_UNIT);
|
|
@@ -1838,6 +1965,7 @@ export class WebGL2Renderer {
|
|
|
1838
1965
|
continue;
|
|
1839
1966
|
gl.texSubImage2D(gl.TEXTURE_2D, 0, tile * COOKIE_TILE, 0, gl.RGBA, gl.HALF_FLOAT, image);
|
|
1840
1967
|
}
|
|
1968
|
+
this.uploadDecalImages();
|
|
1841
1969
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
1842
1970
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1843
1971
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
@@ -2481,6 +2609,8 @@ export class WebGL2Renderer {
|
|
|
2481
2609
|
const { gl } = this;
|
|
2482
2610
|
if (!this.contextLost)
|
|
2483
2611
|
this.skinScatter?.dispose();
|
|
2612
|
+
if (!this.contextLost)
|
|
2613
|
+
this.motionPass.dispose(gl);
|
|
2484
2614
|
this.detachContextLoss();
|
|
2485
2615
|
this.contextLostListeners.length = 0;
|
|
2486
2616
|
this.contextRestoredListeners.length = 0;
|
|
@@ -2652,6 +2782,8 @@ export class WebGL2Renderer {
|
|
|
2652
2782
|
* buffer is the same buffer. Rebinding here would cost a state change for nothing.
|
|
2653
2783
|
*/
|
|
2654
2784
|
updateMesh(mesh, positions, normals) {
|
|
2785
|
+
if (!this.contextLost)
|
|
2786
|
+
this.keepPositionsBefore(mesh);
|
|
2655
2787
|
mesh.update(this.gl, positions, normals);
|
|
2656
2788
|
}
|
|
2657
2789
|
/** The program and feedback object every point skin poses with, made at the first one. */
|
|
@@ -2679,8 +2811,19 @@ export class WebGL2Renderer {
|
|
|
2679
2811
|
if (this.contextLost || this.pointSkinPose === null)
|
|
2680
2812
|
return;
|
|
2681
2813
|
poseBounds(matrices, skin.reach, skin.mesh.bounds);
|
|
2814
|
+
this.keepPositionsBefore(skin.mesh);
|
|
2682
2815
|
skin.pose(this.gl, this.pointSkinPose, matrices);
|
|
2683
2816
|
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Rewrite part of a skin in place. A command in the stream like a pose, so the pose after it reads
|
|
2819
|
+
* the rewrite. See `GlPointSkin.rewrite`.
|
|
2820
|
+
*/
|
|
2821
|
+
updatePointSkin(skin, first, data) {
|
|
2822
|
+
validatePointSkinRange(data, first, skin.mesh.vertexCount);
|
|
2823
|
+
if (this.contextLost)
|
|
2824
|
+
return;
|
|
2825
|
+
skin.rewrite(this.gl, first, data);
|
|
2826
|
+
}
|
|
2684
2827
|
disposePointSkin(skin) {
|
|
2685
2828
|
skin.dispose(this.gl);
|
|
2686
2829
|
}
|
|
@@ -3300,6 +3443,22 @@ export class WebGL2Renderer {
|
|
|
3300
3443
|
this.materialFlags[2] = emissiveMap === null ? 0 : 1;
|
|
3301
3444
|
const emissiveScale = material?.emissiveScale ?? null;
|
|
3302
3445
|
gl.uniform3f(u['uEmissiveScale'] ?? null, emissiveScale?.[0] ?? 1, emissiveScale?.[1] ?? 1, emissiveScale?.[2] ?? 1);
|
|
3446
|
+
/* And its glow's scroll and mask, where the lit stage has room for them: emissiveFlow.ts. */
|
|
3447
|
+
if (packEmissiveFlow(material, this.flowFloats) && this.emissiveFlowFits()) {
|
|
3448
|
+
this.askLit('EMISSIVE_FLOW');
|
|
3449
|
+
}
|
|
3450
|
+
else {
|
|
3451
|
+
this.flowFloats.fill(0, 0, 4);
|
|
3452
|
+
}
|
|
3453
|
+
gl.uniform4fv(u['uEmissiveFlow'] ?? null, this.flowFloats);
|
|
3454
|
+
/* And its iridescent overlay, where it can be drawn: iridescentOverlay.ts. */
|
|
3455
|
+
const iridescent = this.iridescentOf(material);
|
|
3456
|
+
packIridescentOverlay(iridescent, material ?? {}, this.iridescentFloats);
|
|
3457
|
+
if (iridescent !== null)
|
|
3458
|
+
this.askLit('IRIDESCENT_OVERLAY');
|
|
3459
|
+
gl.uniform4fv(u['uIridescence'] ?? null, this.iridescentFloats);
|
|
3460
|
+
/* And whether the frame's decals mark it. See `SurfaceMaterial.receivesDecals`. */
|
|
3461
|
+
gl.uniform4f(u['uDecalReceiver'] ?? null, material?.receivesDecals === false ? 1 : 0, 0, 0, 0);
|
|
3303
3462
|
/*
|
|
3304
3463
|
* **The scale belongs to the material, not to the albedo.** It was uploaded on the albedo
|
|
3305
3464
|
* path alone, which was right while albedo was the only map: a material with a normal and no
|
|
@@ -3530,6 +3689,49 @@ export class WebGL2Renderer {
|
|
|
3530
3689
|
this.overlayRoom ??= this.switchFits('SURFACE_OVERLAY');
|
|
3531
3690
|
return this.overlayRoom;
|
|
3532
3691
|
}
|
|
3692
|
+
/**
|
|
3693
|
+
* A material's iridescent overlay, or null where it is refused: malformed, or the lit stage has no
|
|
3694
|
+
* room for its thirty-six vectors. Each said once.
|
|
3695
|
+
*/
|
|
3696
|
+
iridescentOf(material) {
|
|
3697
|
+
const overlay = material?.iridescentOverlay ?? null;
|
|
3698
|
+
if (overlay === null)
|
|
3699
|
+
return null;
|
|
3700
|
+
const refusal = iridescentRefusal(overlay);
|
|
3701
|
+
if (refusal !== null) {
|
|
3702
|
+
if (!this.warnedIridescent.has(refusal)) {
|
|
3703
|
+
this.warnedIridescent.add(refusal);
|
|
3704
|
+
console.warn(`Renderer: an iridescent overlay is refused: ${refusal}. Drawn without it.`);
|
|
3705
|
+
}
|
|
3706
|
+
return null;
|
|
3707
|
+
}
|
|
3708
|
+
this.iridescentRoom ??= this.switchFits('IRIDESCENT_OVERLAY');
|
|
3709
|
+
if (this.iridescentRoom)
|
|
3710
|
+
return overlay;
|
|
3711
|
+
if (!this.warnedIridescentRoom) {
|
|
3712
|
+
this.warnedIridescentRoom = true;
|
|
3713
|
+
console.warn("Renderer: a material's iridescent overlay is refused on this device. It takes thirty-six " +
|
|
3714
|
+
'fragment uniform vectors the lit shader does not have, so the material is drawn without ' +
|
|
3715
|
+
'it. A lower `maxLights` makes the room, or WebGPU has it.');
|
|
3716
|
+
}
|
|
3717
|
+
return null;
|
|
3718
|
+
}
|
|
3719
|
+
/**
|
|
3720
|
+
* Whether a material's glow can be moved and masked: the lit stage has room for its two vectors.
|
|
3721
|
+
* **Refused, said once, where it has not**, and the glow is then drawn still and unmasked, as it
|
|
3722
|
+
* was before it existed.
|
|
3723
|
+
*/
|
|
3724
|
+
emissiveFlowFits() {
|
|
3725
|
+
this.flowRoom ??= this.switchFits('EMISSIVE_FLOW');
|
|
3726
|
+
if (!this.flowRoom && !this.warnedFlowRoom) {
|
|
3727
|
+
this.warnedFlowRoom = true;
|
|
3728
|
+
console.warn("Renderer: a material's emissive scroll and mask are refused on this device. They take " +
|
|
3729
|
+
'two fragment uniform vectors and the lit shader already uses all ' +
|
|
3730
|
+
`${this.fragmentVectorLimit()} this GPU offers; the glow is drawn still and unmasked. ` +
|
|
3731
|
+
'A lower `maxLights` makes the room, or WebGPU has it.');
|
|
3732
|
+
}
|
|
3733
|
+
return this.flowRoom;
|
|
3734
|
+
}
|
|
3533
3735
|
/**
|
|
3534
3736
|
* Whether a material's projection can be drawn: none asked, or the lit stage has the room for its
|
|
3535
3737
|
* one vector. **Refused, said once, where it has not**, as the overlay is, and the material's
|
|
@@ -5666,6 +5868,9 @@ export class WebGL2Renderer {
|
|
|
5666
5868
|
layered: asIf.LAYERED ?? this.litOn.LAYERED,
|
|
5667
5869
|
layerLooks: asIf.LAYER_LOOKS ?? this.litOn.LAYER_LOOKS,
|
|
5668
5870
|
surfaceDetail: asIf.SURFACE_DETAIL ?? this.litOn.SURFACE_DETAIL,
|
|
5871
|
+
emissiveFlow: asIf.EMISSIVE_FLOW ?? this.litOn.EMISSIVE_FLOW,
|
|
5872
|
+
iridescentOverlay: asIf.IRIDESCENT_OVERLAY ?? this.litOn.IRIDESCENT_OVERLAY,
|
|
5873
|
+
baseDecals: asIf.BASE_DECALS ?? this.litOn.BASE_DECALS,
|
|
5669
5874
|
maxLights: budget.maxLights,
|
|
5670
5875
|
maxAreaLights: budget.maxAreaLights,
|
|
5671
5876
|
});
|
|
@@ -6890,6 +7095,10 @@ export class WebGL2Renderer {
|
|
|
6890
7095
|
return;
|
|
6891
7096
|
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
6892
7097
|
this.temporalHistory.nextFrame();
|
|
7098
|
+
/* A frame states its movers afresh. See `motionTarget.ts`. */
|
|
7099
|
+
this.moverFrame += 1;
|
|
7100
|
+
this.inFrame = true;
|
|
7101
|
+
this.motionPass.reset();
|
|
6893
7102
|
this.skinScattered = false;
|
|
6894
7103
|
this.skinScatter?.reset();
|
|
6895
7104
|
if (this.litAsked) {
|
|
@@ -6978,6 +7187,8 @@ export class WebGL2Renderer {
|
|
|
6978
7187
|
this.endViewModel();
|
|
6979
7188
|
/* Skin not yet spread, because nothing blended came after it, is spread now. */
|
|
6980
7189
|
this.scatterSkin();
|
|
7190
|
+
/* A draw after the frame, an overlay over the presented picture, is no mover of it. */
|
|
7191
|
+
this.inFrame = false;
|
|
6981
7192
|
/*
|
|
6982
7193
|
* Camera motion blur, built from the camera the frame was *rendered* with.
|
|
6983
7194
|
*
|
|
@@ -7000,6 +7211,10 @@ export class WebGL2Renderer {
|
|
|
7000
7211
|
* reproject through a `previousViewProj` that had never been written.
|
|
7001
7212
|
*/
|
|
7002
7213
|
const wantsReprojection = blur > 0 || this.temporalJittering;
|
|
7214
|
+
/* The movers' own motion, drawn while last frame's view is still the one kept: motionTarget.ts. */
|
|
7215
|
+
const moved = this.temporalJittering && view !== null && this.hasPreviousView
|
|
7216
|
+
? this.drawMotionPass(view)
|
|
7217
|
+
: null;
|
|
7003
7218
|
if (wantsReprojection && view !== null) {
|
|
7004
7219
|
if (this.hasPreviousView) {
|
|
7005
7220
|
mat4.invert(this.reprojection, view);
|
|
@@ -7034,6 +7249,7 @@ export class WebGL2Renderer {
|
|
|
7034
7249
|
blend: this.temporalHistoryUsable ? TEMPORAL_HISTORY_BLEND : 0,
|
|
7035
7250
|
periodStart: this.temporalHistory.periodStart,
|
|
7036
7251
|
depthToViewZ: this.taaDepthToViewZ,
|
|
7252
|
+
motion: moved,
|
|
7037
7253
|
};
|
|
7038
7254
|
}
|
|
7039
7255
|
/*
|
|
@@ -7530,6 +7746,10 @@ export class WebGL2Renderer {
|
|
|
7530
7746
|
this.layerFloats.fill(0);
|
|
7531
7747
|
this.lookFloats.fill(0);
|
|
7532
7748
|
this.detailFloats.fill(0);
|
|
7749
|
+
/* Nor a glow that moves or is masked. */
|
|
7750
|
+
this.flowFloats.fill(0);
|
|
7751
|
+
/* Nor an iridescent overlay. */
|
|
7752
|
+
this.iridescentFloats.fill(0);
|
|
7533
7753
|
this.overlayMaps = null;
|
|
7534
7754
|
this.overlayTexture = null;
|
|
7535
7755
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
@@ -7677,6 +7897,11 @@ export class WebGL2Renderer {
|
|
|
7677
7897
|
gl.uniform4fv(u['uLayers'] ?? null, this.layerFloats);
|
|
7678
7898
|
gl.uniform4fv(u['uLayerLooks'] ?? null, this.lookFloats);
|
|
7679
7899
|
gl.uniform4fv(u['uDetail'] ?? null, this.detailFloats);
|
|
7900
|
+
gl.uniform4fv(u['uEmissiveFlow'] ?? null, this.flowFloats);
|
|
7901
|
+
gl.uniform4fv(u['uIridescence'] ?? null, this.iridescentFloats);
|
|
7902
|
+
/* The frame's decals as last laid, held across passes, and every surface receiving them. */
|
|
7903
|
+
gl.uniform4fv(u['uBaseDecals'] ?? null, this.baseDecalFloats);
|
|
7904
|
+
gl.uniform4f(u['uDecalReceiver'] ?? null, 0, 0, 0, 0);
|
|
7680
7905
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
7681
7906
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
7682
7907
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
@@ -8056,12 +8281,10 @@ export class WebGL2Renderer {
|
|
|
8056
8281
|
*/
|
|
8057
8282
|
tint = null,
|
|
8058
8283
|
/**
|
|
8059
|
-
* Where the mesh was last frame
|
|
8060
|
-
*
|
|
8061
|
-
*
|
|
8062
|
-
*
|
|
8063
|
-
* here, so there is no motion target for this to be written into, and a mover passed here is
|
|
8064
|
-
* neither read nor advanced.
|
|
8284
|
+
* Where the mesh was last frame, a `Mover` or a matrix, which the temporal resolve follows under
|
|
8285
|
+
* `temporalAa`: a rigid, skinned or rewritten mesh is drawn again into the motion target at the
|
|
8286
|
+
* end of the frame, so its pixels reproject by its own motion and not the camera's alone. See
|
|
8287
|
+
* `motionTarget.ts`; a mover is advanced only where the resolve reads it.
|
|
8065
8288
|
*/
|
|
8066
8289
|
previousModel = null) {
|
|
8067
8290
|
if (this.contextLost)
|
|
@@ -8100,6 +8323,8 @@ export class WebGL2Renderer {
|
|
|
8100
8323
|
return;
|
|
8101
8324
|
this.drawBudget.ask();
|
|
8102
8325
|
this.takeMaterial();
|
|
8326
|
+
if (this.followsMotion())
|
|
8327
|
+
this.recordDrawMotion(mesh, model, previousModel);
|
|
8103
8328
|
/*
|
|
8104
8329
|
* Skin under the screen-space blur draws its halves: the frame's, then at once its diffuse's
|
|
8105
8330
|
* light and its colour, each into the blur's own target.
|
|
@@ -8123,6 +8348,109 @@ export class WebGL2Renderer {
|
|
|
8123
8348
|
this.sceneTarget?.bind();
|
|
8124
8349
|
}
|
|
8125
8350
|
}
|
|
8351
|
+
/**
|
|
8352
|
+
* The frame's movers drawn into the motion target against its depth, rasterised through the
|
|
8353
|
+
* jittered matrix every geometry upload took (`viewProjFor`) and moved between the unjittered
|
|
8354
|
+
* ones; null where nothing moved or the frame is multisampled, which keeps the camera's motion
|
|
8355
|
+
* and says so once.
|
|
8356
|
+
*/
|
|
8357
|
+
drawMotionPass(view) {
|
|
8358
|
+
const target = this.sceneTarget;
|
|
8359
|
+
if (target === null || this.motionPass.recorded === 0)
|
|
8360
|
+
return null;
|
|
8361
|
+
if (target.sampleCount > 1) {
|
|
8362
|
+
if (!this.warnedMotionUnkept) {
|
|
8363
|
+
this.warnedMotionUnkept = true;
|
|
8364
|
+
console.warn("Renderer: WebGL2's temporal resolve follows the camera's motion alone in a multisampled " +
|
|
8365
|
+
"frame; a mover's own motion is drawn against single-sampled depth.");
|
|
8366
|
+
}
|
|
8367
|
+
return null;
|
|
8368
|
+
}
|
|
8369
|
+
const depth = target.depthAttachment();
|
|
8370
|
+
if (depth === null)
|
|
8371
|
+
return null;
|
|
8372
|
+
jitterProjection(this.motionJittered, this.sceneMatrix(view), this.temporalJitterX, this.temporalJitterY, Math.max(1, this.gl.drawingBufferWidth), Math.max(1, this.gl.drawingBufferHeight));
|
|
8373
|
+
return this.motionPass.draw(this.gl, depth, target.frameWidth, target.frameHeight, this.reversedDepth, this.motionJittered, view, this.previousViewProj);
|
|
8374
|
+
}
|
|
8375
|
+
/**
|
|
8376
|
+
* Whether a draw now is one the temporal resolve follows: the profile resolves temporally, this is
|
|
8377
|
+
* the frame's own picture between `beginFrame` and `endFrame`, and not a mirror's, a probe's, a
|
|
8378
|
+
* capture's, a shadow's or the view model's. See `motionTarget.ts`.
|
|
8379
|
+
*/
|
|
8380
|
+
followsMotion() {
|
|
8381
|
+
return (this.inFrame &&
|
|
8382
|
+
this.quality.temporalAa &&
|
|
8383
|
+
this.sceneTarget !== null &&
|
|
8384
|
+
!this.reflectionPassActive &&
|
|
8385
|
+
!this.probePassActive &&
|
|
8386
|
+
!this.capturePassActive &&
|
|
8387
|
+
!this.shadowPassActive &&
|
|
8388
|
+
!this.viewModelActive);
|
|
8389
|
+
}
|
|
8390
|
+
/**
|
|
8391
|
+
* A draw's motion for the resolve, decided as WebGPU's `recordDrawMotion` decides it: a mesh
|
|
8392
|
+
* rewritten this frame by its last positions, with or without a mover; a skinned mover by its last
|
|
8393
|
+
* pose; a rigid one by its last model. A cloth draw keeps the camera's motion, said once.
|
|
8394
|
+
*/
|
|
8395
|
+
recordDrawMotion(mesh, model, previous) {
|
|
8396
|
+
const rewritten = mesh.keptPrevious && mesh.changedFrame === this.moverFrame;
|
|
8397
|
+
if (this.cloth !== null && this.skinPaletteSet && mesh.isSkinned) {
|
|
8398
|
+
if (previous !== null && !this.warnedMotionUnkept) {
|
|
8399
|
+
this.warnedMotionUnkept = true;
|
|
8400
|
+
console.warn("Renderer: a cloth draw's own motion is not followed by WebGL2's temporal resolve; it " +
|
|
8401
|
+
"takes the camera's. WebGPU's reconstruction follows it.");
|
|
8402
|
+
}
|
|
8403
|
+
return;
|
|
8404
|
+
}
|
|
8405
|
+
if (previous === null) {
|
|
8406
|
+
if (rewritten)
|
|
8407
|
+
this.motionPass.recordDynamic(mesh, model, model);
|
|
8408
|
+
return;
|
|
8409
|
+
}
|
|
8410
|
+
if (!isMover(previous)) {
|
|
8411
|
+
if (rewritten)
|
|
8412
|
+
this.motionPass.recordDynamic(mesh, model, previous);
|
|
8413
|
+
else
|
|
8414
|
+
this.motionPass.recordRigid(mesh, model, previous);
|
|
8415
|
+
return;
|
|
8416
|
+
}
|
|
8417
|
+
const skinned = !rewritten && this.skinPaletteSet && mesh.isSkinned;
|
|
8418
|
+
const verdict = advanceMover(previous, this.moverRenderer, this.moverFrame, model, skinned ? this.paletteData : null);
|
|
8419
|
+
if (verdict === MOVER_TWICE) {
|
|
8420
|
+
if (!this.warnedMoverTwice) {
|
|
8421
|
+
this.warnedMoverTwice = true;
|
|
8422
|
+
console.warn('Renderer: a Mover was drawn twice in one frame. A mover is one object; the second draw ' +
|
|
8423
|
+
'records no motion. Give each moving object a mover of its own.');
|
|
8424
|
+
}
|
|
8425
|
+
return;
|
|
8426
|
+
}
|
|
8427
|
+
if (rewritten) {
|
|
8428
|
+
this.motionPass.recordDynamic(mesh, model, verdict === MOVER_MOTION ? previous.previousModel : model);
|
|
8429
|
+
return;
|
|
8430
|
+
}
|
|
8431
|
+
if (verdict !== MOVER_MOTION)
|
|
8432
|
+
return;
|
|
8433
|
+
if (skinned) {
|
|
8434
|
+
this.motionPass.recordSkinned(mesh, model, previous.previousModel,
|
|
8435
|
+
/* Each exactly its rig's length, as `advanceMover` keeps them, so neither is sliced. */
|
|
8436
|
+
previous.palette, previous.previousPalette);
|
|
8437
|
+
}
|
|
8438
|
+
else {
|
|
8439
|
+
this.motionPass.recordRigid(mesh, model, previous.previousModel);
|
|
8440
|
+
}
|
|
8441
|
+
}
|
|
8442
|
+
/**
|
|
8443
|
+
* Keep a dynamic mesh's positions before its frame's first rewrite, where the temporal resolve
|
|
8444
|
+
* follows motion: a rewrite before `beginFrame` belongs to the next frame (`frameOfChange`).
|
|
8445
|
+
*/
|
|
8446
|
+
keepPositionsBefore(mesh) {
|
|
8447
|
+
if (!(this.quality.temporalAa && this.sceneTarget !== null))
|
|
8448
|
+
return;
|
|
8449
|
+
const frame = frameOfChange(this.moverFrame, this.inFrame);
|
|
8450
|
+
if (keepsPrevious(mesh.changedFrame, frame))
|
|
8451
|
+
mesh.keepPrevious(this.gl);
|
|
8452
|
+
mesh.changedFrame = frame;
|
|
8453
|
+
}
|
|
8126
8454
|
/**
|
|
8127
8455
|
* Whether a skin draw now draws its two halves: the profile asked, the frame has not spread its
|
|
8128
8456
|
* skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
|
|
@@ -14,13 +14,26 @@ import type { GpuMesh } from './buffers.ts';
|
|
|
14
14
|
export declare function createPointSkinPipeline(device: GPUDevice): GPUComputePipeline;
|
|
15
15
|
export declare class GpuPointSkin {
|
|
16
16
|
readonly mesh: GpuMesh;
|
|
17
|
-
|
|
17
|
+
/** Widened by each rewrite, never narrowed: see `widenReach`. */
|
|
18
|
+
reach: PointSkinReach;
|
|
18
19
|
private readonly count;
|
|
19
20
|
private readonly pipeline;
|
|
20
|
-
private
|
|
21
|
+
private joints;
|
|
22
|
+
/** Params, points with their weights, joints named, normals: in that order. */
|
|
21
23
|
private readonly buffers;
|
|
22
|
-
private
|
|
24
|
+
private group;
|
|
25
|
+
/** A rewrite's vertices laid out as the buffers hold them, grown to the largest rewrite. */
|
|
26
|
+
private scratch;
|
|
23
27
|
constructor(device: GPUDevice, pipeline: GPUComputePipeline, mesh: GpuMesh, data: PointSkinData);
|
|
28
|
+
private bind;
|
|
29
|
+
/**
|
|
30
|
+
* Rewrite vertices `first` onward, as many as `data` holds, in place: three queue writes at their
|
|
31
|
+
* offsets. **A queue write reaches every dispatch submitted after it**, so the renderer submits a
|
|
32
|
+
* pose already recorded before calling this, and the rewrite reaches the next pose alone, as it
|
|
33
|
+
* does on WebGL2. A joint none of the skin named before grows the joints buffer, which is made
|
|
34
|
+
* again with its bind group; the old one goes once the work already submitted is done with it.
|
|
35
|
+
*/
|
|
36
|
+
rewrite(device: GPUDevice, first: number, data: PointSkinData): void;
|
|
24
37
|
/** Write the pose's joints and record the dispatch that poses every vertex into `encoder`. */
|
|
25
38
|
pose(device: GPUDevice, encoder: GPUCommandEncoder, matrices: Float32Array): void;
|
|
26
39
|
dispose(): void;
|