@driftengine/core 4.11.0 → 4.12.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 +72 -0
- package/README.md +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/render/ambientOcclusionPass.d.ts +3 -2
- package/dist/render/ambientOcclusionPass.js +4 -2
- package/dist/render/backend/api.d.ts +21 -1
- package/dist/render/backend/webgl2/renderer.d.ts +61 -5
- package/dist/render/backend/webgl2/renderer.js +153 -21
- package/dist/render/backend/webgpu/renderer.d.ts +54 -1
- package/dist/render/backend/webgpu/renderer.js +246 -89
- package/dist/render/instances.d.ts +13 -1
- package/dist/render/instances.js +10 -0
- package/dist/render/occlusionFade.d.ts +21 -0
- package/dist/render/occlusionFade.js +27 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shaders/ambientOcclusion.js +12 -1
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +1 -0
- package/dist/render/shaders/flat/index.d.ts +5 -0
- package/dist/render/shaders/flat/index.js +10 -3
- package/dist/render/shaders/flat/layerLooks.d.ts +16 -0
- package/dist/render/shaders/flat/layerLooks.js +116 -0
- package/dist/render/shaders/flat/layered.d.ts +19 -9
- package/dist/render/shaders/flat/layered.js +169 -37
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +17 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/normalMap.d.ts +1 -1
- package/dist/render/shaders/flat/normalMap.js +11 -2
- package/dist/render/shaders/flat/preamble.js +6 -0
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +6 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +176 -48
- package/dist/render/shaders/generated/flat.wgsl.js +215 -87
- package/dist/render/surfaceLayerLooks.d.ts +80 -0
- package/dist/render/surfaceLayerLooks.js +83 -0
- package/dist/render/surfaceLayers.d.ts +128 -19
- package/dist/render/surfaceLayers.js +73 -13
- package/dist/render/surfaceTexture.d.ts +13 -4
- package/dist/render/transmission.d.ts +21 -0
- package/dist/render/transmission.js +21 -0
- package/dist/ui/splash.d.ts +42 -2
- package/dist/ui/splash.js +78 -13
- package/package.json +6 -6
- package/scripts/browser.d.mts +5 -0
- package/scripts/browser.mjs +68 -14
- package/scripts/look.d.mts +81 -0
- package/scripts/look.mjs +380 -0
- package/src/index.ts +4 -0
- package/src/render/ambientOcclusionPass.ts +4 -1
- package/src/render/backend/api.ts +31 -0
- package/src/render/backend/webgl2/renderer.ts +170 -22
- package/src/render/backend/webgpu/renderer.ts +379 -139
- package/src/render/instances.ts +18 -1
- package/src/render/occlusionFade.ts +29 -0
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shaders/ambientOcclusion.ts +12 -1
- package/src/render/shaders/flat/areaLight.ts +1 -1
- package/src/render/shaders/flat/glassTint.ts +3 -1
- package/src/render/shaders/flat/index.ts +15 -3
- package/src/render/shaders/flat/layerLooks.ts +116 -0
- package/src/render/shaders/flat/layered.ts +170 -37
- package/src/render/shaders/flat/main.ts +17 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/normalMap.ts +11 -2
- package/src/render/shaders/flat/preamble.ts +6 -0
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
- package/src/render/shaders/generated/flat.wgsl.ts +215 -87
- package/src/render/surfaceLayerLooks.ts +155 -0
- package/src/render/surfaceLayers.ts +187 -23
- package/src/render/surfaceTexture.ts +13 -4
- package/src/render/transmission.ts +27 -0
- package/src/ui/splash.ts +83 -14
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,76 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "4.12.0",
|
|
4
|
+
"date": "2026-10-10",
|
|
5
|
+
"entries": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "added",
|
|
8
|
+
"short": "`npm create @driftengine` starts a game that runs: a Vite and TypeScript project with the instructions a coding agent reads, the engine's skill, and `npm run check`.",
|
|
9
|
+
"text": "`@driftengine/create` is the new package `npm create @driftengine@latest my-game` runs. It starts a Vite and TypeScript project from the `starter` example, a lit cube turning at a rate a DriftScript rule sets, or with `-- --template first-game` from the complete 3D game, and both are generated from the typechecked examples, so a new project starts as code the compiler and the examples page have already run. It asks nothing and installs nothing, because the caller is as likely to be a coding agent as a person and an agent cannot answer a prompt. A project carries an `AGENTS.md` naming its commands, the rules the compiler cannot check and what done means there; the `driftengine` skill in `.agents/skills/` and `.claude/skills/`, the folders Codex, Cursor and Claude Code read skills from; and `npm run check`, which typechecks, runs the tests in Node and looks at the game. `-- --skill` adds the skill to a project that already exists. The engine is pinned at the command's own version and DriftScript exactly at the engine's pin. Vitest is 5 rather than the 4.1 this repository runs: Vitest 4.1 cannot be installed into a project with no lockfile by npm 10.9, the npm Node 22 ships, whose resolver stops on its peer list. `npm run cleanroom` now starts both templates from the packed tarballs and typechecks, tests and builds them, and with `--look` photographs them; both pass."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "added",
|
|
13
|
+
"short": "`npm run look` photographs a game on WebGPU and on WebGL2 on the machine's own GPU, and fails on a console error or warning, an empty canvas, or a backend that fell back.",
|
|
14
|
+
"text": "`@driftengine/core/scripts/look.mjs` is the engine's capture instruments as one verdict, for whoever cannot see the screen. `look({ url })` opens the page once with `?backend=webgpu` and once with `?backend=webgl2`, declines the boot badge, and writes what a player sees on each, the canvas alone beside it, and `look.json` with every console line. It says which backend actually drew, read from the context the page's canvas asked for rather than from the address, and how much of the canvas changed between two frames. It fails on a console error or a warning, on a canvas that is 99.5% one colour, measured with the page's own interface hidden so a readout over an empty canvas does not pass, and on a backend asked for and not used. With no hardware GPU it refuses rather than trust a software rasteriser. Against the starter it reported both backends drawing; against a page clearing its canvas under a text readout it reported nothing drew on either, and against a page with no canvas that no renderer started. A started project runs it as `npm run look`, and `-- --headed` opens a real window where a headless browser cannot reach the GPU."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"kind": "added",
|
|
18
|
+
"short": "The `driftengine` skill teaches a coding agent the engine at the version a project installed: `npx skills add drftrun/driftengine`, or `--skill` in a project.",
|
|
19
|
+
"text": "`skills/driftengine/` is an agent skill in the Agent Skills format: when the engine fits and when it does not, how to start and check a project, the shape of a program, the rules the compiler cannot check, the traps that look like engine faults, and where to read further. Its code is regions of the typechecked examples, rewritten from them by `npm run create:templates`, and its three references, every manual page, every example and every package, are generated from the repository with addresses at the installed version's tag, so a page added to the manual reaches the skill on the next run. A test fails when any of it is stale, when its frontmatter breaks the format, and when it names a type, function or option the engine's source does not declare. The manual gains a chapter, Working with a coding agent, and the installation page starts from `npm create @driftengine`."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"kind": "changed",
|
|
23
|
+
"short": "The capture tools look for Chrome or Edge on Windows and macOS as well as Linux, and stop the browser they start on Windows too.",
|
|
24
|
+
"text": "`browser.mjs` knew only Chrome's Linux and macOS paths and stopped a browser by signalling its process group, which Windows does not have, so neither the capture tools nor `look` could run there. `browserCandidates(platform, env)` lists Chrome, Chromium and Edge where each system installs them: under both Program Files and the per-user folder on Windows, where Edge is always present, and under `/Applications` and `~/Applications` on macOS. On Windows the browser's whole tree is stopped with `taskkill /T`. `look` uses each system's own graphics path through ANGLE: Vulkan on Linux, Metal on macOS, Direct3D 11 on Windows. Run on Linux only so far; on Windows and macOS the first run is a measurement still owed, and `look` prints the GPU it found on its second line."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"kind": "added",
|
|
28
|
+
"short": "`SurfaceMaterial.layers` picks each layer from arrays materials share, and gives each layer its own tint, ranges, normal strength, specular and repeats down.",
|
|
29
|
+
"text": "A layered material read layer i from arrays of its own, so a texture worn by many materials in many tints was baked once per material with the tint in its pixels: in the case reported, about 1 GB of arrays for 140 MB of textures. `arrayLayers` picks which layer of the albedo, normal and ORM arrays each layer reads, so materials share one array of each kind; `extrasAt` says where a material's own maps beyond its layers start, a mask and a mesh normal among them, which just past the layers no longer is once arrays are shared. `looks` gives each layer a `tint`, linear and allowed above 1, the `roughness` and `metalness` ranges its ORM's channels are spread over, a `normalStrength` its normal map's slopes are scaled by, and a `specular`, blended as the colours are, that every highlight, reflection and area light reads in place of the vertices'; and a layer's repeat may be a pair, across and down apart. They are a lit switch of their own, `LAYER_LOOKS`, fifteen fragment uniform vectors on WebGL2, off until a material names one, and a layered material that names none draws as before. On `demo/dev/layerLooks.html` layers picked from shared arrays draw the same picture as the same layers in arrays of their own, white tinted draws the same as the coloured texture, a strength of 0 draws the same as a flat map while 3 bands the surface harder than 1, roughness spread over 0 to 0.1 against 0.9 to 1 moved 13.9% of the frame, a layer's specular of 0 took the highlight off, and a repeat of `[4, 4]` drew the same as `4` while `[4, 1]` stretched the layer, identically on both backends. `SurfaceLayers` and `SurfaceLayerLook` are exported for consumers that build them."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"kind": "added",
|
|
33
|
+
"short": "`SurfaceLayers.meshOcclusion` darkens a layered surface's colour or its ambient light by an occlusion read at the mesh's own coordinates, over a range.",
|
|
34
|
+
"text": "A cliff blended from layers repeating dozens of times across it lost the shading of its own large shape, which only a map at the mesh's coordinates holds. `meshOcclusion` reads the red of the ORM array's layer after the material's mask and added mask, at the mesh's coordinates as `meshNormal` reads its normal, spreads it over a `range`, and darkens the surface by `mix(1, occlusion, strength)`. A number is the strength and darkens the blended colour, and so every light on it; `{ into: 'ambient' }` darkens the ambient light alone, sky, ground and probe, as an ambient occlusion does, leaving the sun and the lamps as they were. Absent or 0, nothing is read. On `demo/dev/layerLooks.html` a wall under an occlusion ramp darkened from black at its left edge to untouched at its right, 35.9% of the frame against the same wall without it; lit only by its ambient, the two targets drew the same picture, and with a sun on it the ambient target kept the sun's share; a range of 0.5 to 1 left the edge half dark. The same on both backends."
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"version": "4.11.1",
|
|
40
|
+
"date": "2026-10-10",
|
|
41
|
+
"entries": [
|
|
42
|
+
{
|
|
43
|
+
"kind": "added",
|
|
44
|
+
"short": "`SurfaceMaterial.layers` takes its mask from the ORM array or the vertex colour, sums layers, places them by the world, lays one by facing up, and lays a normal under them.",
|
|
45
|
+
"text": "`layers` covers more of the ways a layered surface is blended. `mask: 'orm'` reads the mask from the ORM array's layer past the material's layers, which leaves the model's slot to a lightmap's page, so a lightmapped surface blends its layers; `mask: 'vertex'` takes the weights from the vertex colour, which then tints nothing. `blend: 'sum'` mixes the base toward each layer times its weight, by the weights' sum held to 1, besides laying each over the ones before it. Under a `projection` the layers are placed by the world, each at its own repeats a metre, and `addMask` adds a mask placed the same way to one layer's weight. `facing` weighs one layer by how much the surface faces up, and `meshNormal` lays a normal map read at the mesh's own coordinates under the layers' blended normal. A triplanar projection lays layers on the horizontal plane, and says so once. On `demo/dev/layerFamilies.html` a mask carried by the ORM array draws exactly as the same mask as a map, keeps its layers under a lightmap's page, and every mode draws the same on both backends."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"kind": "added",
|
|
49
|
+
"short": "`SurfaceMaterial.transmissionColor` names the colour light through a thin surface takes, where it was always the surface's own.",
|
|
50
|
+
"text": "`transmissionColor`, linear, is the colour the light from behind a thin surface (`diffuseTransmission`) takes through it, in place of the surface's own colour: a printed banner glowing a flat red behind its print rather than lit through in the print's colours. Absent, the surface's colour, as before. It rides the glass tint's colour lanes, which no opaque material reads, and a pane drawn between two draws of the material puts it back. On `demo/dev/thinLight.html?color=0,0.4,1` a red banner lit from behind went from 163, 24, 19 to 16, 74, 171 under the sun with no red let through, the same to the pixel on both backends."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"kind": "added",
|
|
54
|
+
"short": "`setAmbientOcclusionFade(distance, radius)` fades ambient occlusion out with distance, so a sky dome or a far range is not shaded in rings.",
|
|
55
|
+
"text": "Ambient occlusion is whole up to `distance` metres from the eye and gone `radius` metres past it, held until changed; a negative distance, the default, fades nothing. Far off, the depth buffer's steps are metres apart and the estimate shaded them in bands across a sky dome or a mountain range; past the fade those pixels are left open and skip the estimate's walk. Bound in DriftScript as `drift/render`'s `occlusionFade`. On `demo/dev/aoFade.html` a ridge 3 km off went from 66,267 banded pixels to none, while the feet of the near boxes darkened exactly as before, on both backends."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"kind": "added",
|
|
59
|
+
"short": "A lightmapped instance fades by `setDitherOpacity` too: its opacity rides its page region in sixty-fourths, where it had none.",
|
|
60
|
+
"text": "A lightmapped batch carries its page regions in the lane `MeshInstances.alphas` would take, so its instances could not fade. Their opacity now travels as whole steps added to the region's U offset, 64 of them, the screen door's own resolution, and the vertex stage takes them back off. An opaque instance's region is written to the bit as before; a fading one's offset keeps 16 bits of fraction, a sixteenth of a texel on a 4,096 page. On `demo/dev/lightmap.html?mode=tiles&fade=0.5` the faded tiles keep 50.0% of their pixels, each lit exactly as unfaded, and nothing else changes, on both backends."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"kind": "added",
|
|
64
|
+
"short": "`prepareMesh` and `prepareInstanced` compile a draw's pipelines in its material before its first frame, so a scene drawn under `pipelineCompile: 'skip'` shows whole.",
|
|
65
|
+
"text": "`prepareMesh(mesh, material)` and `prepareInstanced(batch, material)` compile, off the main thread, every pipeline a draw of that mesh or batch in that material takes that making it did not: a two-sided, cut-out or surface-model variant, a skin's halves under the screen-space blur, and the material's lit switches; `{ translucent: true }` adds the blended ones. `ready()` resolves once they have. Under `pipelineCompile: 'skip'` a draw whose pipeline is new is left out until it lands, so a figure brought on screen came without its skin and hair for a few frames; prepared, it is drawn whole in its first frame. On WebGPU a prepared key is the one the draw asks for, worked out by the same function; on WebGL2 a surface model's programs are what a draw can wait for, and are what it prepares."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"kind": "added",
|
|
69
|
+
"short": "`holdSplash(promise, { capMs })` keeps the engine's badge up until the game's own first screen has loaded, under a cap of its own.",
|
|
70
|
+
"text": "The badge left three seconds after the first frame, twenty at most, so a game whose first screen loads for longer was seen loading. `holdSplash` keeps it up until the promise settles, either way, as well as until the first frame; frames run behind it while the game loads, since a load may need them, and are held once it has settled for what is left of the minimum. `capMs` is the game's ceiling in place of the engine's twenty seconds, the longest of several holds, and the call answers whether there was a badge to hold. On `demo/dev/splashHold.html` a load settling at 6.2 s took the badge down at 6.7 s with 361 frames run under it, a rejected load the same, and an 8-second cap at 8.4 s."
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
2
74
|
{
|
|
3
75
|
"version": "4.11.0",
|
|
4
76
|
"date": "2026-10-10",
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ WebGL2 everywhere else, behind one API, and carries the fixed-step loop, the ren
|
|
|
6
6
|
input, the scene graph, geometry, text and the whole of `@driftengine/physics`, which it re-exports.
|
|
7
7
|
Everything else is a separate package a game adds when it reaches for it.
|
|
8
8
|
|
|
9
|
-
**Cost:
|
|
9
|
+
**Cost: 910.1 KB gzipped, importing `createRenderer`.** Measured by `scripts/size-gate.test.mjs`,
|
|
10
10
|
which fails the build if the figure drifts by more than 3%.
|
|
11
11
|
|
|
12
12
|
The manual is at **[driftengine.dev/docs](https://driftengine.dev/docs)**, with an example running
|
package/dist/index.d.ts
CHANGED
|
@@ -155,7 +155,7 @@ export { BLOOM_LEVELS } from './render/bloomChain.ts';
|
|
|
155
155
|
export type { BloomResponse } from './render/bloomChain.ts';
|
|
156
156
|
export { probeDevice } from './render/backend/probe.ts';
|
|
157
157
|
export type { ProbeVerdict } from './render/backend/probe.ts';
|
|
158
|
-
export type { BoltHandle, CausticsHandle, ClothBindingHandle, ClothParticlesHandle, FlockHandle, IncrementalMeshHandle, LineHandle, MeshHandle, MeshOptions, RenderBackend, RendererApi, ParticleHandle, PlumeHandle, ScatterHandle, InstancedHandle, SdfTextHandle, SurfaceTextureHandle, TextHandle, WaterHandle, WindStreakHandle, } from './render/backend/api.ts';
|
|
158
|
+
export type { BoltHandle, CausticsHandle, ClothBindingHandle, ClothParticlesHandle, FlockHandle, IncrementalMeshHandle, LineHandle, MeshHandle, MeshOptions, PrepareOptions, RenderBackend, RendererApi, ParticleHandle, PlumeHandle, ScatterHandle, InstancedHandle, SdfTextHandle, SurfaceTextureHandle, TextHandle, WaterHandle, WindStreakHandle, } from './render/backend/api.ts';
|
|
159
159
|
export type { ClothBindingData } from './render/clothBindingData.ts';
|
|
160
160
|
export { anisotropicModel, eyeModel, hairModel, lightmapModel, skinModel, } from './render/surfaceModel.ts';
|
|
161
161
|
export type { AnisotropicModel, EyeModel, HairModel, LightmapModel, SkinModel, SurfaceModel, SurfaceModelKind, } from './render/surfaceModel.ts';
|
|
@@ -229,6 +229,8 @@ export { MAX_CLUSTERED_LIGHTS } from './render/clusteredLights.ts';
|
|
|
229
229
|
export { SurfaceTexture } from './render/surfaceTexture.ts';
|
|
230
230
|
export type { SurfaceMaterial, SurfaceTextureOptions } from './render/surfaceTexture.ts';
|
|
231
231
|
export type { SurfaceProjection } from './render/surfaceProjection.ts';
|
|
232
|
+
export type { SurfaceLayers } from './render/surfaceLayers.ts';
|
|
233
|
+
export type { SurfaceLayerLook } from './render/surfaceLayerLooks.ts';
|
|
232
234
|
export { compressedFormatName, isCompressedSource, levelBytes, uploadsCompressed, } from './render/compressedSource.ts';
|
|
233
235
|
export type { BlockFormat, CompressedTextureFormat, CompressedTextureSource, SurfaceSource, } from './render/compressedSource.ts';
|
|
234
236
|
export { DEFAULT_GOVERNOR_LIMITS, ResolutionGovernor } from './render/resolutionGovernor.ts';
|
|
@@ -344,7 +346,7 @@ export type { TouchControlsOptions } from './input/touchControls.ts';
|
|
|
344
346
|
export { pixelCursor } from './ui/pixelCursor.ts';
|
|
345
347
|
export { fullscreenWanted, requestFullscreenOnGesture } from './ui/fullscreen.ts';
|
|
346
348
|
export type { FullscreenConditions } from './ui/fullscreen.ts';
|
|
347
|
-
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.ts';
|
|
349
|
+
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, holdSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.ts';
|
|
348
350
|
export type { MountedSplash, SplashConditions, SplashDecision, SplashOptions, } from './ui/splash.ts';
|
|
349
351
|
export { DEFAULT_CLIP_FPS, FrameRecorder, offerClip } from './ui/frameRecorder.ts';
|
|
350
352
|
export { stillFrame } from './ui/stillFrame.ts';
|
package/dist/index.js
CHANGED
|
@@ -439,7 +439,7 @@ export { fullscreenWanted, requestFullscreenOnGesture } from './ui/fullscreen.js
|
|
|
439
439
|
* they are here for the consumer that declines the automatic one and places its own, and because
|
|
440
440
|
* the two decisions behind it are worth reading before overriding either.
|
|
441
441
|
*/
|
|
442
|
-
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.js';
|
|
442
|
+
export { SPLASH_FADE_MS, SPLASH_HARD_CAP_MS, SPLASH_MIN_MS, forcedSplash, holdSplash, mountSplash, splashDecision, splashWanted, } from './ui/splash.js';
|
|
443
443
|
export { DEFAULT_CLIP_FPS, FrameRecorder, offerClip } from './ui/frameRecorder.js';
|
|
444
444
|
// Not on `FrameRecorder`, so a screenshot does not cost a clip pipeline. See the module.
|
|
445
445
|
export { stillFrame } from './ui/stillFrame.js';
|
|
@@ -46,8 +46,9 @@ export declare class AmbientOcclusionPass {
|
|
|
46
46
|
* by, or null if this pass could not run, which the caller must read as "no occlusion"
|
|
47
47
|
* rather than as black.
|
|
48
48
|
*
|
|
49
|
-
* `invProjection` is column-major, as `gl-matrix` produces it.
|
|
49
|
+
* `invProjection` is column-major, as `gl-matrix` produces it. `fade` is `uFade`, where the
|
|
50
|
+
* occlusion fades out with distance (`occlusionFade.ts`).
|
|
50
51
|
*/
|
|
51
|
-
run(depth: WebGLTexture, width: number, height: number, radius: number, projScale: Float32Array, invProjection: Float32Array): WebGLTexture | null;
|
|
52
|
+
run(depth: WebGLTexture, width: number, height: number, radius: number, projScale: Float32Array, invProjection: Float32Array, fade: Float32Array): WebGLTexture | null;
|
|
52
53
|
dispose(): void;
|
|
53
54
|
}
|
|
@@ -102,9 +102,10 @@ export class AmbientOcclusionPass {
|
|
|
102
102
|
* by, or null if this pass could not run, which the caller must read as "no occlusion"
|
|
103
103
|
* rather than as black.
|
|
104
104
|
*
|
|
105
|
-
* `invProjection` is column-major, as `gl-matrix` produces it.
|
|
105
|
+
* `invProjection` is column-major, as `gl-matrix` produces it. `fade` is `uFade`, where the
|
|
106
|
+
* occlusion fades out with distance (`occlusionFade.ts`).
|
|
106
107
|
*/
|
|
107
|
-
run(depth, width, height, radius, projScale, invProjection) {
|
|
108
|
+
run(depth, width, height, radius, projScale, invProjection, fade) {
|
|
108
109
|
if (!this.ensureSize(width, height))
|
|
109
110
|
return null;
|
|
110
111
|
const { gl } = this;
|
|
@@ -123,6 +124,7 @@ export class AmbientOcclusionPass {
|
|
|
123
124
|
gl.uniform1i(this.estimateUniforms['uDepth'] ?? null, 0);
|
|
124
125
|
gl.uniform2fv(this.estimateUniforms['uProjScale'] ?? null, projScale);
|
|
125
126
|
gl.uniform1f(this.estimateUniforms['uRadius'] ?? null, radius);
|
|
127
|
+
gl.uniform2fv(this.estimateUniforms['uFade'] ?? null, fade);
|
|
126
128
|
gl.uniformMatrix4fv(this.estimateUniforms['uInvProjection'] ?? null, false, invProjection);
|
|
127
129
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
128
130
|
/*
|
|
@@ -177,6 +177,14 @@ export interface SurfaceTextureHandle {
|
|
|
177
177
|
* `WebGLBuffer` in a private field, so no WebGPU object can satisfy it however identical its
|
|
178
178
|
* shape. It is not meant to be read; it is meant to be enforced.
|
|
179
179
|
*/
|
|
180
|
+
/** Which of a draw's pipelines `prepareMesh` and `prepareInstanced` compile beside the opaque ones. */
|
|
181
|
+
export interface PrepareOptions {
|
|
182
|
+
/**
|
|
183
|
+
* The blended ones too, for a draw made with `drawTranslucentMesh` or `drawTranslucentInstanced`.
|
|
184
|
+
* Off by default: an opaque surface would pay a compile it never draws with.
|
|
185
|
+
*/
|
|
186
|
+
readonly translucent?: boolean;
|
|
187
|
+
}
|
|
180
188
|
/** What a mesh needs to be told at creation that its geometry cannot say. */
|
|
181
189
|
export interface MeshOptions {
|
|
182
190
|
/**
|
|
@@ -190,7 +198,7 @@ export interface MeshOptions {
|
|
|
190
198
|
}
|
|
191
199
|
export type RendererApi = Omit<{
|
|
192
200
|
[K in keyof Renderer]: Renderer[K];
|
|
193
|
-
}, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'drawDeviceParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'createLightmap' | 'createSceneCapture' | 'captureScene' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial' | 'setSurfaceOverlay' | 'createClothBinding' | 'createClothParticles' | 'updateClothParticles' | 'setCloth' | 'disposeClothBinding' | 'disposeClothParticles'> & {
|
|
201
|
+
}, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'drawDeviceParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'createLightmap' | 'createSceneCapture' | 'captureScene' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial' | 'prepareMesh' | 'prepareInstanced' | 'setSurfaceOverlay' | 'createClothBinding' | 'createClothParticles' | 'updateClothParticles' | 'setCloth' | 'disposeClothBinding' | 'disposeClothParticles'> & {
|
|
194
202
|
/**
|
|
195
203
|
* A mesh's cloth binding: which simulation triangle each of its vertices follows, where on it and
|
|
196
204
|
* by how much (`ClothBindingData`). The mesh must be skinned — a binding blends each vertex
|
|
@@ -299,6 +307,18 @@ export type RendererApi = Omit<{
|
|
|
299
307
|
* setter each — and `setSurfaceTexture` is now a wrapper over it.
|
|
300
308
|
*/
|
|
301
309
|
setMaterial(material: SurfaceMaterial<SurfaceTextureHandle> | null): void;
|
|
310
|
+
/**
|
|
311
|
+
* Compile, off the main thread, every pipeline a draw of `mesh` in `material` takes that making
|
|
312
|
+
* the mesh did not — a two-sided, cut-out or shaded-by-a-model variant, a material's lit switches,
|
|
313
|
+
* a skin's halves — so the first frame that draws it waits on none; `ready()` resolves once they
|
|
314
|
+
* have. For `pipelineCompile: 'skip'` above all, where a draw whose pipeline is new is left out
|
|
315
|
+
* until it lands: a figure brought on screen whole rather than without its face for a few frames.
|
|
316
|
+
* A rigged mesh is prepared as drawn with a palette, and one with targets both with weights and
|
|
317
|
+
* without. Call it between frames: it sets `material` to read it and leaves none set after.
|
|
318
|
+
*/
|
|
319
|
+
prepareMesh(mesh: MeshHandle, material: SurfaceMaterial<SurfaceTextureHandle> | null, options?: PrepareOptions): void;
|
|
320
|
+
/** `prepareMesh` for an instanced batch drawn in `material`, its own pipelines being its own. */
|
|
321
|
+
prepareInstanced(batch: InstancedHandle, material: SurfaceMaterial<SurfaceTextureHandle> | null, options?: PrepareOptions): void;
|
|
302
322
|
/**
|
|
303
323
|
* A 3D text object: one string, drawn as instanced glyph cubes over the scene.
|
|
304
324
|
*
|
|
@@ -49,7 +49,7 @@ import type { RenderQuality, RenderQualityOptions } from '../../renderQuality.ts
|
|
|
49
49
|
import { Mesh } from '../../mesh.ts';
|
|
50
50
|
import type { IncrementalMesh } from '../../mesh.ts';
|
|
51
51
|
import type { MeshData } from '../../mesh.ts';
|
|
52
|
-
import type { MeshOptions } from '../api.ts';
|
|
52
|
+
import type { MeshOptions, PrepareOptions } from '../api.ts';
|
|
53
53
|
import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
54
54
|
import type { CompressedTextureFormat, CompressedTextureSource, SurfaceSource } from '../../compressedSource.ts';
|
|
55
55
|
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
@@ -1114,6 +1114,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1114
1114
|
private readonly aoProjScale;
|
|
1115
1115
|
/** Where this frame's jitter put the occlusion, in the composite's uv. See `uAoOffset`. */
|
|
1116
1116
|
private readonly aoOffset;
|
|
1117
|
+
/** Where occlusion fades out with distance, `uFade`: none until a caller sets one. */
|
|
1118
|
+
private readonly aoFade;
|
|
1117
1119
|
/**
|
|
1118
1120
|
* Whether this frame has already been resolved to the canvas.
|
|
1119
1121
|
*
|
|
@@ -1281,14 +1283,23 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1281
1283
|
private warnedProjectionRoom;
|
|
1282
1284
|
/** `uWorldUv`, the current material's projection packed. See `surfaceProjection.ts`. */
|
|
1283
1285
|
private readonly projection;
|
|
1284
|
-
/**
|
|
1285
|
-
|
|
1286
|
+
/**
|
|
1287
|
+
* `uGlassTint` as the current material leaves it: its light from behind and that light's colour,
|
|
1288
|
+
* which a pane overwrites for its own draw and `unbindSeeThrough` puts back. See `transmission.ts`.
|
|
1289
|
+
*/
|
|
1290
|
+
private readonly transmission;
|
|
1286
1291
|
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
1287
1292
|
private readonly layerFloats;
|
|
1293
|
+
/** `uLayerLooks`, the current material's layer picks and looks packed. See `surfaceLayerLooks.ts`. */
|
|
1294
|
+
private readonly lookFloats;
|
|
1288
1295
|
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
1289
1296
|
private layersRoom;
|
|
1290
1297
|
private warnedLayersRoom;
|
|
1298
|
+
/** Whether it has room for the layers' looks beside them, and whether the refusal was said. */
|
|
1299
|
+
private looksRoom;
|
|
1300
|
+
private warnedLooksRoom;
|
|
1291
1301
|
private warnedLayersModelMap;
|
|
1302
|
+
private warnedLayersTriplanar;
|
|
1292
1303
|
private warnedOverlayRoom;
|
|
1293
1304
|
private warnedOverlayBlocks;
|
|
1294
1305
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
@@ -1959,6 +1970,25 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1959
1970
|
* `uScale`/`vScale` are repeats across the mesh's own UV range: geometry authored in
|
|
1960
1971
|
* metres is textured at whatever density the material wants without rebuilding it.
|
|
1961
1972
|
*/
|
|
1973
|
+
/**
|
|
1974
|
+
* Compile, off the frame, every program a draw of `mesh` in `material` takes that making the mesh
|
|
1975
|
+
* did not, so the first frame that draws it waits on none: the program of the material's shading
|
|
1976
|
+
* model for each vertex variant the mesh can be drawn with (its rig, with a palette; its targets,
|
|
1977
|
+
* with weights and without), a skin's halves under the screen-space blur, and the lit switches
|
|
1978
|
+
* the material turns on. `ready()` resolves once they have.
|
|
1979
|
+
*
|
|
1980
|
+
* **For `pipelineCompile: 'skip'` above all**, where a draw whose program is new is left out until
|
|
1981
|
+
* it lands: a figure brought on screen whole rather than without its face and hair for the frames
|
|
1982
|
+
* those take. Where draws wait, the programs compile here, at the call, rather than in a frame.
|
|
1983
|
+
* Two-sided and cut-out surfaces are draw state on this backend and need nothing; on WebGPU they
|
|
1984
|
+
* are pipelines of their own, prepared the same way.
|
|
1985
|
+
*
|
|
1986
|
+
* **Call it between frames**: it sets `material` to read it and leaves no material set after, as
|
|
1987
|
+
* `setMaterial(null)` does. `options` is the other backend's; blended draws share a program here.
|
|
1988
|
+
*/
|
|
1989
|
+
prepareMesh(mesh: Mesh, material: SurfaceMaterial | null, _options?: PrepareOptions): void;
|
|
1990
|
+
/** `prepareMesh` for an instanced batch: its own programs, and its model's for its variant. */
|
|
1991
|
+
prepareInstanced(batch: InstancedBatch, material: SurfaceMaterial | null, _options?: PrepareOptions): void;
|
|
1962
1992
|
/**
|
|
1963
1993
|
* Choose the joint palette the following `drawMesh` calls skin by, or null for none.
|
|
1964
1994
|
*
|
|
@@ -2140,7 +2170,19 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2140
2170
|
* no room for their two vectors each leave one layer, the last two said once each.
|
|
2141
2171
|
*/
|
|
2142
2172
|
private layersOf;
|
|
2143
|
-
/**
|
|
2173
|
+
/**
|
|
2174
|
+
* Whether a layered material's picks and looks are drawn: asked for, and room for their fifteen
|
|
2175
|
+
* vectors beside the layers' four. Refused, the layers are drawn reading layer `i` of their arrays
|
|
2176
|
+
* as they are, said once — which on a material sharing its arrays reads the wrong textures, so
|
|
2177
|
+
* the sentence names the way out.
|
|
2178
|
+
*/
|
|
2179
|
+
private looksFit;
|
|
2180
|
+
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
2181
|
+
private warnLayersTriplanar;
|
|
2182
|
+
/**
|
|
2183
|
+
* Whether the lit shader with `feature` compiled in, and `beside` with it where a switch means
|
|
2184
|
+
* nothing alone, fits this GPU's fragment uniform vectors.
|
|
2185
|
+
*/
|
|
2144
2186
|
private switchFits;
|
|
2145
2187
|
/**
|
|
2146
2188
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
@@ -2236,7 +2278,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2236
2278
|
*
|
|
2237
2279
|
* What it gives up: a fade reads as grain while it lasts, which a temporal resolve smooths and a
|
|
2238
2280
|
* plain frame does not; a blended draw that asks spends its opacity here and blends none of it;
|
|
2239
|
-
* and a lightmapped batch
|
|
2281
|
+
* and a lightmapped batch's opacities ride its page regions, so they fade in steps of a
|
|
2282
|
+
* sixty-fourth, which is the pattern's own resolution (`MeshInstances.lightmapRegions`).
|
|
2240
2283
|
*/
|
|
2241
2284
|
setDitherOpacity(on: boolean): void;
|
|
2242
2285
|
/**
|
|
@@ -2404,6 +2447,8 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2404
2447
|
* taking it mid-bake is how a probe's next face came to be attached to the scene's framebuffer.
|
|
2405
2448
|
*/
|
|
2406
2449
|
private bindSeeThrough;
|
|
2450
|
+
/** `uGlassTint` as the current material leaves it. See `transmission`. */
|
|
2451
|
+
private writeTransmission;
|
|
2407
2452
|
/** Put back what `bindSeeThrough` bound: the binding outlives its frame (the 2026-08-27 rule). */
|
|
2408
2453
|
private unbindSeeThrough;
|
|
2409
2454
|
disposeMesh(mesh: Mesh): void;
|
|
@@ -3293,6 +3338,17 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
3293
3338
|
* once rather than doing nothing. See `shaders/localExposure.ts`.
|
|
3294
3339
|
*/
|
|
3295
3340
|
setLocalExposure(strength: number): void;
|
|
3341
|
+
/**
|
|
3342
|
+
* Where ambient occlusion fades out with distance: whole up to `distance` metres from the eye and
|
|
3343
|
+
* gone `radius` metres past it, so the far depth buffer's steps — a sky dome kilometres off, a
|
|
3344
|
+
* mountain range — are not shaded in rings, and those pixels skip the occlusion's sampling.
|
|
3345
|
+
*
|
|
3346
|
+
* A distance that is not a finite number at or above zero is no fade, which is the default, and
|
|
3347
|
+
* `Infinity` reads as "never fades"; a radius below zero is a cut at `distance`. Set before
|
|
3348
|
+
* `endFrame` and held until changed, like `setDepthOfField`. Nothing at all when
|
|
3349
|
+
* `ambientOcclusion` is 0. See `occlusionFade.ts` for what it gives up.
|
|
3350
|
+
*/
|
|
3351
|
+
setAmbientOcclusionFade(distance: number, radius: number): void;
|
|
3296
3352
|
/**
|
|
3297
3353
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
3298
3354
|
* to take.
|