lecodes-sdk 0.20.0 → 0.20.2

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.
Files changed (64) hide show
  1. package/dist/global.d.ts +31 -0
  2. package/dist/inject.js +361 -260
  3. package/dist/types/audio/Bus.d.ts +45 -0
  4. package/dist/types/audio/Sound.d.ts +28 -0
  5. package/dist/types/audio/Voice.d.ts +27 -0
  6. package/dist/types/audio/audio.d.ts +83 -0
  7. package/dist/types/audio/support.d.ts +1 -0
  8. package/dist/types/gl/AudioSource.d.ts +60 -0
  9. package/dist/types/gl/AudioZone.d.ts +32 -0
  10. package/dist/types/gl/DecalSet.d.ts +103 -0
  11. package/dist/types/gl/Geometry.d.ts +5 -0
  12. package/dist/types/gl/Light.d.ts +7 -0
  13. package/dist/types/gl/Material.d.ts +86 -2
  14. package/dist/types/gl/Mesh.d.ts +11 -0
  15. package/dist/types/gl/Scene.d.ts +23 -0
  16. package/dist/types/gl/SceneAudio.d.ts +11 -0
  17. package/dist/types/gl/Texture.d.ts +29 -1
  18. package/dist/types/gl/animation/AnimationClip.d.ts +25 -12
  19. package/dist/types/gl/animation/core.d.ts +15 -10
  20. package/dist/types/gl/state.d.ts +0 -1
  21. package/dist/types/inject.d.ts +10 -0
  22. package/dist/types/runtime/input.d.ts +11 -0
  23. package/dist/types/ui/UIImage.d.ts +15 -5
  24. package/dist/types.json +1 -1
  25. package/package.json +1 -1
  26. package/prompts/dist/2d-game.md +408 -197
  27. package/prompts/dist/3d-app.md +491 -166
  28. package/prompts/dist/ar-app.md +373 -163
  29. package/prompts/dist/design.md +83 -87
  30. package/prompts/dist/ui-app.md +325 -136
  31. package/src/audio/Bus.ts +102 -0
  32. package/src/audio/Sound.ts +96 -0
  33. package/src/audio/Voice.ts +102 -0
  34. package/src/audio/audio.ts +161 -0
  35. package/src/audio/support.ts +6 -0
  36. package/src/bridges.d.ts +1481 -1352
  37. package/src/compile/compileProject.ts +30 -15
  38. package/src/compile/index.ts +3 -0
  39. package/src/core/Aspect.ts +33 -8
  40. package/src/g2/Scene2D.ts +7 -0
  41. package/src/gl/AudioSource.ts +113 -0
  42. package/src/gl/AudioZone.ts +75 -0
  43. package/src/gl/CameraPlace.ts +52 -52
  44. package/src/gl/DecalSet.ts +233 -0
  45. package/src/gl/Geometry.ts +5 -0
  46. package/src/gl/Light.ts +16 -0
  47. package/src/gl/Lightmap.ts +3 -2
  48. package/src/gl/Material.ts +152 -4
  49. package/src/gl/Mesh.ts +20 -1
  50. package/src/gl/Ragdoll.ts +270 -270
  51. package/src/gl/Scene.ts +41 -7
  52. package/src/gl/SceneAudio.ts +26 -0
  53. package/src/gl/Texture.ts +43 -3
  54. package/src/gl/Trigger.ts +45 -45
  55. package/src/gl/Vehicle.ts +5 -5
  56. package/src/gl/animation/AnimationClip.ts +43 -20
  57. package/src/gl/animation/Animator.ts +4 -3
  58. package/src/gl/animation/core.ts +20 -15
  59. package/src/gl/scenarios.ts +291 -291
  60. package/src/gl/state.ts +1 -1
  61. package/src/inject.ts +12 -0
  62. package/src/runtime/input.ts +6 -1
  63. package/src/scene/gizmos.ts +148 -148
  64. package/src/ui/UIImage.ts +21 -7
@@ -1,5 +1,28 @@
1
1
  import { type FetchResponse, type File } from "../runtime/fetch";
