lecodes-sdk 2.3.1 → 2.4.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/global.d.ts CHANGED
@@ -105,6 +105,7 @@ declare global {
105
105
  const Replicated: typeof SDK.Replicated
106
106
  const Router: typeof SDK.Router
107
107
  const RpcError: typeof SDK.RpcError
108
+ const Scatter: typeof SDK.Scatter
108
109
  const Scene: typeof SDK.Scene
109
110
  const Scene2D: typeof SDK.Scene2D
110
111
  const Scene2dHandle: typeof SDK.Scene2dHandle
@@ -527,6 +528,9 @@ declare global {
527
528
  type RibbonOptions = SDK.RibbonOptions
528
529
  type Rolloff = SDK.Rolloff
529
530
  type RpcError = SDK.RpcError
531
+ type Scatter = SDK.Scatter
532
+ type ScatterOptions = SDK.ScatterOptions
533
+ type ScatterPlacement = SDK.ScatterPlacement
530
534
  type Scene = SDK.Scene
531
535
  type Scene2D = SDK.Scene2D
532
536
  type Scene2dDef = SDK.Scene2dDef
@@ -48,6 +48,11 @@ export type FoliageOptions = {
48
48
  /** Coverage held over distance: the cards' alpha × (1 + coverage × mip level), so a far crown does not thin out as its
49
49
  * atlas mips away. Default 0.25, 0 = none; past ~0.5 a crown turns into a solid mass. */
50
50
  coverage?: number;
51
+ /** THE PLANTS' CASTER DISTANCE, metres: a plant casts the sun's shadow only this close to the camera (the LOD pass takes
52
+ * the farther ones out of the shadow map; 3 m of hysteresis). Meant for a level with a BAKED ground, which keeps the
53
+ * far shadows while the shadow map holds a fraction of the casters (the Forest's Radeon frame: 6 ms of 39 at 25 m).
54
+ * Default 0 = every plant casts. */
55
+ castDistance?: number;
51
56
  };
52
57
  export declare const Foliage: {
53
58
  /** Level-wide options; each field given replaces the current value (a scene file's `env.foliage`
@@ -77,9 +77,11 @@ export declare class Lightmap {
77
77
  private static warned;
78
78
  /** The bake bound last (`load`): what `probe` reads rects from. */
79
79
  private static bound;
80
- /** Register static geometry — a Model, a Mesh, or any node whose subtree holds them. Statics are
81
- * both receivers and occluders in the bake. `key` names the entry in the .bake (default:
82
- * the node's name + a running number, `container#3`); pass one when names are not stable. */
80
+ /** Register static geometry — a Model, a Mesh, a Terrain's node, or any node whose subtree holds them. Statics are
81
+ * both receivers and occluders in the bake (a model without lightmap UVs, or with materials the lightmap shader
82
+ * declines — alpha cards — is an occluder alone; a terrain is ONE rect, built with `Material.terrainLightmap()`).
83
+ * `key` names the entry in the .bake (default: the node's name + a running number, `container#3`); pass one when
84
+ * names are not stable. */
83
85
  static add(node: Node, key?: string): Node;
84
86
  private static next;
85
87
  private static movers;
@@ -199,6 +199,12 @@ export declare class Material {
199
199
  private static _glbDefaults;
200
200
  private static _lightmapDefaults;
201
201
  private static _folTemplate;
202
+ /** Which material a foliage model's CARDS (its alpha-MASK materials) take: `"full"` = `foliage-masked`, the
203
+ * shader with the card's own plane, normal map, edge fade and the ground's baked light; `"lean"` =
204
+ * `foliage-cards`, the same wind with the lighting moved to the vertex (one texture read, the sun's shadow
205
+ * kept) — a third of a forest frame less on a phone-class GPU (2026-10-18). Read when the first foliage
206
+ * model loads; set it before. */
207
+ static foliageCards: "full" | "lean";
202
208
  static _foliageMaskedTemplate(): Material | null;
203
209
  private static _folMaskedTemplate;
204
210
  private static _glbTemplate;
@@ -0,0 +1,25 @@
1
+ import { Node } from "./Node";
2
+ import type { Model } from "./Model";
3
+ import { type Vec3Like } from "../math/vec";
4
+ /** One placement: where the kind's origin stands, its yaw about +Y in degrees, its uniform scale. */
5
+ export type ScatterPlacement = {
6
+ position: Vec3Like;
7
+ /** Degrees, default 0. */
8
+ yaw?: number;
9
+ /** Uniform, default 1. */
10
+ scale?: number;
11
+ };
12
+ export type ScatterOptions = {
13
+ /** The cell edge in metres the placements are bucketed by (one draw per 64 placements of a cell). Default 16. */
14
+ cell?: number;
15
+ /** Whether the copies cast the sun's shadow. Default false: ground cover never does. */
16
+ castShadows?: boolean;
17
+ name?: string;
18
+ };
19
+ export declare class Scatter extends Node {
20
+ /** Placements given. */
21
+ readonly count: number;
22
+ /** Renderables the engine made (0 on a host that cloned instead). */
23
+ readonly draws: number;
24
+ constructor(kind: Model, placements: readonly ScatterPlacement[], options?: ScatterOptions);
25
+ }
@@ -19,6 +19,9 @@ export type TerrainLayer = {
19
19
  /** Sample this layer triplanar in world space (cliff rock): no stretching on steep slopes; its
20
20
  * normal map is then skipped. Default false. */
21
21
  triplanar?: boolean;
22
+ /** The layer's MEAN COLOUR, for the plants: a grass blade's base takes the colour of the soil it stands in (the
23
+ * vegetation tier's cards read the control map and these four colours). Unset on every layer = no tint. */
24
+ color?: ColorInput;
22
25
  };
23
26
  export type TerrainOptions = {
24
27
  /** Samples per side (square). 257 / 513 / 1025 are the usual sizes. Default 129. */
@@ -274,6 +277,8 @@ export declare class Terrain {
274
277
  private _rebuildFallbackChunks;
275
278
  private _applyShadows;
276
279
  private _uploadControl;
280
+ /** The layers' colours to the vegetation tier (TerrainLayer.color), once the control map exists. */
281
+ private _pushGroundTint;
277
282
  private _applyLayers;
278
283
  /** Re-push layer scalars after editing `layers` (textures given as Texture apply too). */
279
284
  applyLayers(): Promise<void>;
@@ -143,6 +143,8 @@ export type { LocomotionSet, LocomotionClip, LocomotionTuning, LocomotionState,
143
143
  export { Material } from "./gl/Material";
144
144
  export { Geometry } from "./gl/Geometry";
145
145
  export { InstancedMesh } from "./gl/InstancedMesh";
146
+ export { Scatter } from "./gl/Scatter";
147
+ export type { ScatterPlacement, ScatterOptions } from "./gl/Scatter";
146
148
  export type { InstanceTransform } from "./gl/InstancedMesh";
147
149
  export { Texture } from "./gl/Texture";
148
150
  export type { TextureLoadOptions } from "./gl/Texture";
@@ -63,6 +63,26 @@ export declare const device: {
63
63
  * what it measures. It belongs to the host, not to the project: it stays on when another project
64
64
  * is opened. Reads `false`, and writing does nothing, on hosts without one (web, headless). */
65
65
  statsOverlay: boolean;
66
+ /** Cap the frame rate: the host runs the frame — the tick, the 3D scene — at most `fps` times a
67
+ * second. `0` removes the cap: the display's own rate (120 on a 120 Hz phone), or, with vsync
68
+ * off, as fast as the host renders. A REQUEST, not a state the app owns: a phone asks its panel
69
+ * for the rate (a 120 Hz panel at 60 shows every frame for a full period — even, cooler) and an
70
+ * OEM may refuse the switch, in which case the host renders every second refresh instead; the
71
+ * screen's own maximum always wins. The tick IS the frame here, so a cap of 30 is 30 Hz of game
72
+ * logic and input sampling too — raise it for gameplay. Host state: it stays through a project
73
+ * swap. A no-op on hosts whose loop has no pacing of its own (web, headless). */
74
+ setFrameRate(fps: number): void;
75
+ /** Vertical sync: `false` presents without waiting for the display's refresh — the lowest input
76
+ * latency, tearing possible, and with no cap set (`setFrameRate(0)`) the loop renders as fast as
77
+ * it can. Set a cap unless you are measuring. Desktop only: a mobile compositor never presents
78
+ * outside the refresh. Returns whether the host took the request (`false` on a host without
79
+ * the switch), so a settings menu can hide the toggle. */
80
+ setVsync(enabled: boolean): boolean;
81
+ /** The display's refresh rate in Hz: the fastest the screen offers the app — 120 on a 120 Hz
82
+ * phone whatever mode a cap has put it in (an iPhone's 60 unless the app's Info.plist lifts the
83
+ * floor), the monitor's current mode on desktop. The sensible choices of a frame-rate menu are
84
+ * its divisors. 0 where the host cannot tell (web). */
85
+ readonly displayRefreshRate: number;
66
86
  /** Device-orientation sensor (gyro + accelerometer, fused) for tilt/steering and magic-window /
67
87
  * 360° panoramas. Poll `attitude` / `gravity` inside setLoop; they return the freshest fused
68
88
  * sample, so the sensor rate need not match your frame rate. Host-gated: a no-op with no sensor. */
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "2.3.1";
1
+ export declare const SDK_VERSION = "2.4.1";
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