lecodes-sdk 2.2.14 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/gl/Scene.d.ts +0 -11
- package/dist/types/gl/postProcessing.d.ts +31 -2
- package/dist/types/runtime/device.d.ts +7 -19
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/src/bridges/gl.d.ts +5 -13
- package/src/compile/serverSplit.ts +8 -1
- package/src/gl/Scene.ts +0 -13
- package/src/gl/postProcessing.ts +52 -9
- package/src/runtime/device.ts +8 -37
- package/src/version.ts +1 -1
package/package.json
CHANGED
package/src/bridges/gl.d.ts
CHANGED
|
@@ -127,9 +127,11 @@ declare global {
|
|
|
127
127
|
setVignetteOptions?(sceneId: u32, enabled: boolean, midPoint: f32, roundness: f32, feather: f32, color: u32): void
|
|
128
128
|
/** Colour grading: the operator and every adjustment baked into the view's ONE 3D LUT. `params` is
|
|
129
129
|
* the fixed layout of `sdk/src/gl/postProcessing.ts` (`CG_*`, mirrored by creator.h's
|
|
130
|
-
* ColorGradingParam;
|
|
131
|
-
*
|
|
132
|
-
*
|
|
130
|
+
* ColorGradingParam; 41 floats: [38] = apply the LUT of setColorLut, [39] / [40] = the HDR look,
|
|
131
|
+
* strength 0..1 and paper white 1..8, read only while the output is extended range). The engine
|
|
132
|
+
* takes an older bundle's 39 with the HDR look at its defaults. A change re-bakes a 32³ table on
|
|
133
|
+
* the CPU, so the SDK coalesces its writes per tick and quantises; an identical array is a no-op
|
|
134
|
+
* in the engine. Optional: a host that predates it renders filament's default (ACES legacy). */
|
|
133
135
|
setColorGrading?(sceneId: u32, params: Float32Array): void
|
|
134
136
|
/** A custom 3D LUT after the operator, in display-referred sRGB: the TEXT of a `.cube` file
|
|
135
137
|
* (`LUT_3D_SIZE n` + n³ `r g b` lines, red fastest), parsed by the engine once and applied while
|
|
@@ -156,17 +158,7 @@ declare global {
|
|
|
156
158
|
* (1 = SDR), constant once the surface exists: the value to branch content on. Optional: hosts
|
|
157
159
|
* that predate it, or render 8-bit, are SDR. */
|
|
158
160
|
getDisplayHeadroom?(): f64
|
|
159
|
-
/** @c getDisplayHeadroom */
|
|
160
161
|
getDisplayMaxHeadroom?(): f64
|
|
161
|
-
/** HDR look, engine-wide (creator.h `setHdrStrength` / `setHdrPaperWhite`): `strength` 0..1 =
|
|
162
|
-
* how much of the picture reaches for the headroom (0 only what SDR clipped, 1 nearly
|
|
163
|
-
* everything), `paperWhite` 1..8 = where the operator's white lands as a multiple of SDR white
|
|
164
|
-
* (the "HDR brightness" of a console calibration screen; clamped to the headroom). A host may
|
|
165
|
-
* pin either from its environment — the getters report what is in force. Optional. */
|
|
166
|
-
setHdrStrength?(strength: f32): void
|
|
167
|
-
getHdrStrength?(): f64
|
|
168
|
-
setHdrPaperWhite?(paperWhite: f32): void
|
|
169
|
-
getHdrPaperWhite?(): f64
|
|
170
162
|
/** Screen-space ambient occlusion: the contact darkening in creases and where objects meet the
|
|
171
163
|
* ground. `radius` is world-space metres, `power` the falloff contrast, `quality` 0 LOW … 3
|
|
172
164
|
* ULTRA (sample count — not the buffer resolution, which stays half-res).
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { AppConfig } from "../server/auth/appConfig"
|
|
16
|
+
import { CODE_EXT } from "./assetMacro"
|
|
16
17
|
import { bundleProjectWithMap, SERVER_INJECT, type MemoryFiles } from "./bundler"
|
|
17
18
|
import type { CompileEntry } from "./compileProject"
|
|
18
19
|
import { deriveEndpointParams } from "./serverTypes"
|
|
@@ -271,12 +272,18 @@ export const compileServerBundle = async (opts: CompileServerOptions): Promise<{
|
|
|
271
272
|
// with a Figma-exported design next to app.server.ts). A stub's value is the resource's PATH: a
|
|
272
273
|
// row of shared data that names a picture (`photo: "images/a.jpg"`) reaches the server as the
|
|
273
274
|
// path of that picture, which a seed stores into a file field (files/db.ts). The board's `design/`
|
|
274
|
-
// files are never server code and stay out entirely.
|
|
275
|
+
// files are never server code and stay out entirely. A text file that is not code follows the
|
|
276
|
+
// app bundle's rules (compileProject.ts entriesToFiles): a `.json` is its parsed data — a shared
|
|
277
|
+
// module of a price list the person gave does `import raw from "./price.json"`, and the seed
|
|
278
|
+
// that imports the module ran into "raw is not defined" while the text went in as code — and any
|
|
279
|
+
// other text (.csv, .txt, .md) is its string.
|
|
275
280
|
const files: MemoryFiles = {}
|
|
276
281
|
for (const e of opts.entries) {
|
|
277
282
|
const path = normalizePath(e.path)
|
|
278
283
|
if (/^\/design\//.test(path)) continue
|
|
279
284
|
if (e.type !== "text" || path.toLowerCase().endsWith(".svg")) { files[path] = `export default ${JSON.stringify(path)}`; continue }
|
|
285
|
+
if (path.toLowerCase().endsWith(".json")) { files[path] = `export default ${e.text?.trim() || "null"}`; continue }
|
|
286
|
+
if (!CODE_EXT.test(path)) { files[path] = `export default ${JSON.stringify(e.text ?? "")}`; continue }
|
|
280
287
|
files[path] = e.text ?? ""
|
|
281
288
|
}
|
|
282
289
|
let params = opts.params
|
package/src/gl/Scene.ts
CHANGED
|
@@ -12,7 +12,6 @@ import { _bumpNavEpoch, _setCurrent, Presentable, type DestTuple, type DismissOp
|
|
|
12
12
|
import { _transitionId, TRANSITION_NONE } from "../ui/transitions"
|
|
13
13
|
import { TREE_DEST_SCENE3D, TREE_FLAG_BACK, ensureEmitter, registerDest, unregisterDest } from "../ui/tree"
|
|
14
14
|
import { _surfaceWidgets, type LayerElement, type WidgetSet } from "../ui/UILayer"
|
|
15
|
-
import { device } from "../runtime/device"
|
|
16
15
|
import { File } from "../runtime/fetch"
|
|
17
16
|
import type { ClickEvent, TouchStartEvent } from "../runtime/touch"
|
|
18
17
|
import { Camera } from "./Camera"
|
|
@@ -142,14 +141,6 @@ export type SceneOptions = {
|
|
|
142
141
|
* loses its top mip levels on load, a glTF image is downsampled. Applied before this scene's
|
|
143
142
|
* assets load; like `anisotropy` it does not touch textures already loaded. */
|
|
144
143
|
maxTextureSize?: number
|
|
145
|
-
/** The look on an HDR display (a screen with headroom above SDR white — Apple XDR panels, the
|
|
146
|
-
* macOS host today); ignored on SDR. `strength` 0..1 is how much of the picture reaches for the
|
|
147
|
-
* display's headroom (0 only what SDR clipped, 1 nearly everything; default 0.35). `paperWhite`
|
|
148
|
-
* 1..8 is where white lands as a multiple of SDR white — the "HDR brightness" of a console
|
|
149
|
-
* calibration screen, where 1.5–2 is the norm; default 1 keeps a white wall at the UI's white,
|
|
150
|
-
* faithful but dim next to what "HDR on" is expected to look like. Engine-wide like `anisotropy`;
|
|
151
|
-
* `device.hdr` is the runtime form and tells you whether the display has any headroom at all. */
|
|
152
|
-
hdr?: { strength?: number; paperWhite?: number }
|
|
153
144
|
}
|
|
154
145
|
|
|
155
146
|
// Route aspect update(dt) through the render-synced native phases (render() early = before physics,
|
|
@@ -253,10 +244,6 @@ export class Scene implements Presentable {
|
|
|
253
244
|
// place by the time assets bind their samplers.
|
|
254
245
|
if (options.anisotropy !== undefined) Texture.anisotropy = options.anisotropy
|
|
255
246
|
if (options.maxTextureSize !== undefined) Texture.maxSize = options.maxTextureSize
|
|
256
|
-
if (options.hdr) {
|
|
257
|
-
if (options.hdr.strength !== undefined) device.hdr.strength = options.hdr.strength
|
|
258
|
-
if (options.hdr.paperWhite !== undefined) device.hdr.paperWhite = options.hdr.paperWhite
|
|
259
|
-
}
|
|
260
247
|
if (options.renderScale !== undefined || options.dynamicResolution) {
|
|
261
248
|
this.setRenderOptions(options.renderScale ?? 1, options.dynamicResolution ?? false)
|
|
262
249
|
}
|
package/src/gl/postProcessing.ts
CHANGED
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
// Two bands inside, because the engine treats them differently:
|
|
13
13
|
//
|
|
14
14
|
// - The GRADING band — tone mapping, exposure, white balance, contrast / saturation / vibrance,
|
|
15
|
-
// shadows / midtones / highlights, slope / offset / power, curves, the .cube LUT
|
|
16
|
-
// engine into the ONE 3D lookup table the frame always samples. Free per frame;
|
|
17
|
-
// a 32³ table on the CPU. So writes here are COALESCED: a field write marks the
|
|
18
|
-
// whole band goes to the engine once, in a microtask (the end of the current
|
|
19
|
-
// values are quantised to 1/1000
|
|
20
|
-
// animate every frame; a blend between two looks over a
|
|
15
|
+
// shadows / midtones / highlights, slope / offset / power, curves, the .cube LUT, the HDR look —
|
|
16
|
+
// is baked by the engine into the ONE 3D lookup table the frame always samples. Free per frame;
|
|
17
|
+
// a CHANGE re-bakes a 32³ table on the CPU. So writes here are COALESCED: a field write marks the
|
|
18
|
+
// band dirty and the whole band goes to the engine once, in a microtask (the end of the current
|
|
19
|
+
// handler), and the values are quantised to 1/1000 (the HDR look to 0.05) so a tween that barely
|
|
20
|
+
// moves does not re-bake. Not a thing to animate every frame; a blend between two looks over a
|
|
21
|
+
// second is fine (a few dozen bakes).
|
|
21
22
|
// - The LIVE band — bloom, vignette, ambient occlusion, screen-space reflections, depth of field —
|
|
22
23
|
// is a set of view options: written to the engine at once, free to animate (a focus pull every
|
|
23
24
|
// frame is what depthOfField is for).
|
|
@@ -134,6 +135,26 @@ export type ColorCurves = {
|
|
|
134
135
|
highlightScale?: Rgb
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
/** The look on an HDR display — a screen with headroom above SDR white (Apple's XDR panels, an
|
|
139
|
+
* external display in HDR mode), where the operator is stretched past SDR white toward the peak.
|
|
140
|
+
* Ignored on SDR: stored, read back, nothing changes on screen (`device.hdr.available` says which
|
|
141
|
+
* display this is). Part of the grading band — a change is a bake — and quantised to 0.05 steps. */
|
|
142
|
+
export type HdrLook = {
|
|
143
|
+
/** How much of the picture reaches for the display's headroom, 0..1 (default 0.35). 0 touches only
|
|
144
|
+
* what SDR clipped — the SDR grade kept exactly, but a mostly-mid-tone frame then looks flat next
|
|
145
|
+
* to its one bright spot; 1 lifts nearly everything above black toward the peak. A sunlit
|
|
146
|
+
* exterior takes 1, a dim interior wants less. */
|
|
147
|
+
strength?: number
|
|
148
|
+
/** Where white lands, as a multiple of SDR white, 1..8 (default 1) — the "HDR brightness" of a
|
|
149
|
+
* console game's calibration screen, where 1.5–2 is the norm. It scales the WHOLE picture: at 1
|
|
150
|
+
* a white wall is as bright as the UI's white (faithful, and dim next to what "HDR on" is
|
|
151
|
+
* expected to look like), at 2 twice that, and highlights still climb above it (the engine keeps
|
|
152
|
+
* `headroom / paperWhite` for them, clamped at the peak). `strength` decides how much of the
|
|
153
|
+
* picture reaches for the peak; this decides where the picture starts — a game that wants to read
|
|
154
|
+
* as "HDR on" rather than "SDR with a brighter sun" wants this over `strength`. */
|
|
155
|
+
paperWhite?: number
|
|
156
|
+
}
|
|
157
|
+
|
|
137
158
|
export type PostProcessingOptions = {
|
|
138
159
|
/** The operator that folds the scene's HDR light into the display's range (default 'aces').
|
|
139
160
|
* `'aces'` (filament's ACES legacy) desaturates bright colours towards white — HDR fire reads
|
|
@@ -167,6 +188,11 @@ export type PostProcessingOptions = {
|
|
|
167
188
|
* the operator, in display-referred sRGB — a `.cube` file: `asset('./look.cube')`, or a bare
|
|
168
189
|
* staged filename. `null` removes it. */
|
|
169
190
|
lut?: string | null
|
|
191
|
+
/** The look on an HDR display: `strength` 0..1 (how much of the picture reaches for the peak:
|
|
192
|
+
* 0 only what SDR clipped … 1 nearly everything; default 0.35), `paperWhite` 1..8 (where white
|
|
193
|
+
* lands × SDR white — the console "HDR brightness", 1.5–2 the norm; default 1). Ignored on SDR.
|
|
194
|
+
* An overlay takes its parent's, like the rest of the grading. */
|
|
195
|
+
hdr?: HdrLook
|
|
170
196
|
/** `true` = defaults (intensity 0.2), an object = its knobs, `false` = off. */
|
|
171
197
|
bloom?: boolean | BloomOptions
|
|
172
198
|
/** `true` = defaults (a soft black edge), an object = its knobs, `false` = off. */
|
|
@@ -180,12 +206,13 @@ export type PostProcessingOptions = {
|
|
|
180
206
|
}
|
|
181
207
|
|
|
182
208
|
/** The float layout of `_creator.setColorGrading` — creator.h's ColorGradingParam. Indices never
|
|
183
|
-
* move; a new field goes at the end and COUNT grows
|
|
209
|
+
* move; a new field goes at the end and COUNT grows (the engine takes an older bundle's shorter
|
|
210
|
+
* array, down to the 39 before HDR_*, with the tail at its defaults). */
|
|
184
211
|
export const CG = {
|
|
185
212
|
TONE_MAPPING: 0, EXPOSURE: 1, TEMPERATURE: 2, TINT: 3, CONTRAST: 4, VIBRANCE: 5, SATURATION: 6,
|
|
186
213
|
SHADOWS: 7, MIDTONES: 10, HIGHLIGHTS: 13, RANGES: 16, SLOPE: 20, OFFSET: 23, POWER: 26,
|
|
187
|
-
SHADOW_GAMMA: 29, MID_POINT: 32, HIGHLIGHT_SCALE: 35, LUT: 38,
|
|
188
|
-
COUNT:
|
|
214
|
+
SHADOW_GAMMA: 29, MID_POINT: 32, HIGHLIGHT_SCALE: 35, LUT: 38, HDR_STRENGTH: 39, HDR_PAPER_WHITE: 40,
|
|
215
|
+
COUNT: 41,
|
|
189
216
|
} as const
|
|
190
217
|
|
|
191
218
|
const TONE_MAPPING_MODE: Record<ToneMappingOperator, number> = { aces: 0, neutral: 1, linear: 2, filmic: 3 }
|
|
@@ -196,6 +223,10 @@ const ONE: Rgb = [ 1, 1, 1 ]
|
|
|
196
223
|
const ZERO: Rgb = [ 0, 0, 0 ]
|
|
197
224
|
const rgb = (v: Rgb | undefined, d: Rgb): Rgb => v ? [ v[0], v[1], v[2] ] : d
|
|
198
225
|
const pair = (v: readonly [number, number] | undefined, d: readonly [number, number]): readonly [number, number] => v ? [ v[0], v[1] ] : d
|
|
226
|
+
/** The HDR look on the wire: clamped to its range and quantised to 0.05 — a slider must not re-bake a
|
|
227
|
+
* 32³ LUT per pixel of travel (the quantum is the SDK's; the engine only clamps). */
|
|
228
|
+
const hdrStep = (v: number, min: number, max: number, d: number): number =>
|
|
229
|
+
Number.isFinite(v) ? Math.round(Math.min(max, Math.max(min, v)) * 20) / 20 : d
|
|
199
230
|
|
|
200
231
|
/** The grading band, normalised: every field present. */
|
|
201
232
|
type Grading = {
|
|
@@ -218,12 +249,15 @@ type Grading = {
|
|
|
218
249
|
midPoint: Rgb
|
|
219
250
|
highlightScale: Rgb
|
|
220
251
|
lut: string | null
|
|
252
|
+
hdrStrength: number
|
|
253
|
+
hdrPaperWhite: number
|
|
221
254
|
}
|
|
222
255
|
|
|
223
256
|
const defaultGrading = (): Grading => ({
|
|
224
257
|
toneMapping: "aces", exposure: 0, temperature: 0, tint: 0, contrast: 1, saturation: 1, vibrance: 1,
|
|
225
258
|
shadows: ONE, midtones: ONE, highlights: ONE, rangeShadows: [ 0, 0.333 ], rangeHighlights: [ 0.55, 1 ],
|
|
226
259
|
slope: ONE, offset: ZERO, power: ONE, shadowGamma: ONE, midPoint: ONE, highlightScale: ONE, lut: null,
|
|
260
|
+
hdrStrength: 0.35, hdrPaperWhite: 1,
|
|
227
261
|
})
|
|
228
262
|
|
|
229
263
|
const BLOOM_DEFAULTS: Required<BloomOptions> = { intensity: 0.2, threshold: true, highlight: 1000, levels: 6, quality: "medium", lensFlare: false, dirt: null, dirtStrength: 0.2 }
|
|
@@ -279,6 +313,7 @@ export class PostProcessing {
|
|
|
279
313
|
if (options.power !== undefined) this.power = options.power
|
|
280
314
|
if (options.curves !== undefined) this.curves = options.curves
|
|
281
315
|
if (options.lut !== undefined) this.lut = options.lut
|
|
316
|
+
if (options.hdr !== undefined) this.hdr = options.hdr
|
|
282
317
|
this.flush()
|
|
283
318
|
if (options.bloom !== undefined) this.bloom = options.bloom
|
|
284
319
|
if (options.vignette !== undefined) this.vignette = options.vignette
|
|
@@ -348,6 +383,12 @@ export class PostProcessing {
|
|
|
348
383
|
}
|
|
349
384
|
get lut(): string | null { return this._g.lut }
|
|
350
385
|
set lut(v: string | null) { this._g.lut = v; this._lutDirty = v !== this._sentLut; this._touch() }
|
|
386
|
+
get hdr(): Required<HdrLook> { return { strength: this._g.hdrStrength, paperWhite: this._g.hdrPaperWhite } }
|
|
387
|
+
set hdr(v: HdrLook) {
|
|
388
|
+
if (v.strength !== undefined) this._g.hdrStrength = v.strength
|
|
389
|
+
if (v.paperWhite !== undefined) this._g.hdrPaperWhite = v.paperWhite
|
|
390
|
+
this._touch()
|
|
391
|
+
}
|
|
351
392
|
|
|
352
393
|
// ---- the live band: view options, written at once ----
|
|
353
394
|
|
|
@@ -445,6 +486,8 @@ export class PostProcessing {
|
|
|
445
486
|
put3(CG.MID_POINT, g.midPoint)
|
|
446
487
|
put3(CG.HIGHLIGHT_SCALE, g.highlightScale)
|
|
447
488
|
p[CG.LUT] = g.lut !== null ? 1 : 0
|
|
489
|
+
p[CG.HDR_STRENGTH] = hdrStep(g.hdrStrength, 0, 1, 0.35)
|
|
490
|
+
p[CG.HDR_PAPER_WHITE] = hdrStep(g.hdrPaperWhite, 1, 8, 1)
|
|
448
491
|
return p
|
|
449
492
|
}
|
|
450
493
|
}
|
package/src/runtime/device.ts
CHANGED
|
@@ -30,11 +30,8 @@ const onResize = (width: number, height: number): void => {
|
|
|
30
30
|
|
|
31
31
|
// --- device.hdr state ---
|
|
32
32
|
// The 3D bridge is a free global that a 2D-only bundle never defines — reach it defensively. Hosts
|
|
33
|
-
// without the methods are SDR by definition
|
|
34
|
-
// back what the app asked for.
|
|
33
|
+
// without the methods are SDR by definition.
|
|
35
34
|
const gl = (): typeof _creator | undefined => (typeof _creator !== "undefined" ? _creator : undefined)
|
|
36
|
-
let hdrStrengthLocal = 0.35
|
|
37
|
-
let hdrPaperWhiteLocal = 1
|
|
38
35
|
// `hdrchange` is a poll: the engine quantises the headroom to half-stops, so a change is rare and
|
|
39
36
|
// a 4 Hz timer (only while someone listens) is cheaper than a per-frame bridge call.
|
|
40
37
|
const hdrListeners: HdrChangeCallback[] = []
|
|
@@ -202,11 +199,13 @@ export const device = {
|
|
|
202
199
|
return new Vec3(g.x, -g.y, g.z)
|
|
203
200
|
},
|
|
204
201
|
},
|
|
205
|
-
/** The display's extended dynamic range — HDR
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* the
|
|
202
|
+
/** The display's extended dynamic range — HDR: the FACTS about the display. On the macOS desktop
|
|
203
|
+
* host, in a visible window on a screen with headroom above SDR white (Apple's XDR panels, an HDR
|
|
204
|
+
* external display), the 3D surface is float and the engine renders past 1.0: 1.0 is the white of
|
|
205
|
+
* the UI and of a diffuse white surface, and the sun, emissives and speculars climb up to
|
|
206
|
+
* `headroom` times that. Everywhere else it is SDR (the other hosts, a hidden window, a render to
|
|
207
|
+
* PNG): `available` false, `headroom` 1. The LOOK on such a display — how much of the picture
|
|
208
|
+
* reaches for the peak, where white lands — is the scene's: `scene.postProcessing.hdr`.
|
|
210
209
|
*
|
|
211
210
|
* Branch content on `available`, not on `headroom` — the live value ramps up from 1 over the
|
|
212
211
|
* first seconds after launch and follows the brightness keys. Particle `emissive` is the thing
|
|
@@ -230,34 +229,6 @@ export const device = {
|
|
|
230
229
|
const v = gl()?.getDisplayHeadroom?.() ?? 1
|
|
231
230
|
return v > 1 ? v : 1
|
|
232
231
|
},
|
|
233
|
-
/** How much of the picture reaches for the headroom, 0..1 (default 0.35). 0 touches only what
|
|
234
|
-
* SDR clipped — faithful to the SDR grade, but a mostly-mid-tone frame then looks flat next to
|
|
235
|
-
* the one bright spot; 1 lifts nearly everything above black toward the peak. A sunlit exterior
|
|
236
|
-
* takes 1, a dim interior wants less. A host may pin it (`LECODES_HDR_STRENGTH`). */
|
|
237
|
-
get strength(): number {
|
|
238
|
-
return gl()?.getHdrStrength?.() ?? hdrStrengthLocal
|
|
239
|
-
},
|
|
240
|
-
set strength(v: number) {
|
|
241
|
-
const s = Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0.35
|
|
242
|
-
hdrStrengthLocal = s
|
|
243
|
-
gl()?.setHdrStrength?.(s)
|
|
244
|
-
},
|
|
245
|
-
/** Where white lands, as a multiple of SDR white, 1..8 (default 1) — the "HDR brightness" of a
|
|
246
|
-
* console game's calibration screen, where paper white sits at ~200 nits against SDR's ~100.
|
|
247
|
-
* It scales the WHOLE picture: at 1 a white wall is as bright as the UI's white, at 2 it is
|
|
248
|
-
* twice that, and highlights still climb above it (the engine keeps `headroom / paperWhite` for
|
|
249
|
-
* them, clamped at the peak). `strength` decides how much of the picture reaches for the
|
|
250
|
-
* peak; this decides where the picture starts. 1.5–2 is the console norm; a game that wants to
|
|
251
|
-
* read as "HDR on" rather than "SDR with a brighter sun" wants this over `strength`. A host may
|
|
252
|
-
* pin it (`LECODES_HDR_PAPER_WHITE`). */
|
|
253
|
-
get paperWhite(): number {
|
|
254
|
-
return gl()?.getHdrPaperWhite?.() ?? hdrPaperWhiteLocal
|
|
255
|
-
},
|
|
256
|
-
set paperWhite(v: number) {
|
|
257
|
-
const p = Number.isFinite(v) ? Math.min(8, Math.max(1, v)) : 1
|
|
258
|
-
hdrPaperWhiteLocal = p
|
|
259
|
-
gl()?.setHdrPaperWhite?.(p)
|
|
260
|
-
},
|
|
261
232
|
},
|
|
262
233
|
/** Current connectivity — best-effort navigator.onLine semantics: `false` only when the platform
|
|
263
234
|
* is sure there's no network. `true` on hosts that don't track it. Change events: `"online"` /
|
package/src/version.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// same manifest). Its MAJOR is the bundle ↔ runtime contract: a bundle's `// sdk:` header line
|
|
5
5
|
// and a host's embedded version must agree on the major for the host to run the bundle; minor
|
|
6
6
|
// and patch never gate anything (features are detected, never versioned).
|
|
7
|
-
export const SDK_VERSION = "2.
|
|
7
|
+
export const SDK_VERSION = "2.3.1"
|
|
8
8
|
|
|
9
9
|
/** The major of a semver string, or null when it is not one. */
|
|
10
10
|
export const sdkMajor = (version: string | null | undefined): number | null => {
|