2
+ /** `Texture.load` options. */
3
+ export type TextureLoadOptions = {
4
+ /** `true` (default): colour, stored sRGB. `false`: data (normal map, mask, heightmap) — kept linear. */
5
+ srgb?: boolean;
6
+ /** Ignore `Texture.maxSize` for this texture (a lightmap page, a lookup table). */
7
+ fullSize?: boolean;
8
+ };
2
9
  export declare class Texture {
10
+ static _maxSize: number;
11
+ /** Engine-wide cap on texture size (a "texture quality" setting), 0 = none. A KTX2 wider or
12
+ * taller than this loses its top mip levels on load (nothing resampled, less memory and
13
+ * bandwidth), a glTF PNG/JPEG is downsampled. Reaches textures loaded AFTER it is set — a loaded
14
+ * level keeps its textures — so set it up front (`SceneOptions.maxTextureSize`, or before the
15
+ * level loads) and apply a menu change on the next level load. Lightmap pages are exempt
16
+ * (`TextureLoadOptions.fullSize`). A host may pin it (desktop `CREATOR_TEXTURE_MAX_SIZE`). */
17
+ static _anisotropy: number;
18
+ /** Engine-wide anisotropic filtering, 1 (off) … 16 (default 2; `SceneOptions.anisotropy` sets it up
19
+ * front). A sampler is baked when its texture is bound, so like `maxSize` this reaches textures
20
+ * loaded AFTER it — set it before the level loads. Measured on a lightmapped interior at 720p:
21
+ * 4× costs ~20 % of the frame over 1× on an integrated GPU. A host may pin it. */
22
+ static get anisotropy(): number;
23
+ static set anisotropy(level: number);
24
+ static get maxSize(): number;
25
+ static set maxSize(size: number);
3
26
  readonly width: number;
4
27
  readonly height: number;
5
28
  /** Horizontal wrap mode (U). */
@@ -23,5 +46,10 @@ export declare class Texture {
23
46
  }): Texture;
24
47
  /** Re-upload a rectangle of a `fromPixels` texture (same channel count). */
25
48
  update(x: number, y: number, width: number, height: number, data: Uint8Array): void;
26
- static load(source: string | FetchResponse | File): Promise<Texture>;
49
+ /** Decode an image (PNG / JPG, or a KTX2 the core transcodes) into a texture. `srgb` (default
50
+ * true) says the bytes are COLOUR, stored sRGB so the GPU linearises them on sample; pass
51
+ * `false` for DATA — a normal map, a mask, a heightmap — which must come back as stored (a
52
+ * flat normal read through sRGB bends by ~35°). A KTX2 decides by its own header. Image
53
+ * textures get a mip chain (trilinear) on hosts that build one. */
54
+ static load(source: string | FetchResponse | File, options?: TextureLoadOptions): Promise<Texture>;
27
55
  }
@@ -22,6 +22,19 @@ export type ClipInfo = {
22
22
  duration: number;
23
23
  trackCount: number;
24
24
  };
