lecodes-sdk 2.0.12 → 2.1.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.
@@ -8,17 +8,8 @@ import { Camera } from "./Camera";
8
8
  import { SceneAudio } from "./audio/SceneAudio";
9
9
  import { type ControlsHandle, type ControlsOptions } from "./controls";
10
10
  import { Material } from "./Material";
11
+ import { PostProcessing, type PostProcessingOptions } from "./postProcessing";
11
12
  import { Node } from "./Node";
12
- export type AmbientOcclusionOptions = {
13
- /** Strength of the darkening (default 1). */
14
- intensity?: number;
15
- /** How far the occlusion reaches, in metres (default 0.3). */
16
- radius?: number;
17
- /** Falloff contrast; >1 tightens it into the crease (default 1). */
18
- power?: number;
19
- /** Sample count + filtering (default 'medium'). Not the buffer resolution — that stays half. */
20
- quality?: "low" | "medium" | "high" | "ultra";
21
- };
22
13
  /** `SceneOptions.taa` / `scene.setAntialias("taa", 4, taa)`: the temporal anti-aliasing knobs. */
23
14
  export type TaaOptions = {
24
15
  /** Render the 3D at this fraction (0.5–1) and TAA-upscale it to the viewport; 1 = none. */
@@ -74,14 +65,10 @@ export type SceneOptions = {
74
65
  * rather than inflating the lights: scaling lamps past what they physically emit gives bright
75
66
  * fixtures in a black room, because it changes the RATIO, not the level. */
76
67
  exposureCompensation?: number;
77
- /** Bloom post-processing. */
78
- bloom?: boolean;
79
- bloomIntensity?: number;
80
- /** Tone mapping operator. `'aces'` (default, filament's ACES legacy) desaturates bright colours
81
- * towards white — HDR fire reads pale; `'neutral'` (Khronos PBR Neutral) keeps hue and
82
- * saturation until very bright; `'linear'` clips each channel (what an engine without a
83
- * tonemapper shows — saturated, Unity-without-post-processing look); `'filmic'` (Uncharted). */
84
- toneMapping?: "aces" | "neutral" | "linear" | "filmic";
68
+ /** The look: tone mapping, colour grading, a `.cube` LUT, bloom, vignette, ambient occlusion,
69
+ * screen-space reflections, depth of field — as one object (`scene.postProcessing` after the
70
+ * fact; every field is live, postProcessing.ts says which are free to animate). */
71
+ postProcessing?: PostProcessingOptions;
85
72
  /** The sky. Three forms:
86
73
  * - a colour — a flat clear colour;
87
74
  * - `{ texture }` — a KTX1 **cubemap**, the sharp `<name>_skybox.ktx` that filament's `cmgen`
@@ -96,12 +83,6 @@ export type SceneOptions = {
96
83
  skybox?: ColorInput | "environment" | {
97
84
  texture: string;
98
85
  };
99
- /** Screen-space ambient occlusion — the contact darkening in creases and where props meet the
100
- * ground. Without it an IBL lights a crease exactly as brightly as an open face, so everything
101
- * reads as pasted onto the floor rather than standing on it. `true` takes defaults tuned for
102
- * human-scale props; `radius` is world-space metres and is the one knob that must follow the
103
- * scene's scale (~0.3 for objects on a table, ~0.6-1 for a yard of crates and containers). */
104
- ambientOcclusion?: boolean | AmbientOcclusionOptions;
105
86
  /** Distance fog / aerial perspective: distant geometry loses contrast so the eye reads depth, and
106
87
  * the hard edge where a finite level ends against the skybox goes away. By default the fog applies
107
88
  * at every distance — the skybox included — so the sky itself takes the fog colour and the horizon
@@ -167,6 +148,14 @@ export declare class Scene implements Presentable {
167
148
  readonly audio: SceneAudio;
168
149
  readonly _touchStartListeners: Array<(ev: TouchStartEvent<Node | null>) => void>;
169
150
  private _material?;
151
+ private readonly _postProcessing;
152
+ /** The scene's post-processing (postProcessing.ts): a live object — write a field
153
+ * (`scene.postProcessing.contrast = 1.1`), or `apply({ ... })` several at once (named fields
154
+ * only), `reset()` for the defaults. Read-only: there is nothing to assign. An overlay's grading
155
+ * is its parent's; its bloom, vignette and the rest are its own. */
156
+ get postProcessing(): PostProcessing;
157
+ /** An explicit throw, not a bare getter: a bundle evaluated in sloppy mode would swallow the assignment silently. */
158
+ set postProcessing(_: never);
170
159
  private static _active;
171
160
  static get active(): Scene | null;
172
161
  constructor(options?: SceneOptions);
@@ -181,8 +170,6 @@ export declare class Scene implements Presentable {
181
170
  set skybox(sky: ColorInput | "environment" | {
182
171
  texture: string;
183
172
  });
184
- /** Runtime form of `ambientOcclusion` (a graphics-settings menu). `false` turns it off. */
185
- setAmbientOcclusion(options: boolean | AmbientOcclusionOptions): void;
186
173
  /** Runtime form of `fog` (weather, entering a building). `false` turns it off. */
187
174
  setFog(options: FogOptions | false): void;
188
175
  setMaterialGlobalParameter(i: number, x: number, y: number, z: number, w: number): void;
@@ -196,8 +183,6 @@ export declare class Scene implements Presentable {
196
183
  * 0.5 = full detail twice as far, 1 = the defaults. Engine-wide, live. Only GLBs that carry
197
184
  * `_LOD<n>` meshes (`lecodes assets doctor --lod`) have levels to switch. */
198
185
  setLodBias(bias: number): void;
199
- /** Runtime form of `bloom` / `bloomIntensity` (a graphics-settings menu). */
200
- setBloom(enabled: boolean, intensity?: number): void;
201
186
  /**
202
187
  * How bright the environment (IBL) lights the scene, in lux — `SceneOptions.environmentIntensity`
203
188
  * after the fact. Live: it changes the probe's intensity, not the probe, so it costs nothing and
@@ -0,0 +1,228 @@
1
+ import { type ColorInput } from "../core/color";
2
+ import type { Texture } from "./Texture";
3
+ export type ToneMappingOperator = "aces" | "neutral" | "linear" | "filmic";
4
+ /** A linear RGB triple. */
5
+ export type Rgb = readonly [number, number, number];
6
+ export type Quality = "low" | "medium" | "high" | "ultra";
7
+ export type BloomOptions = {
8
+ /** How much of the blurred highlights is added back, 0..1 (default 0.2). */
9
+ intensity?: number;
10
+ /** Only what is brighter than 1.0 after exposure blooms (default true); false makes everything
11
+ * glow a little — the soft "dreamy" look. */
12
+ threshold?: boolean;
13
+ /** Clamp of one pixel's contribution (default 1000); lower it when a few emissive pixels flicker. */
14
+ highlight?: number;
15
+ /** Depth of the blur chain, 1..12 (default 6): how far the glow spreads. */
16
+ levels?: number;
17
+ /** The blur's sample count (default 'medium'). */
18
+ quality?: Quality;
19
+ /** Ghosts + halo around the brightest spots (default false). */
20
+ lensFlare?: boolean;
21
+ /** A "dirty lens": this texture multiplied into the bloom (default none). */
22
+ dirt?: Texture | null;
23
+ /** How much of the dirt shows, 0..1 (default 0.2). */
24
+ dirtStrength?: number;
25
+ };
26
+ export type VignetteOptions = {
27
+ /** How far from the centre the darkening starts, 0..1 (default 0.5). */
28
+ midPoint?: number;
29
+ /** 0 follows the aspect ratio, 1 is a circle (default 0.5). */
30
+ roundness?: number;
31
+ /** Softness of the edge, 0..1 (default 0.5). */
32
+ feather?: number;
33
+ /** The colour at the edge (default black). */
34
+ color?: ColorInput;
35
+ };
36
+ /** Screen-space ambient occlusion — the contact darkening in creases and where props meet the
37
+ * ground. Without it an IBL lights a crease exactly as brightly as an open face, so everything
38
+ * reads as pasted onto the floor rather than standing on it. `true` takes defaults tuned for
39
+ * human-scale props; `radius` is world-space metres and is the one knob that must follow the
40
+ * scene's scale (~0.3 for objects on a table, ~0.6-1 for a yard of crates and containers). */
41
+ export type AmbientOcclusionOptions = {
42
+ /** Strength of the darkening (default 1). */
43
+ intensity?: number;
44
+ /** How far the occlusion reaches, in metres (default 0.3). */
45
+ radius?: number;
46
+ /** Falloff contrast; >1 tightens it into the crease (default 1). */
47
+ power?: number;
48
+ /** Sample count + filtering (default 'medium'). Not the buffer resolution — that stays half. */
49
+ quality?: Quality;
50
+ };
51
+ /** Screen-space reflections: glossy surfaces reflect what is ON SCREEN, by a ray march through the
52
+ * depth buffer (a wet floor reflecting the lamps above it). Nothing off screen reflects and a
53
+ * reflection ends at the frame's edge — the classic limit. A per-pixel ray: a real cost on a
54
+ * phone, not measured yet. */
55
+ export type ScreenSpaceReflectionsOptions = {
56
+ /** Metres a ray travels before giving up (default 3). */
57
+ maxDistance?: number;
58
+ /** Metres a surface is assumed to extend behind its depth — a hit inside it counts (default 0.1). */
59
+ thickness?: number;
60
+ /** Pixels per ray step: 1 = exact and slow, 2..4 the usual (default 2). */
61
+ stride?: number;
62
+ };
63
+ /** Depth of field: the camera focuses at `focusDistance` and everything else blurs as the lens
64
+ * would — as if it were at f/`aperture`, WITHOUT touching the exposure (the camera's own aperture,
65
+ * f/16 by default, would show next to no blur; the engine scales the blur instead of opening the
66
+ * lens). The physics are honest and therefore mild: a game camera is a WIDE lens (fov 60° is a
67
+ * 21 mm lens), and a wide lens at f/1.4 still blurs a prop 9 m behind a 2.5 m focus by only ~2 px
68
+ * of 600 — `strength` multiplies the blur for the look a 50 mm lens would give (3-4 reads as
69
+ * "cinematic" at fov 60°). All live: a focus pull is a `focusDistance` written every frame. */
70
+ export type DepthOfFieldOptions = {
71
+ /** Metres from the camera to the plane in focus (default 10). */
72
+ focusDistance?: number;
73
+ /** The f-number the blur should look like (default 2.8; smaller = shallower). */
74
+ aperture?: number;
75
+ /** Multiplier on the blur (default 1 = the physics of the camera's lens). */
76
+ strength?: number;
77
+ };
78
+ export type WhiteBalance = {
79
+ /** Blue / yellow axis, -1 (cool, 50 000 K) .. 1 (warm, 2 000 K); 0 = none. */
80
+ temperature?: number;
81
+ /** Green / magenta axis, -1 .. 1; 0 = none. */
82
+ tint?: number;
83
+ };
84
+ /** Where the tonal zones of `shadows` / `midtones` / `highlights` fade into each other, in 0..1 of
85
+ * the (linear) luminance: `shadows` = (start, end) of the shadows → midtones fade (default 0, 0.333),
86
+ * `highlights` = (start, end) of the midtones → highlights fade (default 0.55, 1). */
87
+ export type TonalRanges = {
88
+ shadows?: readonly [number, number];
89
+ highlights?: readonly [number, number];
90
+ };
91
+ /** Per-channel curves: a gamma on the shadows, the point where shadows stop and highlights start,
92
+ * and a scale on the highlights — each per RGB channel, `[1, 1, 1]` = none. */
93
+ export type ColorCurves = {
94
+ shadowGamma?: Rgb;
95
+ midPoint?: Rgb;
96
+ highlightScale?: Rgb;
97
+ };
98
+ export type PostProcessingOptions = {
99
+ /** The operator that folds the scene's HDR light into the display's range (default 'aces').
100
+ * `'aces'` (filament's ACES legacy) desaturates bright colours towards white — HDR fire reads
101
+ * pale; `'neutral'` (Khronos PBR Neutral) keeps hue and saturation until very bright; `'linear'`
102
+ * clips each channel (what an engine without a tonemapper shows — saturated, Unity-without-post
103
+ * look); `'filmic'` is the Uncharted curve. */
104
+ toneMapping?: ToneMappingOperator;
105
+ /** Post-exposure in stops, applied AFTER bloom to the finished picture (default 0). Not the
106
+ * camera's `exposureCompensation`, which scales the light itself — what bloom and fog see. */
107
+ exposure?: number;
108
+ whiteBalance?: WhiteBalance;
109
+ /** 0..2, 1 = none. Applied in log space. */
110
+ contrast?: number;
111
+ /** 0..2, 1 = none. */
112
+ saturation?: number;
113
+ /** Saturation that spares what is already saturated, 0..2, 1 = none. */
114
+ vibrance?: number;
115
+ /** Linear RGB multiplier of the shadows zone, `[1, 1, 1]` = none. */
116
+ shadows?: Rgb;
117
+ midtones?: Rgb;
118
+ highlights?: Rgb;
119
+ ranges?: TonalRanges;
120
+ /** ASC CDL — the lift / gamma / gain of a grading suite, per channel, applied in log space:
121
+ * `slope` multiplies (gain; `[1, 1, 1]` = none), `offset` adds (lift; `[0, 0, 0]` = none),
122
+ * `power` is the exponent (gamma; `[1, 1, 1]` = none). */
123
+ slope?: Rgb;
124
+ offset?: Rgb;
125
+ power?: Rgb;
126
+ curves?: ColorCurves;
127
+ /** A 3D LUT from a grading tool (Resolve, Photoshop, Unity's Color Lookup) applied last, after
128
+ * the operator, in display-referred sRGB — a `.cube` file: `asset('./look.cube')`, or a bare
129
+ * staged filename. `null` removes it. */
130
+ lut?: string | null;
131
+ /** `true` = defaults (intensity 0.2), an object = its knobs, `false` = off. */
132
+ bloom?: boolean | BloomOptions;
133
+ /** `true` = defaults (a soft black edge), an object = its knobs, `false` = off. */
134
+ vignette?: boolean | VignetteOptions;
135
+ /** `true` = defaults (radius 0.3 m), an object = its knobs, `false` = off. */
136
+ ambientOcclusion?: boolean | AmbientOcclusionOptions;
137
+ /** `true` = defaults, an object = its knobs, `false` = off. */
138
+ screenSpaceReflections?: boolean | ScreenSpaceReflectionsOptions;
139
+ /** `true` = focus at 10 m as f/2.8, an object = its knobs, `false` = off. */
140
+ depthOfField?: boolean | DepthOfFieldOptions;
141
+ };
142
+ /** The float layout of `_creator.setColorGrading` — creator.h's ColorGradingParam. Indices never
143
+ * move; a new field goes at the end and COUNT grows. */
144
+ export declare const CG: {
145
+ readonly TONE_MAPPING: 0;
146
+ readonly EXPOSURE: 1;
147
+ readonly TEMPERATURE: 2;
148
+ readonly TINT: 3;
149
+ readonly CONTRAST: 4;
150
+ readonly VIBRANCE: 5;
151
+ readonly SATURATION: 6;
152
+ readonly SHADOWS: 7;
153
+ readonly MIDTONES: 10;
154
+ readonly HIGHLIGHTS: 13;
155
+ readonly RANGES: 16;
156
+ readonly SLOPE: 20;
157
+ readonly OFFSET: 23;
158
+ readonly POWER: 26;
159
+ readonly SHADOW_GAMMA: 29;
160
+ readonly MID_POINT: 32;
161
+ readonly HIGHLIGHT_SCALE: 35;
162
+ readonly LUT: 38;
163
+ readonly COUNT: 39;
164
+ };
165
+ /** The scene's post-processing (`scene.postProcessing`). Read a field for the current value, write
166
+ * one to change it; `apply(options)` writes the named ones; `reset()` is the defaults. */
167
+ export declare class PostProcessing {
168
+ private _g;
169
+ private _bloom;
170
+ private _vignette;
171
+ private _ao;
172
+ private _ssr;
173
+ private _dof;
174
+ private _dirty;
175
+ private _lutDirty;
176
+ private _sentLut;
177
+ private _sent;
178
+ private readonly _sceneId;
179
+ /** Write the fields named in `options`, nothing else — at every level: `{ bloom: { intensity:
180
+ * 0.4 } }` changes one bloom knob, `{ whiteBalance: { tint: 0.1 } }` keeps the temperature.
181
+ * The grading band goes to the engine at once (one bake), not in a microtask. */
182
+ apply(options: PostProcessingOptions): this;
183
+ /** Every field back to its default: the engine's own look, every live feature off. */
184
+ reset(): this;
185
+ get toneMapping(): ToneMappingOperator;
186
+ set toneMapping(v: ToneMappingOperator);
187
+ get exposure(): number;
188
+ set exposure(v: number);
189
+ get whiteBalance(): Required<WhiteBalance>;
190
+ set whiteBalance(v: WhiteBalance);
191
+ get contrast(): number;
192
+ set contrast(v: number);
193
+ get saturation(): number;
194
+ set saturation(v: number);
195
+ get vibrance(): number;
196
+ set vibrance(v: number);
197
+ get shadows(): Rgb;
198
+ set shadows(v: Rgb);
199
+ get midtones(): Rgb;
200
+ set midtones(v: Rgb);
201
+ get highlights(): Rgb;
202
+ set highlights(v: Rgb);
203
+ get ranges(): Required<TonalRanges>;
204
+ set ranges(v: TonalRanges);
205
+ get slope(): Rgb;
206
+ set slope(v: Rgb);
207
+ get offset(): Rgb;
208
+ set offset(v: Rgb);
209
+ get power(): Rgb;
210
+ set power(v: Rgb);
211
+ get curves(): Required<ColorCurves>;
212
+ set curves(v: ColorCurves);
213
+ get lut(): string | null;
214
+ set lut(v: string | null);
215
+ get bloom(): Required<BloomOptions> | false;
216
+ set bloom(v: boolean | BloomOptions);
217
+ get vignette(): Required<VignetteOptions> | false;
218
+ set vignette(v: boolean | VignetteOptions);
219
+ get ambientOcclusion(): Required<AmbientOcclusionOptions> | false;
220
+ set ambientOcclusion(v: boolean | AmbientOcclusionOptions);
221
+ get screenSpaceReflections(): Required<ScreenSpaceReflectionsOptions> | false;
222
+ set screenSpaceReflections(v: boolean | ScreenSpaceReflectionsOptions);
223
+ get depthOfField(): Required<DepthOfFieldOptions> | false;
224
+ set depthOfField(v: boolean | DepthOfFieldOptions);
225
+ private _touch;
226
+ /** Send the grading band now (it is sent on its own at the end of the current handler). */
227
+ flush(): void;
228
+ }
@@ -16,11 +16,40 @@ type AppEventMap = {
16
16
  * it to `animateTo` to move a composer in sync with the keyboard. */
17
17
  keyboard: (height: number, duration: number) => void;
18
18
  };
19
+ /** The installed app this bundle runs in — see `app.host`. */
20
+ export interface AppHost {
21
+ /** The store build's version string (Android versionName, iOS CFBundleShortVersionString; app.json
22
+ * `version` in a `lecodes app` shell). `undefined` where the host is not an installed app. */
23
+ version?: string;
24
+ /** The store build's build number (versionCode / CFBundleVersion; app.json `buildNumber` /
25
+ * `versionCode`). `undefined` where the host is not an installed app, `0` when not numeric. */
26
+ build?: number;
27
+ /** The SDK embedded in the host — the lecodes-ios-sdk / lecodes-android-sdk release a shell pins,
28
+ * the runtime's version elsewhere. */
29
+ sdk: string;
30
+ }
31
+ /** The bundle that is running — see `app.bundle`. */
32
+ export interface AppBundle {
33
+ /** The SDK this bundle was compiled with (`app.sdkVersion`). */
34
+ sdk: string;
35
+ /** When it was compiled (ISO 8601) — the `// last-updated:` header line; `undefined` on a bundle
36
+ * without it. */
37
+ updatedAt?: string;
38
+ }
19
39
  export declare const app: {
20
40
  /** The SDK this app was compiled with (semver, e.g. `"2.0.0"`). Its major is the bundle ↔ runtime
21
41
  * contract: a host runs only bundles of its own major, and the launchers send it as `?sdk=` when
22
- * they fetch a published bundle — the platform keeps one bundle per major. */
42
+ * they fetch a published bundle — the platform keeps one bundle per major. Also `app.bundle.sdk`. */
23
43
  sdkVersion: string;
44
+ /** The INSTALLED app: the store build this bundle runs in. After an over-the-air update the code
45
+ * is newer than the binary, so a feature that needs native support added in a later store build
46
+ * (a plugin, an SDK method) branches on `app.host.sdk` / `app.host.build` — and on `typeof` of
47
+ * the method itself, which is the final word. Minor and patch of the SDK never gate anything. */
48
+ readonly host: AppHost;
49
+ /** The RUNNING code: what the compiler stamped into this bundle's header. `updatedAt` is the
50
+ * compile time — what the updater compares before replacing a bundle (an older one never
51
+ * replaces a newer one), useful for an "about" screen or a support log. */
52
+ readonly bundle: AppBundle;
24
53
  /** Current lifecycle state. `"background"` while the app is not the foreground app / the tab is
25
54
  * hidden. `"active"` on hosts that don't track it. */
26
55
  readonly state: AppState;
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "2.0.12";
1
+ export declare const SDK_VERSION = "2.1.0";
2
2
  /** The major of a semver string, or null when it is not one. */
3
3
  export declare const sdkMajor: (version: string | null | undefined) => number | null;
4
4
  /** The `// sdk: <version>` line of a compiled bundle's header (sdk/src/compile/header.ts), read from