@driftengine/core 4.11.1 → 4.13.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 +102 -0
- package/README.md +18 -9
- package/dist/geometry/meshBuilder.d.ts +10 -0
- package/dist/geometry/meshBuilder.js +11 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -2
- package/dist/math/color.d.ts +25 -0
- package/dist/math/color.js +37 -0
- package/dist/render/backend/webgl2/renderer.d.ts +33 -5
- package/dist/render/backend/webgl2/renderer.js +123 -49
- package/dist/render/backend/webgl2/skinScatter.d.ts +3 -1
- package/dist/render/backend/webgl2/skinScatter.js +5 -1
- package/dist/render/backend/webgpu/boltPass.d.ts +1 -1
- package/dist/render/backend/webgpu/causticsPass.d.ts +1 -1
- package/dist/render/backend/webgpu/filmPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flockPass.d.ts +1 -1
- package/dist/render/backend/webgpu/giFieldPass.d.ts +3 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +10 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +28 -11
- package/dist/render/backend/webgpu/panelPass.d.ts +1 -1
- package/dist/render/backend/webgpu/postPass.d.ts +2 -2
- package/dist/render/backend/webgpu/renderer.d.ts +21 -8
- package/dist/render/backend/webgpu/renderer.js +103 -38
- package/dist/render/backend/webgpu/scatterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +5 -1
- package/dist/render/backend/webgpu/skinScatterPass.js +9 -3
- package/dist/render/backend/webgpu/skyPass.d.ts +11 -1
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/windStreakPass.d.ts +1 -1
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +5 -1
- package/dist/render/decalPass.d.ts +3 -1
- package/dist/render/decalPass.js +4 -1
- package/dist/render/flockRenderer.d.ts +1 -1
- package/dist/render/flockRenderer.js +4 -1
- package/dist/render/globalMediumPass.d.ts +3 -1
- package/dist/render/globalMediumPass.js +5 -1
- package/dist/render/gradeColor.d.ts +21 -0
- package/dist/render/gradeColor.js +64 -0
- package/dist/render/lightMatrix.d.ts +13 -0
- package/dist/render/lightMatrix.js +17 -0
- package/dist/render/passGrade.d.ts +13 -0
- package/dist/render/passGrade.js +47 -0
- package/dist/render/plumeRenderer.d.ts +1 -1
- package/dist/render/plumeRenderer.js +4 -1
- package/dist/render/renderQuality.d.ts +57 -18
- package/dist/render/renderQuality.js +3 -3
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -0
- package/dist/render/sdfTextRenderer.d.ts +1 -1
- package/dist/render/sdfTextRenderer.js +1 -1
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/arcane.js +5 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +4 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/caustics.js +4 -1
- package/dist/render/shaders/decalProject.js +12 -2
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/film.js +22 -9
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/fire.js +4 -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 +1 -1
- package/dist/render/shaders/flat/layerLooks.d.ts +16 -0
- package/dist/render/shaders/flat/layerLooks.js +108 -0
- package/dist/render/shaders/flat/layered.d.ts +6 -4
- package/dist/render/shaders/flat/layered.js +77 -13
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +7 -2
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/preamble.js +6 -0
- package/dist/render/shaders/flock.d.ts +1 -1
- package/dist/render/shaders/flock.js +4 -1
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +11 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/bolt.wgsl.js +12 -2
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/caustics.wgsl.js +12 -2
- package/dist/render/shaders/generated/decalProject.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/decalProject.wgsl.js +6 -1
- package/dist/render/shaders/generated/film.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/film.wgsl.js +12 -2
- package/dist/render/shaders/generated/fire.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/fire.wgsl.js +11 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +144 -16
- package/dist/render/shaders/generated/flat.wgsl.js +183 -55
- package/dist/render/shaders/generated/flock.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/flock.wgsl.js +12 -2
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/globalMedium.wgsl.js +12 -2
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +36 -16
- package/dist/render/shaders/generated/lightVolume.wgsl.js +39 -19
- package/dist/render/shaders/generated/panel.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/panel.wgsl.js +12 -2
- package/dist/render/shaders/generated/rush.wgsl.d.ts +31 -26
- package/dist/render/shaders/generated/rush.wgsl.js +31 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/scatter.wgsl.js +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.js +11 -1
- package/dist/render/shaders/generated/sky.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/sky.wgsl.js +12 -2
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +11 -1
- package/dist/render/shaders/generated/text.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/text.wgsl.js +11 -1
- package/dist/render/shaders/generated/water.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/water.wgsl.js +12 -2
- package/dist/render/shaders/generated/windStreaks.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/windStreaks.wgsl.js +12 -2
- package/dist/render/shaders/globalMedium.js +14 -1
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +15 -6
- package/dist/render/shaders/gpudriven/present.wgsl.js +71 -8
- package/dist/render/shaders/lightVolume.js +4 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +8 -2
- package/dist/render/shaders/outputTransform.js +18 -11
- package/dist/render/shaders/panel.d.ts +1 -1
- package/dist/render/shaders/panel.js +5 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +8 -0
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/scatter.js +4 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/skinBlur.js +5 -1
- package/dist/render/shaders/sky.d.ts +1 -1
- package/dist/render/shaders/sky.js +5 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/smoke.js +4 -1
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +4 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -2
- package/dist/render/shaders/windStreaks.d.ts +1 -1
- package/dist/render/shaders/windStreaks.js +4 -1
- package/dist/render/surfaceLayerLooks.d.ts +92 -0
- package/dist/render/surfaceLayerLooks.js +102 -0
- package/dist/render/surfaceLayers.d.ts +47 -6
- package/dist/render/surfaceLayers.js +5 -1
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +7 -1
- package/dist/render/vertexDefaults.d.ts +1 -1
- package/dist/render/vertexDefaults.js +1 -1
- package/dist/render/waterRenderer.d.ts +1 -1
- package/dist/render/waterRenderer.js +4 -1
- package/dist/render/windStreakRenderer.d.ts +3 -1
- package/dist/render/windStreakRenderer.js +5 -1
- 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/geometry/meshBuilder.ts +21 -2
- package/src/index.ts +13 -2
- package/src/math/color.ts +39 -0
- package/src/render/backend/webgl2/renderer.ts +174 -46
- package/src/render/backend/webgl2/skinScatter.ts +5 -0
- package/src/render/backend/webgpu/giFieldPass.ts +3 -1
- package/src/render/backend/webgpu/gpuDrivenPass.ts +29 -13
- package/src/render/backend/webgpu/renderer.ts +112 -37
- package/src/render/backend/webgpu/skinScatterPass.ts +17 -1
- package/src/render/causticsRenderer.ts +5 -0
- package/src/render/decalPass.ts +3 -0
- package/src/render/flockRenderer.ts +5 -0
- package/src/render/globalMediumPass.ts +10 -1
- package/src/render/gradeColor.ts +90 -0
- package/src/render/lightMatrix.ts +32 -0
- package/src/render/passGrade.ts +58 -0
- package/src/render/plumeRenderer.ts +5 -0
- package/src/render/renderQuality.ts +60 -21
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/sdfTextRenderer.ts +1 -1
- package/src/render/shaders/arcane.ts +5 -1
- package/src/render/shaders/bolt.ts +4 -1
- package/src/render/shaders/caustics.ts +4 -1
- package/src/render/shaders/decalProject.ts +12 -2
- package/src/render/shaders/film.ts +22 -9
- package/src/render/shaders/fire.ts +4 -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 +6 -1
- package/src/render/shaders/flat/layerLooks.ts +108 -0
- package/src/render/shaders/flat/layered.ts +78 -13
- package/src/render/shaders/flat/main.ts +7 -2
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +6 -0
- package/src/render/shaders/flock.ts +4 -1
- package/src/render/shaders/generated/arcane.wgsl.ts +11 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +12 -2
- package/src/render/shaders/generated/caustics.wgsl.ts +12 -2
- package/src/render/shaders/generated/decalProject.wgsl.ts +6 -1
- package/src/render/shaders/generated/film.wgsl.ts +12 -2
- package/src/render/shaders/generated/fire.wgsl.ts +11 -1
- package/src/render/shaders/generated/flat.wgsl.ts +183 -55
- package/src/render/shaders/generated/flock.wgsl.ts +12 -2
- package/src/render/shaders/generated/globalMedium.wgsl.ts +12 -2
- package/src/render/shaders/generated/lightVolume.wgsl.ts +39 -19
- package/src/render/shaders/generated/panel.wgsl.ts +12 -2
- package/src/render/shaders/generated/rush.wgsl.ts +31 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +12 -2
- package/src/render/shaders/generated/skinBlur.wgsl.ts +11 -1
- package/src/render/shaders/generated/sky.wgsl.ts +12 -2
- package/src/render/shaders/generated/smoke.wgsl.ts +11 -1
- package/src/render/shaders/generated/text.wgsl.ts +11 -1
- package/src/render/shaders/generated/water.wgsl.ts +12 -2
- package/src/render/shaders/generated/windStreaks.wgsl.ts +12 -2
- package/src/render/shaders/globalMedium.ts +14 -1
- package/src/render/shaders/gpudriven/present.wgsl.ts +72 -8
- package/src/render/shaders/lightVolume.ts +4 -1
- package/src/render/shaders/outputTransform.ts +19 -11
- package/src/render/shaders/panel.ts +5 -1
- package/src/render/shaders/rush.ts +8 -0
- package/src/render/shaders/scatter.ts +4 -1
- package/src/render/shaders/skinBlur.ts +5 -1
- package/src/render/shaders/sky.ts +5 -1
- package/src/render/shaders/smoke.ts +4 -1
- package/src/render/shaders/text.ts +4 -1
- package/src/render/shaders/water.ts +12 -2
- package/src/render/shaders/windStreaks.ts +4 -1
- package/src/render/surfaceLayerLooks.ts +186 -0
- package/src/render/surfaceLayers.ts +52 -7
- package/src/render/textRenderer.ts +6 -0
- package/src/render/vertexDefaults.ts +1 -1
- package/src/render/waterRenderer.ts +5 -0
- package/src/render/windStreakRenderer.ts +5 -0
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,106 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "4.13.0",
|
|
4
|
+
"date": "2026-10-10",
|
|
5
|
+
"entries": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "changed",
|
|
8
|
+
"short": "The frame is encoded for the screen by default, every pass and the clear colour alike: `outputTransform` is `'srgb'`, so a model reads as it does elsewhere.",
|
|
9
|
+
"text": "`outputTransform` defaults to `'srgb'`. The light the renderer computes is linear, and a texture's colours are decoded to linear when they are read, so a frame written without encoding them back, which `'none'`, the default until now, did, reads every midtone far too dark: a textured model drawn from the manual with no quality options came out darker than the same file in any other engine, and the cause looks like lighting, so that is where people looked. **Every pass now grades where nothing after it will, and the clear colour with them**, so a colour handed to the renderer means one thing wherever it lands: the sky, water, scatter, the plumes, flocks, wind streaks, bolts, caustics, light volumes and film grade themselves, as the lit pass, particles, lines and world text already did; the decal multiply, the global medium, the skin blur's add and the resolve's occlusion work in the terms of a pixel already graded; and the gpu-driven pipeline's blit grades instead of warning. Until now, without `hdrScene`, those passes drew their colours as given beside a graded world, so a sky never met the fog fading into it. `'srgb'` is the display encoding with no tone curve; `'aces'` with an `outputExposure` still rolls highlights off. A world graded under `'none'` says so and draws exactly as it did, bit for bit, and a frame with `hdrScene` is graded once at the resolve exactly as before. What changes for a world on another curve without `hdrScene` is that its sky and water are graded with it, and its interface is encoded for the screen, as the next entry says."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "added",
|
|
13
|
+
"short": "Colours picked by eye are stated as such: `srgbColor(r, g, b)`, `MeshBuilder.build({ colorSpace: 'srgb' })` and `decodeSrgbInPlace`, each decoding to the linear values the renderer draws.",
|
|
14
|
+
"text": "A colour picked by eye, on a screen or in a paint program, is a display value, and the renderer draws linear ones, so typed in as it looks it comes out too light. Three ways say which it is. `srgbColor(r, g, b)` decodes one colour through the sRGB curve, so a surface lit at 1, a sky or a clear colour comes out as the colour picked. `MeshBuildOptions.colorSpace: 'srgb'` decodes every vertex colour and emissive colour of a built mesh once, at build, as a texture uploaded as sRGB is decoded at upload, so a world built from hand-picked colours changes one line. `decodeSrgbInPlace` decodes an array of them, leaving a negative sentinel alone, for a mesh written by hand. `srgbToLinear` decodes one channel, for a colour that arrives a number at a time, as a packed hex token does. A light's intensity is an amount rather than a look and stays as it is. The examples state their colours this way, and the starter, which a new project begins from, shows the default."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"kind": "changed",
|
|
18
|
+
"short": "Text, panels and the 2D layer are encoded for the screen and never tone mapped, so an interface comes out as picked under any transform and at any exposure.",
|
|
19
|
+
"text": "Screen text, `fillPanel` and `@driftengine/ui2d`'s 2D layer take the screen encode and never the curve or the exposure: `interfaceGrade` says which code that is, for a contributed pass that draws an interface. A caption or a panel is not light, and its colour was picked for the screen, so under `'aces'` a menu's white text came out grey, its dark panel crushed to black, and the whole interface brightened and darkened as the scene's exposure eased. Now every interface colour stated through `srgbColor`, or as a theme's hex token, comes out exactly as picked under every transform. Inside an `hdrScene` frame an interface is part of the picture the resolve tone maps, as it was; drawn after `endFrame`, it is not. Text drawn in the world at a model matrix is part of the world and keeps the curve."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"kind": "fixed",
|
|
23
|
+
"short": "A pass drawn after `endFrame` grades itself under an `hdrScene` composite too, where it drew its colours as display values beside a graded frame.",
|
|
24
|
+
"text": "With `hdrScene` the resolve grades the frame and every pass before it draws linear light. A pass drawn after `endFrame` lands on the canvas the resolve has already written, so nothing after it grades it, and it used to draw ungraded there: an interface drawn after the present, which is where the manual puts one to keep it out of the screen-space chain, was the one surface on screen reading its colours as display values. The decision is now one function both backends read, and past the present it answers the frame's own grade, composite or not."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"kind": "changed",
|
|
28
|
+
"short": "A colour picked for the screen is decoded where an interface takes one: ui2d's hex tokens, and a script's `ui.tint` and `sprites.tinted`.",
|
|
29
|
+
"text": "`unpackRgba` and `themeRgba` decode a packed `0xRRGGBBAA` token from the sRGB a hex colour is written in to the linear light a node holds, keeping its alpha as coverage. Read as linear, `0x336699` came out as `0x7caacb` under the new default, every panel a washed-out version of the one picked. DriftScript's `ui.tint` and `sprites.tinted` take a colour picked for the screen in the same terms and decode it, since a script has no `srgbColor` to say so, so a white sprite draws exactly the tint named. A renderer asked for `outputTransform: 'none'` draws these darker than picked, as it does every colour."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"kind": "changed",
|
|
33
|
+
"short": "A splat capture's colour is a display value, decoded after its view-dependent band, so a trained capture draws as it does in the tool that made it.",
|
|
34
|
+
"text": "A Gaussian capture is trained against photographs, so its colour terms reproduce their pixels drawn as they are. `SplatSource.colors` was documented as linear and drawn so, which under the new `'srgb'` default is a washed-out cloud. The splat shader now decodes a splat's colour after adding its l=1 band, which training added in the same space, and the eight bits a colour is packed into are spent where the eye sees them. What it gives up is the blend: splats compose in linear light rather than in the display values training composed them in, which moves a translucent edge a little. `@driftengine/capture` fits in linear light against decoded frames and now hands its colours over encoded, its l=1 band carried through the encode's slope at each colour, so a fitted capture draws as fitted."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"kind": "changed",
|
|
38
|
+
"short": "Sun shadows no longer dissolve past six metres or below 18° of sun: `directionalShadowMaxDistance` defaults to 1,000 and `directionalShadowMaxSlope` to 60.",
|
|
39
|
+
"text": "`directionalShadowMaxDistance` defaults to 1,000 metres and `directionalShadowMaxSlope` to 60. The old 6 and 3 were a tuning for short casters: any sun shadow longer than six metres along the ground dissolved, so a building's walls and arcades cast nothing on its floor under a high sun and the light read as random, and every sun shadow faded below about 18° of elevation, which is an evening without shadows. A shadow now reaches as far as its map holds, whose edge already fades it, and holds until the sun is about a degree above the horizon. The reach is a shadow's own length along the ground and not a distance from the camera, which its name suggests, and both options now say what their default gives up and what would make it wrong. A scene whose low sun streaks lowers the slope; one tuned around the old values sets them. The medium's shafts and a surface's shadows, which disagreed past six metres, now agree."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"kind": "added",
|
|
43
|
+
"short": "`DrftLoader` draws, casts and prepares its model: `draw()`, `casters` and `prepare()` apply every rule a container needs, and `PartDraws` does it for a chosen list.",
|
|
44
|
+
"text": "`DrftLoader.draw()` draws this frame's parts into the open mesh pass by every rule a baked container needs, each of which fails as a picture rather than an error when it is missed: a part's whole material, so foliage keeps its alpha test and a one-sided card its faces; its copies through one instanced draw, where a part drawn as a plain mesh shows one arch of a repeated row; a blended part translucent whatever its opacity and writing no depth, since its alpha is in its texture, where drawn opaque a grime decal is a black patch; a blended batch through the translucent instanced path; glass as glass, its options kept per pane so a frame allocates nothing; and each part's own reflectivity, with nothing left set for the caller's draws after it. `loader.casters` is a ready `ShadowCasters`: every part but a decal, which would shadow its own surface, each with its material so a cutout casts its shape, glass as glass and copies as copies. `await loader.prepare()` compiles every pipeline the parts draw with, off the frame, translucent where they will be drawn so. `PartDraws` is the same for any list of parts, several loaders' or a filtered one. The manual's chapter on importing models and the models example draw through them."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"kind": "added",
|
|
48
|
+
"short": "`computeLightMatrixForBounds` covers a box with the sun's map whole, and `DrftLoader.shadowFit` fits the map to a model as placed, from the bounds its file declares.",
|
|
49
|
+
"text": "`computeLightMatrixForBounds(lightDir, min, max, mapSize, out)` places a directional light's map around a box of known size: the sphere around the box, at its centre, widened by the half texel the centre's snap may move it, so a corner on the sphere stays inside. A focus and a radius chosen by eye around a guessed centre left part of a model outside the map, where it cast nothing. `DrftLoader.shadowFit(lightDir, mapSize, out)` does it for the model as fitted and returns the depth span for `shadowDepthSpan`, or null until `HEAD` has said how large the model is. What both give up is resolution on a large box, whose texels are spread over all of it; a world that follows a player keeps `computeLightMatrix` around the viewer."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"kind": "added",
|
|
53
|
+
"short": "`meshOcclusion` darkens the colour and the ambient light at once, each at its own strength: `colorStrength` beside `into: 'ambient'`, past 1 where a material asks.",
|
|
54
|
+
"text": "`SurfaceMeshOcclusion.colorStrength` is the colour's own strength: the blended colour times `max(0, 1 − colorStrength × (1 − occlusion))`, which up to 1 is the `mix` a strength always gave and past it darkens faster than the occlusion does. Named beside `into: 'ambient'`, one occlusion darkens the ambient light at `strength` and the colour at `colorStrength`, which is how a material that takes its ambient occlusion from a map and darkens its colour by the same map, at up to twice that, is drawn; with a single target it drew one of the two. Absent, it is `strength` where the colour is the target and 0 where the ambient light is, so every material written for 4.12.0 draws as it did. The two floats that carried a strength and a target now carry the two strengths."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"kind": "fixed",
|
|
58
|
+
"short": "A summed layer blend reflects the surface's own specular once where its layers name none, rather than its own times the sum of the layers' shares.",
|
|
59
|
+
"text": "Under `blend: 'sum'` a layer's share is its weight, and the shares add past 1 where the weights do, which is what a summed blend is for in colour. The specular took the same sum: a layer that named none gave the surface's own times its share, so a surface painted to weights adding to 2.5 or 3 reflected two and a half to three times its own specular, measured as a sandy floor 2.5% brighter across a frame than with its own. The layers that name no specular now give the surface's own at most once, whatever their shares add to, and named speculars still add as their colours do. A blend laid over, whose shares never pass 1, draws exactly as before, as does any layered material while no material has asked for looks."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"kind": "fixed",
|
|
63
|
+
"short": "A reflection probe stays ungraded when its bake's callback binds the mesh pass, so the frame no longer grades what it reads from the probe a second time.",
|
|
64
|
+
"text": "A probe stores radiance, so its bake holds the output transform at 0 while it draws the faces. `bindMeshPass`, which every bake's callback calls first, set the frame's grade again, so under any forward grade each face was graded as it was drawn and the frame graded what it read back from the cube a second time: reflections and probe ambient lifted and flattened. Every pass, the mesh pass among them, now asks one gate, which answers the frame's grade except in a probe's face or a capture. The same gate keeps particles, lines, text and contributed passes ungraded inside a bake."
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"version": "4.12.0",
|
|
70
|
+
"date": "2026-10-10",
|
|
71
|
+
"entries": [
|
|
72
|
+
{
|
|
73
|
+
"kind": "added",
|
|
74
|
+
"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`.",
|
|
75
|
+
"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."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"kind": "added",
|
|
79
|
+
"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.",
|
|
80
|
+
"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."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"kind": "added",
|
|
84
|
+
"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.",
|
|
85
|
+
"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`."
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"kind": "changed",
|
|
89
|
+
"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.",
|
|
90
|
+
"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."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"kind": "added",
|
|
94
|
+
"short": "`SurfaceMaterial.layers` picks each layer from arrays materials share, and gives each layer its own tint, ranges, normal strength, specular and repeats down.",
|
|
95
|
+
"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."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"kind": "added",
|
|
99
|
+
"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.",
|
|
100
|
+
"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."
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
},
|
|
2
104
|
{
|
|
3
105
|
"version": "4.11.1",
|
|
4
106
|
"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: 920.9 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
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
SceneNode,
|
|
36
36
|
createEnvironment,
|
|
37
37
|
createRenderer,
|
|
38
|
+
srgbColor,
|
|
38
39
|
startLoop,
|
|
39
40
|
} from '@driftengine/core';
|
|
40
41
|
import { bindModule } from '@driftengine/script';
|
|
@@ -60,15 +61,23 @@ if (readout !== null) readout.textContent = `${backend}: ${reason}`;
|
|
|
60
61
|
renderer.resize();
|
|
61
62
|
addEventListener('resize', () => renderer.resize());
|
|
62
63
|
|
|
63
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Light, air and ground bounce. Chosen once, because these are allocation-sized decisions.
|
|
66
|
+
*
|
|
67
|
+
* Every colour here and below was picked by eye, so it goes through `srgbColor`: the renderer
|
|
68
|
+
* lights in linear values and encodes them for the screen at the end, and a colour typed as it
|
|
69
|
+
* looks would come out too light. The clear colour in the loop is one of them.
|
|
70
|
+
*/
|
|
71
|
+
const CLEAR = srgbColor(0.05, 0.06, 0.09);
|
|
72
|
+
|
|
64
73
|
const ENV = createEnvironment({
|
|
65
74
|
directionalDir: [0.4, 0.7, 0.35],
|
|
66
|
-
directionalColor:
|
|
67
|
-
ambient:
|
|
68
|
-
ambientGround:
|
|
75
|
+
directionalColor: srgbColor(1, 0.96, 0.88),
|
|
76
|
+
ambient: srgbColor(0.2, 0.22, 0.28),
|
|
77
|
+
ambientGround: srgbColor(0.08, 0.08, 0.1),
|
|
69
78
|
emissiveGain: 0,
|
|
70
79
|
nightFactor: 0,
|
|
71
|
-
fogColor:
|
|
80
|
+
fogColor: srgbColor(0.16, 0.18, 0.24),
|
|
72
81
|
fogDensity: 0.004,
|
|
73
82
|
fogHeightFalloff: 0.03,
|
|
74
83
|
fogBaseY: 0,
|
|
@@ -80,11 +89,11 @@ const ENV = createEnvironment({
|
|
|
80
89
|
* is two units on a side.
|
|
81
90
|
*/
|
|
82
91
|
const shapes = new MeshBuilder();
|
|
83
|
-
shapes.addBox([0, 1, 0], [1, 1, 1],
|
|
92
|
+
shapes.addBox([0, 1, 0], [1, 1, 1], srgbColor(0.85, 0.45, 0.25));
|
|
84
93
|
const cube = renderer.createMesh(shapes.build());
|
|
85
94
|
|
|
86
95
|
const slab = new MeshBuilder();
|
|
87
|
-
slab.addBox([0, -0.25, 0], [12, 0.25, 12],
|
|
96
|
+
slab.addBox([0, -0.25, 0], [12, 0.25, 12], srgbColor(0.3, 0.32, 0.36));
|
|
88
97
|
const ground = renderer.createMesh(slab.build());
|
|
89
98
|
|
|
90
99
|
const spinner = new SceneNode();
|
|
@@ -136,7 +145,7 @@ startLoop({
|
|
|
136
145
|
|
|
137
146
|
camera.updateMatrices(canvas.height > 0 ? canvas.width / canvas.height : 1);
|
|
138
147
|
|
|
139
|
-
renderer.beginFrame(
|
|
148
|
+
renderer.beginFrame(CLEAR);
|
|
140
149
|
renderer.bindMeshPass(camera, ENV);
|
|
141
150
|
renderer.drawMesh(ground, stillness.worldMatrix);
|
|
142
151
|
renderer.drawMesh(cube, spinner.worldMatrix);
|
|
@@ -10,6 +10,16 @@ export interface MeshBuildOptions {
|
|
|
10
10
|
* sample them. On, for a mesh that will be drawn with a `SurfaceTexture`.
|
|
11
11
|
*/
|
|
12
12
|
planarUvs?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The space the builder's colours were given in. `'linear'`, the default, is the renderer's own:
|
|
15
|
+
* light as a surface reflects it. `'srgb'` is a colour picked by eye, on a screen or in a paint
|
|
16
|
+
* program, and decodes every vertex's colour and emissive colour to linear here, once — as a
|
|
17
|
+
* texture of colours uploaded as sRGB is decoded — so a world built from hand-picked colours draws
|
|
18
|
+
* them as picked under the default `outputTransform`, where the same numbers taken as linear come
|
|
19
|
+
* out pale. What it gives up: nothing at the colour picked; a colour meant as light rather than as
|
|
20
|
+
* a look wants a linear number of its own.
|
|
21
|
+
*/
|
|
22
|
+
colorSpace?: 'linear' | 'srgb';
|
|
13
23
|
}
|
|
14
24
|
/**
|
|
15
25
|
* CPU-side accumulator that merges axis-aligned boxes into one static mesh —
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GrowableF32, GrowableU32 } from './growable.js';
|
|
2
|
+
import { decodeSrgbInPlace } from '../math/color.js';
|
|
2
3
|
/**
|
|
3
4
|
* The emissive colour of a vertex that has not been given one: negative, which no real
|
|
4
5
|
* colour can be, so "inherit the surface's albedo" is representable without a second
|
|
@@ -914,14 +915,22 @@ export class MeshBuilder {
|
|
|
914
915
|
/* Same promise as `specular`: a mesh nothing bound uploads no skinning buffers, and
|
|
915
916
|
`MeshData` requires both arrays or neither. */
|
|
916
917
|
const skinned = this.anyJoint;
|
|
918
|
+
/* Colours picked by eye, decoded once here: see `MeshBuildOptions.colorSpace`. */
|
|
919
|
+
const picked = options.colorSpace === 'srgb';
|
|
917
920
|
return {
|
|
918
921
|
positions: this.positions.toTyped(),
|
|
919
922
|
normals: this.normals.toTyped(),
|
|
920
|
-
colors: this.colors.toTyped(),
|
|
923
|
+
colors: picked ? decodeSrgbInPlace(this.colors.toTyped()) : this.colors.toTyped(),
|
|
921
924
|
emissive: this.emissive.toTyped(),
|
|
922
925
|
...(shines ? { specular: this.specular.toTyped() } : {}),
|
|
923
926
|
...(textured ? { relief: this.relief.toTyped() } : {}),
|
|
924
|
-
...(tinted
|
|
927
|
+
...(tinted
|
|
928
|
+
? {
|
|
929
|
+
emissiveColor: picked
|
|
930
|
+
? decodeSrgbInPlace(this.emissiveColor.toTyped())
|
|
931
|
+
: this.emissiveColor.toTyped(),
|
|
932
|
+
}
|
|
933
|
+
: {}),
|
|
925
934
|
...(rough ? { roughness: this.roughness.toTyped() } : {}),
|
|
926
935
|
...(mineral ? { grain: this.grain.toTyped() } : {}),
|
|
927
936
|
...(skinned ? { joints: this.expandedJoints(), weights: this.expandedWeights() } : {}),
|
package/dist/index.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export type { CelestialSite, CelestialState } from './environment/celestialClock
|
|
|
38
38
|
export { createDaylightPalette, createDaylightState, easeExposure, resolveDaylight, } from './environment/daylight.ts';
|
|
39
39
|
export type { DaylightKey, DaylightPalette, DaylightState } from './environment/daylight.ts';
|
|
40
40
|
export { TAU, DEG_TO_RAD, clamp, lerp, smoothstep, damp, dampTracking, angleDelta, dampAngle, wrapAngle, lerpAngle, } from './math/scalar.ts';
|
|
41
|
-
export { hslToRgb, mixColor, mixColorInto, scaleColor } from './math/color.ts';
|
|
41
|
+
export { decodeSrgbInPlace, hslToRgb, mixColor, mixColorInto, scaleColor, srgbColor, srgbToLinear, } from './math/color.ts';
|
|
42
42
|
export type { Vec3 } from './math/color.ts';
|
|
43
43
|
export { Camera } from './render/camera.ts';
|
|
44
44
|
export { ThirdPersonCamera } from './render/thirdPersonCamera.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';
|
|
@@ -248,7 +250,7 @@ export type { LightGrid } from './render/lightGrid.ts';
|
|
|
248
250
|
export type { PointLightBuffer, PointLightSource } from './render/pointLightSelection.ts';
|
|
249
251
|
export { createFlame, flameFrequencyHz, updateFlame } from './render/flameLight.ts';
|
|
250
252
|
export type { Flame, FlameOptions } from './render/flameLight.ts';
|
|
251
|
-
export { computeLightMatrix } from './render/lightMatrix.ts';
|
|
253
|
+
export { computeLightMatrix, computeLightMatrixForBounds } from './render/lightMatrix.ts';
|
|
252
254
|
export { PlumeRenderer } from './render/plumeRenderer.ts';
|
|
253
255
|
export { createEnvironment } from './render/backend/webgl2/renderer.ts';
|
|
254
256
|
export type { InsetRect } from './render/backend/webgl2/renderer.ts';
|
|
@@ -262,6 +264,7 @@ export type { LoadTask, LoadSummary } from './core/loadTracker.ts';
|
|
|
262
264
|
export { buildShellFill, shellSkin, shellFillMatrix, DEFAULT_SHELL_PROFILE, } from './geometry/shellFill.ts';
|
|
263
265
|
export type { ShellStation, ShellFillOptions } from './geometry/shellFill.ts';
|
|
264
266
|
export { OUTPUT_TRANSFORM_GLSL } from './render/shaders/outputTransform.ts';
|
|
267
|
+
export { interfaceGrade } from './render/passGrade.ts';
|
|
265
268
|
export { PLUME_VERT, FIRE_FRAG } from './render/shaders/fire.ts';
|
|
266
269
|
export { ARCANE_FRAG } from './render/shaders/arcane.ts';
|
|
267
270
|
export { SMOKE_FRAG } from './render/shaders/smoke.ts';
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ export { MessageQueue } from './core/messageQueue.js';
|
|
|
35
35
|
export { celestialStateAt, createCelestialState, moonIllumination, } from './environment/celestialClock.js';
|
|
36
36
|
export { createDaylightPalette, createDaylightState, easeExposure, resolveDaylight, } from './environment/daylight.js';
|
|
37
37
|
export { TAU, DEG_TO_RAD, clamp, lerp, smoothstep, damp, dampTracking, angleDelta, dampAngle, wrapAngle, lerpAngle, } from './math/scalar.js';
|
|
38
|
-
export { hslToRgb, mixColor, mixColorInto, scaleColor } from './math/color.js';
|
|
38
|
+
export { decodeSrgbInPlace, hslToRgb, mixColor, mixColorInto, scaleColor, srgbColor, srgbToLinear, } from './math/color.js';
|
|
39
39
|
export { Camera } from './render/camera.js';
|
|
40
40
|
export { ThirdPersonCamera } from './render/thirdPersonCamera.js';
|
|
41
41
|
export { CinematicCamera } from './render/cinematicCamera.js';
|
|
@@ -305,7 +305,7 @@ export { describeGpu } from './render/gpuCapability.js';
|
|
|
305
305
|
export { DEFAULT_POINT_LIGHT_VIEW_RANGE, createPointLightBuffer, selectGridLights, selectPointLights, } from './render/pointLightSelection.js';
|
|
306
306
|
export { LIGHT_GRID_MAX_CELLS, createLightGrid, gatherLights } from './render/lightGrid.js';
|
|
307
307
|
export { createFlame, flameFrequencyHz, updateFlame } from './render/flameLight.js';
|
|
308
|
-
export { computeLightMatrix } from './render/lightMatrix.js';
|
|
308
|
+
export { computeLightMatrix, computeLightMatrixForBounds } from './render/lightMatrix.js';
|
|
309
309
|
export { PlumeRenderer } from './render/plumeRenderer.js';
|
|
310
310
|
export { createEnvironment } from './render/backend/webgl2/renderer.js';
|
|
311
311
|
export { TrampleField, TRAMPLE_SLOTS } from './render/trample.js';
|
|
@@ -337,6 +337,7 @@ export { buildShellFill, shellSkin, shellFillMatrix, DEFAULT_SHELL_PROFILE, } fr
|
|
|
337
337
|
* this line the only way for it to comply was to copy, which is what the rule forbids.
|
|
338
338
|
*/
|
|
339
339
|
export { OUTPUT_TRANSFORM_GLSL } from './render/shaders/outputTransform.js';
|
|
340
|
+
export { interfaceGrade } from './render/passGrade.js';
|
|
340
341
|
export { PLUME_VERT, FIRE_FRAG } from './render/shaders/fire.js';
|
|
341
342
|
export { ARCANE_FRAG } from './render/shaders/arcane.js';
|
|
342
343
|
export { SMOKE_FRAG } from './render/shaders/smoke.js';
|
package/dist/math/color.d.ts
CHANGED
|
@@ -6,3 +6,28 @@ export declare function mixColor(a: Vec3, b: Vec3, t: number): Vec3;
|
|
|
6
6
|
/** In-place `mixColor` for per-frame use — allocating here would be a hot-path bug. */
|
|
7
7
|
export declare function mixColorInto(out: Vec3, a: Vec3, b: Vec3, t: number): void;
|
|
8
8
|
export declare function scaleColor(color: Vec3, factor: number): Vec3;
|
|
9
|
+
/**
|
|
10
|
+
* A colour picked by eye — on a screen, in a paint program, as a CSS colour — as the linear value
|
|
11
|
+
* the renderer lights, blends, fogs and grades: each channel through the sRGB curve, which the
|
|
12
|
+
* default `outputTransform`, `'srgb'`, applies the other way at the end, so a colour drawn as it is,
|
|
13
|
+
* a sky, a clear or a surface lit at exactly 1, comes out as the colour picked.
|
|
14
|
+
*
|
|
15
|
+
* **Why a colour needs it**: everything the renderer draws is linear and encoded at the end, so a
|
|
16
|
+
* colour typed as it looks comes out too light — a brown picked as `(0.55, 0.3, 0.15)` draws as
|
|
17
|
+
* tan. **What it gives up**: nothing at the colour picked. **What would make it wrong** is a number
|
|
18
|
+
* that is an amount rather than a look: a light's intensity, a multiplier, a strength, which are
|
|
19
|
+
* linear already. Allocates, so it is for building a world rather than for a frame.
|
|
20
|
+
*/
|
|
21
|
+
export declare function srgbColor(r: number, g: number, b: number): Vec3;
|
|
22
|
+
/**
|
|
23
|
+
* Every value of `values` decoded from sRGB in place, but a negative one, which is a sentinel and
|
|
24
|
+
* not a colour (`MeshBuilder`'s emissive colour left to the albedo). For whole arrays of colours
|
|
25
|
+
* picked by eye; see `MeshBuildOptions.colorSpace`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function decodeSrgbInPlace(values: Float32Array): Float32Array;
|
|
28
|
+
/**
|
|
29
|
+
* One channel, 0 to 1, through the sRGB decode: the straight segment below 0.04045. For a colour
|
|
30
|
+
* that arrives as numbers per channel rather than as a triple, a packed hex token being the usual
|
|
31
|
+
* one; allocates nothing.
|
|
32
|
+
*/
|
|
33
|
+
export declare function srgbToLinear(value: number): number;
|
package/dist/math/color.js
CHANGED
|
@@ -33,3 +33,40 @@ export function mixColorInto(out, a, b, t) {
|
|
|
33
33
|
export function scaleColor(color, factor) {
|
|
34
34
|
return [color[0] * factor, color[1] * factor, color[2] * factor];
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* A colour picked by eye — on a screen, in a paint program, as a CSS colour — as the linear value
|
|
38
|
+
* the renderer lights, blends, fogs and grades: each channel through the sRGB curve, which the
|
|
39
|
+
* default `outputTransform`, `'srgb'`, applies the other way at the end, so a colour drawn as it is,
|
|
40
|
+
* a sky, a clear or a surface lit at exactly 1, comes out as the colour picked.
|
|
41
|
+
*
|
|
42
|
+
* **Why a colour needs it**: everything the renderer draws is linear and encoded at the end, so a
|
|
43
|
+
* colour typed as it looks comes out too light — a brown picked as `(0.55, 0.3, 0.15)` draws as
|
|
44
|
+
* tan. **What it gives up**: nothing at the colour picked. **What would make it wrong** is a number
|
|
45
|
+
* that is an amount rather than a look: a light's intensity, a multiplier, a strength, which are
|
|
46
|
+
* linear already. Allocates, so it is for building a world rather than for a frame.
|
|
47
|
+
*/
|
|
48
|
+
export function srgbColor(r, g, b) {
|
|
49
|
+
return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Every value of `values` decoded from sRGB in place, but a negative one, which is a sentinel and
|
|
53
|
+
* not a colour (`MeshBuilder`'s emissive colour left to the albedo). For whole arrays of colours
|
|
54
|
+
* picked by eye; see `MeshBuildOptions.colorSpace`.
|
|
55
|
+
*/
|
|
56
|
+
export function decodeSrgbInPlace(values) {
|
|
57
|
+
for (let i = 0; i < values.length; i++) {
|
|
58
|
+
const value = values[i];
|
|
59
|
+
if (value >= 0)
|
|
60
|
+
values[i] = srgbToLinear(value);
|
|
61
|
+
}
|
|
62
|
+
return values;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One channel, 0 to 1, through the sRGB decode: the straight segment below 0.04045. For a colour
|
|
66
|
+
* that arrives as numbers per channel rather than as a triple, a packed hex token being the usual
|
|
67
|
+
* one; allocates nothing.
|
|
68
|
+
*/
|
|
69
|
+
export function srgbToLinear(value) {
|
|
70
|
+
// determinism: build-time — a colour for drawing, which no tick reads
|
|
71
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
72
|
+
}
|
|
@@ -1290,15 +1290,22 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1290
1290
|
private readonly transmission;
|
|
1291
1291
|
/** `uLayers`, the current material's layers packed. See `surfaceLayers.ts`. */
|
|
1292
1292
|
private readonly layerFloats;
|
|
1293
|
+
/** `uLayerLooks`, the current material's layer picks and looks packed. See `surfaceLayerLooks.ts`. */
|
|
1294
|
+
private readonly lookFloats;
|
|
1293
1295
|
/** Whether the lit stage has room for layers' two vectors, and which refusals were said. */
|
|
1294
1296
|
private layersRoom;
|
|
1295
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;
|
|
1296
1301
|
private warnedLayersModelMap;
|
|
1297
1302
|
private warnedLayersTriplanar;
|
|
1298
1303
|
private warnedOverlayRoom;
|
|
1299
1304
|
private warnedOverlayBlocks;
|
|
1300
1305
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1301
1306
|
private probePassActive;
|
|
1307
|
+
/** `clearOf`'s scratch: a clear colour as the open target takes it. */
|
|
1308
|
+
private readonly gradedClear;
|
|
1302
1309
|
/**
|
|
1303
1310
|
* Whether a scene capture is being drawn (`captureScene`): an off-screen pass like a bake, so the
|
|
1304
1311
|
* frame's own view, its refraction copy and skin's spread are left alone, but lit as the frame is.
|
|
@@ -2165,9 +2172,19 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2165
2172
|
* no room for their two vectors each leave one layer, the last two said once each.
|
|
2166
2173
|
*/
|
|
2167
2174
|
private layersOf;
|
|
2175
|
+
/**
|
|
2176
|
+
* Whether a layered material's picks and looks are drawn: asked for, and room for their fifteen
|
|
2177
|
+
* vectors beside the layers' four. Refused, the layers are drawn reading layer `i` of their arrays
|
|
2178
|
+
* as they are, said once — which on a material sharing its arrays reads the wrong textures, so
|
|
2179
|
+
* the sentence names the way out.
|
|
2180
|
+
*/
|
|
2181
|
+
private looksFit;
|
|
2168
2182
|
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
2169
2183
|
private warnLayersTriplanar;
|
|
2170
|
-
/**
|
|
2184
|
+
/**
|
|
2185
|
+
* Whether the lit shader with `feature` compiled in, and `beside` with it where a switch means
|
|
2186
|
+
* nothing alone, fits this GPU's fragment uniform vectors.
|
|
2187
|
+
*/
|
|
2171
2188
|
private switchFits;
|
|
2172
2189
|
/**
|
|
2173
2190
|
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
@@ -2546,7 +2563,9 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2546
2563
|
* real draw call touches the unit in the first place.
|
|
2547
2564
|
*/
|
|
2548
2565
|
/**
|
|
2549
|
-
* The
|
|
2566
|
+
* The grade a pass applies to what it draws into the target open now, and the exposure it
|
|
2567
|
+
* applies it at. The decision is `passGrade.ts`'s, for both backends; this only gathers the
|
|
2568
|
+
* state it is taken from.
|
|
2550
2569
|
*
|
|
2551
2570
|
* **Only where that pass is genuinely the last one to touch the frame.** With a scene target
|
|
2552
2571
|
* the resolve grades, so a pass that graded as well would be applying a curve to its own
|
|
@@ -2554,15 +2573,24 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
2554
2573
|
* `screenEffects` off there is no resolve at all and every pass writes straight to the canvas,
|
|
2555
2574
|
* so each one has to grade itself or the frame comes out half converted: linear values in an
|
|
2556
2575
|
* eight-bit buffer read as display values, which is a saturated, clipped version of the colour
|
|
2557
|
-
* the caller asked for rather than a slightly different one.
|
|
2576
|
+
* the caller asked for rather than a slightly different one. A probe's face or a capture is
|
|
2577
|
+
* never graded, and past the present every pass grades itself, composite or not.
|
|
2558
2578
|
*
|
|
2559
2579
|
* One pair of accessors rather than the expression written out at each site, because that is
|
|
2560
2580
|
* how the mesh pass and the particle pass came to disagree in the first place: the mesh pass
|
|
2561
2581
|
* gated, and the particle pass uploaded the transform unconditionally to a stage that did not
|
|
2562
2582
|
* yet declare it.
|
|
2563
2583
|
*/
|
|
2564
|
-
private
|
|
2565
|
-
private
|
|
2584
|
+
private passGrade;
|
|
2585
|
+
private passGradeExposure;
|
|
2586
|
+
/** Whether the frame's resolve applies the transform, so the passes before it draw linear. */
|
|
2587
|
+
private resolveGrades;
|
|
2588
|
+
/**
|
|
2589
|
+
* A clear colour as the target open now takes it: graded as a pass would grade it, so a colour
|
|
2590
|
+
* handed to the renderer is linear wherever it lands — the clear too, which the resolve grades
|
|
2591
|
+
* with everything else under `hdrScene`. See `gradeColor.ts`. Into one scratch, read at once.
|
|
2592
|
+
*/
|
|
2593
|
+
private clearOf;
|
|
2566
2594
|
drawSdfText(handle: SdfTextRenderer, model: Float32Array, color: Vec3, opacity: number): void;
|
|
2567
2595
|
/** Release an SDF text label. Takes no context; see `disposeText`. */
|
|
2568
2596
|
disposeSdfText(handle: SdfTextRenderer): void;
|