25
+ /** What `AnimationClip.from(clip, …)` derives from a clip. */
26
+ export type DeriveOptions = {
27
+ /** The clip MIRRORED — left ↔ right, the motion on the other side of the body (a stop that brakes on the
28
+ * left foot brakes on the right). Derived on the rig of the clip's own file (joints pair by name, the
29
+ * sagittal plane comes from the rest pose), so the result is the same on every model: its contacts,
30
+ * phase, root motion and heading are the mirrored ones, its name is `<name>_M`. Only clips from a GLB
31
+ * carry a rig; a curve-built clip cannot be mirrored. */
32
+ mirror?: boolean;
33
+ /** Start of the window, seconds of the source (default 0). */
34
+ from?: number;
35
+ /** End of the window, seconds of the source (default the clip's end). */
36
+ to?: number;
37
+ };
25
38
  export declare class AnimationClip {
26
39
  /** Source name (the clip's name inside its file; `"clip"` for procedural clips). Informational —
27
40
  * an Animator addresses clips by the key YOU give it. */
@@ -33,8 +46,8 @@ export declare class AnimationClip {
33
46
  private constructor();
34
47
  /** Mark a moment of the clip (SECONDS from its start) with an event name: `kick.addEvent(0.4, 'hit')`
35
48
  * → `anim.on('hit', (clip, layer) => …)` fires when the playhead crosses it, loops included.
36
- * Events are part of the clip: every model playing it gets them; `slice()` keeps the ones inside
37
- * the range, re-timed. Chainable. */
49
+ * Events are part of the clip: every model playing it gets them; `AnimationClip.from(clip, { from, to })`
50
+ * keeps the ones inside the window, re-timed. Chainable. */
38
51
  addEvent(time: number, name: string): this;
39
52
  private _pushEvents;
40
53
  /** Load ONE clip from a GLB: the file's only/first clip, or the one named / at the given index. */
@@ -43,18 +56,18 @@ export declare class AnimationClip {
43
56
  static loadAll(source: string | FetchResponse): Promise<Record<string, AnimationClip>>;
44
57
  /** Build a clip from curves in code — no DCC needed. Keys are `[time, value]`; a track binds to the
45
58
  * node of that name when the clip is used by an Animator (bones, or any child node). */
46
- static from(def: ClipDef): AnimationClip;
47
- /** A sub-range of this clip as a NEW clip — `[start, end]` seconds of the source, re-timed to 0
48
- * (`end` defaults to the clip's end; array-slice semantics). Cut a too-long take down to the
49
- * action, or carve several sub-clips out of one packed timeline:
59
+ static fromCurves(def: ClipDef): AnimationClip;
60
+ /** A NEW clip derived from `clip`: its mirror (`{ mirror: true }` — the other side of the body), a window
61
+ * of it (`{ from, to }` seconds of the source, re-timed to 0; array-slice semantics), or both. Cut a
62
+ * too-long take down to the action, carve several sub-clips out of one packed timeline, get the
63
+ * left-footed stop from the right-footed one:
50
64
  *
51
- * clips: { Kick: kick.slice(0.2, 1.1) }
65
+ * clips: { Kick: AnimationClip.from(kick, { from: 0.2, to: 1.1 }), StopM: AnimationClip.from(stop, { mirror: true }) }
52
66
  *
53
- * Keys outside the range are dropped and exact boundary values are interpolated in, so the
54
- * sliced clip starts and ends precisely on the source's pose at the cut points. Everything
55
- * downstream (blend spaces, events' 0–1 times, root motion, phase sync) sees a normal clip of
56
- * the new duration. */
57
- slice(start: number, end?: number): AnimationClip;
67
+ * A window drops the keys outside it and interpolates exact boundary values in, so the clip starts and
68
+ * ends precisely on the source's pose at the cut points. Everything downstream (blend spaces, events'
69
+ * times, root motion, foot contacts, phase sync) sees a normal clip. */
70
+ static from(clip: AnimationClip, options: DeriveOptions): AnimationClip;
58
71
  private static _label;
59
72
  private static _loadSet;
60
73
  }
@@ -6,18 +6,17 @@ import { Vec3 } from "../../math/vec";
6
6
  import type { Node } from "../Node";
7
7
  /** A position on a blend axis (1D) or plane (2D). */
8
8
  export type BlendPosition = number | readonly [number, number];
9
- /** The transition time (seconds) a play / playLoop without `fade` uses. */
10
- export declare const DEFAULT_FADE = 0.2;
11
9
  export type PlayOptions = {
12
- /** Transition seconds — how long the difference to the pose the layer SHOWED takes to decay
13
- * (default 0.2; `0` = cut). Also the transition back at the end unless `fadeOut` overrides it. */
10
+ /** Transition seconds — how long the difference to the pose the layer SHOWED (its loop, a one-shot,
11
+ * or rest) takes to decay. Default `0` = cut: a fade is always asked for, never implied. Also the
12
+ * transition back to the loop at the end unless `fadeOut` overrides it. */
14
13
  fade?: number;
15
14
  /** Transition seconds for the way in only (overrides `fade`). */
16
15
  fadeIn?: number;
17
- /** One-shots: the transition BACK at the end (overrides `fade`). It starts this long before the
18
- * clip ends — while it still plays — so the hand-over lands on the last pose, not after it. On a
19
- * layer with no loop to return to, giving `fadeOut` releases the layer at the end instead of
20
- * holding the last frame. */
16
+ /** One-shots: the transition BACK to the loop at the end (overrides `fade`; `0` = cut at the end).
17
+ * It starts this long before the clip ends — while it still plays — so the hand-over lands on the
18
+ * last pose, not after it. On a layer with no loop the clip HOLDS its last frame (a rock that broke
19
+ * stays broken) — `anim.stop({ fade })` is the way back to rest. */
21
20
  fadeOut?: number;
22
21
  /** Playback rate for this clip (default 1). */
23
22
  speed?: number;
@@ -39,8 +38,8 @@ export type StopOptions = {
39
38
  fade?: number;
40
39
  };
41
40
  export type LoopOptions = {
42
- /** Transition seconds from whatever the layer shows — its previous loop, or a one-shot still
43
- * playing (default 0.2; `0` = cut). */
41
+ /** Transition seconds from whatever the layer shows — its previous loop, a one-shot still playing,
42
+ * or rest. Default `0` = cut: a loop wanting a transition asks for one (`{ fade: 0.2 }`). */
44
43
  fade?: number;
45
44
  /** Playback rate of the loop's clips (default 1). */
46
45
  speed?: number;
@@ -165,6 +164,12 @@ export type ClipInfo = {
165
164
  turnAt(time: number): number;
166
165
  /** the root's own speed at `time`, m/s — how fast the clip is moving the body right there */
167
166
  speedAt(time: number): number;
167
+ /** the PELVIS' yaw relative to the clip's heading at `time` (rad, + = left, wrapped to ±π): the stance twist
168
+ * the heading leaves out — an idle stands ~43° off the course it starts along, a stop's tail turns into that
169
+ * stance while the heading holds. A controller that switches clips mid-pose can keep the PELVIS continuous
170
+ * (turn the node by the difference between the two clips' values) where keeping the course would swing
171
+ * the body. Without a baked heading: the pelvis' yaw at frame 0 throughout. */
172
+ pelvisYawAt(time: number): number;
168
173
  /** the unit travel direction at `time` ([x, z], model space, held through stills) — integrate
169
174
  * direction × d(travel) to reconstruct the root's 2D path (a treadmill display, a turn's arc) */
170
175
  directionAt(time: number): [number, number];
@@ -1,5 +1,4 @@
1
1
  import type { Scene } from "./Scene";
2
2
  export declare const glState: {
3
3
  activeScene: Scene | null;
4
- frame: number;
5
4
  };
@@ -23,6 +23,10 @@ export { WebSocket } from "./runtime/net";
23
23
  export { Net, NetPlayer, Replicated, NetEntity, type NetMessage, type NetRole, type NetStatus, type NetEvents, type NetLaunch, type NetInput, type NetKind, type NetTransform } from "./net";
24
24
  export { __rpc, __channel, __serverOnly, RpcError, type ClientChannel } from "./runtime/rpc";
25
25
  export { AudioPlayer, VideoPlayer } from "./runtime/media";
26
+ export { Sound, type SoundOptions } from "./audio/Sound";
27
+ export { Voice, type VoiceEvents } from "./audio/Voice";
28
+ export { Bus, REVERB_PRESETS, type ReverbPreset, type ReverbParams, type EchoParams } from "./audio/Bus";
29
+ export { audio, type PlaySoundOptions, type SpatialOptions, type Rolloff, type AudioStats } from "./audio/audio";
26
30
  export { openFilePicker, files, type FileData } from "./runtime/files";
27
31
  export { share } from "./runtime/share";
28
32
  export { ClickEvent, TouchStartEvent, LongPressEvent } from "./runtime/touch";
@@ -84,6 +88,9 @@ export { Model } from "./gl/Model";
84
88
  export { Shape } from "./gl/Shape";
85
89
  export { Physics } from "./gl/Physics";
86
90
  export { Trigger } from "./gl/Trigger";
91
+ export { AudioSource, type AudioCone } from "./gl/AudioSource";
92
+ export { AudioZone } from "./gl/AudioZone";
93
+ export { SceneAudio } from "./gl/SceneAudio";
87
94
  export { CharacterController } from "./gl/CharacterController";
88
95
  export { Vehicle } from "./gl/Vehicle";
89
96
  export type { DriveLayout, DifferentialMode, EngineConfig, SteeringConfig, AeroConfig } from "./gl/Vehicle";
@@ -121,8 +128,11 @@ export { Geometry } from "./gl/Geometry";
121
128
  export { InstancedMesh } from "./gl/InstancedMesh";
122
129
  export type { InstanceTransform } from "./gl/InstancedMesh";
123
130
  export { Texture } from "./gl/Texture";
131
+ export type { TextureLoadOptions } from "./gl/Texture";
124
132
  export { Camera } from "./gl/Camera";
125
133
  export { Ray } from "./gl/Ray";
126
134
  export { Plane } from "./gl/Plane";
127
135
  export { Noise } from "./gl/Noise";
128
136
  export { Particles, Trail, curve, colorCurve } from "./gl/Particles";
137
+ export { DecalSet } from "./gl/DecalSet";
138
+ export type { DecalOptions, DecalSetOptions, DecalSpawnOptions, DecalPlacement } from "./gl/DecalSet";
@@ -8,6 +8,7 @@ export declare const InputChannel: {
8
8
  readonly WheelY: 5;
9
9
  readonly PointerLocked: 6;
10
10
  readonly GamepadCount: 7;
11
+ readonly PointerOnUI: 8;
11
12
  readonly GamepadBase: 16;
12
13
  readonly GamepadStride: 16;
13
14
  readonly PadConnected: 0;
@@ -64,6 +65,11 @@ export declare const mouse: {
64
65
  * gesture (call it in a 'keydown' handler for MouseLeft); hosts drop it on focus loss and
65
66
  * re-acquire on focus. Escape does NOT unlock by itself on native hosts — call `unlock()`. */
66
67
  readonly locked: boolean;
68
+ /** The primary button went down on a UI element (a button, a scrollable, an input, a modal
69
+ * backdrop) and is still held. Stays true wherever the cursor drifts until the release, so
70
+ * `if (!Input.mouse.onUI) look(Input.mouse.delta)` never turns a HUD drag into camera look.
71
+ * Always false while `locked`. A press that fell through to the scene reads false. */
72
+ readonly onUI: boolean;
67
73
  lock(): boolean;
68
74
  unlock(): void;
69
75
  };
@@ -108,6 +114,11 @@ export declare const Input: {
108
114
  * gesture (call it in a 'keydown' handler for MouseLeft); hosts drop it on focus loss and
109
115
  * re-acquire on focus. Escape does NOT unlock by itself on native hosts — call `unlock()`. */
110
116
  readonly locked: boolean;
117
+ /** The primary button went down on a UI element (a button, a scrollable, an input, a modal
118
+ * backdrop) and is still held. Stays true wherever the cursor drifts until the release, so
119
+ * `if (!Input.mouse.onUI) look(Input.mouse.delta)` never turns a HUD drag into camera look.
120
+ * Always false while `locked`. A press that fell through to the scene reads false. */
121
+ readonly onUI: boolean;
111
122
  lock(): boolean;
112
123
  unlock(): void;
113
124
  };
@@ -2,6 +2,7 @@ import type { FetchResponse } from "../runtime/fetch";
2
2
  import type { SvgSourceValue as SvgSource } from "../runtime/misc";
3
3
  import type { File } from "../runtime/fetch";
4
4
  import { Canvas } from "../canvas/Canvas";
5
+ import type { Scene2D } from "../g2/Scene2D";
5
6
  import { type Color, type ElementStyle, type UIElementBase } from "./UINode";
6
7
  export type UIImageStyle = ElementStyle & {
7
8
  borderRadius?: number;
@@ -13,12 +14,21 @@ export type UIImageStyle = ElementStyle & {
13
14
  export type CanvasImageSource = {
14
15
  canvasSurface: number;
15
16
  };
16
- /** Accepted image sources. A Canvas is baked to a surface marker on assign. */
17
- export type ImageSource = string | FetchResponse | File | SvgSource | Canvas;
18
- /** The stored/resolved image source (a Canvas is resolved to a surface marker). */
19
- export type ResolvedImageSource = string | FetchResponse | File | SvgSource | CanvasImageSource;
17
+ /** A live 2D scene as an image source: the host draws the scene into the node's box every frame
18
+ * (the scene's camera, projected into the box). No intrinsic size — give the node a width and
19
+ * height. */
20
+ export type Scene2DImageSource = {
21
+ scene2d: number;
22
+ };
23
+ /** Accepted image sources. A Canvas is baked to a surface marker on assign; a Scene2D becomes a
24
+ * live-scene marker. */
25
+ export type ImageSource = string | FetchResponse | File | SvgSource | Canvas | Scene2D;
26
+ /** The stored/resolved image source (a Canvas is resolved to a surface marker, a Scene2D to a
27
+ * live-scene marker). */
28
+ export type ResolvedImageSource = string | FetchResponse | File | SvgSource | CanvasImageSource | Scene2DImageSource;
20
29
  /** An image element. Sources: URL / `asset(...)`, fetch response / File, an SVG source
21
- * (`assetIcon`), or a live Canvas (`canvas.update()` refreshes the node in place). */
30
+ * (`assetIcon`), a live Canvas (`canvas.update()` refreshes the node in place), or a live Scene2D
31
+ * (drawn into the box every frame — a minimap). */
22
32
  export interface UIImage extends UIElementBase<UIImageStyle> {
23
33
  readonly type: "image";
24
34
  /** The image source — writable to swap the picture in place. */