lecodes-sdk 0.20.2 → 1.0.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.
Files changed (77) hide show
  1. package/dist/global.d.ts +17 -5
  2. package/dist/types/canvas/Canvas.d.ts +2 -0
  3. package/dist/types/gl/DecalSet.d.ts +48 -3
  4. package/dist/types/gl/Geometry.d.ts +12 -0
  5. package/dist/types/gl/Lightmap.d.ts +9 -0
  6. package/dist/types/gl/Material.d.ts +4 -0
  7. package/dist/types/gl/Mesh.d.ts +7 -1
  8. package/dist/types/gl/Model.d.ts +34 -0
  9. package/dist/types/gl/Particles.d.ts +13 -0
  10. package/dist/types/gl/Scene.d.ts +1 -1
  11. package/dist/types/gl/animation/DynamicBone.d.ts +173 -0
  12. package/dist/types/gl/{IK.d.ts → animation/IK.d.ts} +4 -4
  13. package/dist/types/gl/{Locomotion.d.ts → animation/Locomotion.d.ts} +6 -6
  14. package/dist/types/gl/{AudioSource.d.ts → audio/AudioSource.d.ts} +6 -6
  15. package/dist/types/gl/{AudioZone.d.ts → audio/AudioZone.d.ts} +4 -4
  16. package/dist/types/gl/{SceneAudio.d.ts → audio/SceneAudio.d.ts} +1 -1
  17. package/dist/types/gl/{NavAgent.d.ts → nav/NavAgent.d.ts} +4 -4
  18. package/dist/types/gl/{NavMesh.d.ts → nav/NavMesh.d.ts} +4 -4
  19. package/dist/types/gl/{CharacterController.d.ts → physics/CharacterController.d.ts} +5 -5
  20. package/dist/types/gl/{Physics.d.ts → physics/Physics.d.ts} +6 -5
  21. package/dist/types/gl/{Ragdoll.d.ts → physics/Ragdoll.d.ts} +4 -4
  22. package/dist/types/gl/{Shape.d.ts → physics/Shape.d.ts} +3 -3
  23. package/dist/types/gl/{Trigger.d.ts → physics/Trigger.d.ts} +2 -2
  24. package/dist/types/gl/{Terrain.d.ts → terrain/Terrain.d.ts} +6 -6
  25. package/dist/types/gl/{terrainMesh.d.ts → terrain/terrainMesh.d.ts} +1 -1
  26. package/dist/types/gl/vehicle/Vehicle.d.ts +300 -0
  27. package/dist/types/gl/vehicle/Wheel.d.ts +147 -0
  28. package/dist/types/inject.d.ts +28 -26
  29. package/dist/types/runtime/files.d.ts +24 -1
  30. package/dist/types/scene/defineScene.d.ts +10 -3
  31. package/dist/types.json +1 -1
  32. package/package.json +1 -1
  33. package/prompts/README.md +142 -142
  34. package/src/bridges.d.ts +1599 -1481
  35. package/src/canvas/Canvas.ts +21 -0
  36. package/src/compile/__tests__/compile.test.ts +11 -0
  37. package/src/compile/header.ts +6 -3
  38. package/src/compile/index.ts +1 -0
  39. package/src/compile/sceneEditor.ts +42 -1
  40. package/src/gl/DecalSet.ts +132 -5
  41. package/src/gl/Geometry.ts +348 -284
  42. package/src/gl/Lightmap.ts +32 -5
  43. package/src/gl/Material.ts +21 -0
  44. package/src/gl/Mesh.ts +120 -102
  45. package/src/gl/Model.ts +33 -1
  46. package/src/gl/Node.ts +1 -1
  47. package/src/gl/Particles.ts +21 -3
  48. package/src/gl/Scene.ts +1 -1
  49. package/src/gl/animation/DynamicBone.ts +459 -0
  50. package/src/gl/{IK.ts → animation/IK.ts} +4 -4
  51. package/src/gl/{Locomotion.ts → animation/Locomotion.ts} +7 -7
  52. package/src/gl/{AudioSource.ts → audio/AudioSource.ts} +7 -7
  53. package/src/gl/{AudioZone.ts → audio/AudioZone.ts} +75 -75
  54. package/src/gl/{SceneAudio.ts → audio/SceneAudio.ts} +2 -2
  55. package/src/gl/{NavAgent.ts → nav/NavAgent.ts} +5 -5
  56. package/src/gl/{NavMesh.ts → nav/NavMesh.ts} +8 -8
  57. package/src/gl/{CharacterController.ts → physics/CharacterController.ts} +5 -5
  58. package/src/gl/{Physics.ts → physics/Physics.ts} +12 -5
  59. package/src/gl/{Ragdoll.ts → physics/Ragdoll.ts} +8 -6
  60. package/src/gl/{Shape.ts → physics/Shape.ts} +3 -3
  61. package/src/gl/{Trigger.ts → physics/Trigger.ts} +45 -45
  62. package/src/gl/{physicsEvents.ts → physics/physicsEvents.ts} +1 -1
  63. package/src/gl/{Terrain.ts → terrain/Terrain.ts} +10 -10
  64. package/src/gl/{terrainMesh.ts → terrain/terrainMesh.ts} +1 -1
  65. package/src/gl/vehicle/Vehicle.ts +666 -0
  66. package/src/gl/vehicle/Wheel.ts +290 -0
  67. package/src/inject.ts +226 -224
  68. package/src/runtime/files.ts +32 -2
  69. package/src/scene/defineScene.ts +26 -10
  70. package/src/scene/level.ts +2 -2
  71. package/dist/types/gl/Gearbox.d.ts +0 -86
  72. package/dist/types/gl/Vehicle.d.ts +0 -191
  73. package/dist/types/gl/Wheel.d.ts +0 -95
  74. package/src/gl/Gearbox.ts +0 -212
  75. package/src/gl/Vehicle.ts +0 -473
  76. package/src/gl/Wheel.ts +0 -240
  77. /package/dist/types/gl/{physicsEvents.d.ts → physics/physicsEvents.d.ts} +0 -0
package/dist/global.d.ts CHANGED
@@ -28,11 +28,13 @@ declare global {
28
28
  const Color: typeof SDK.Color
29
29
  const DateValue: typeof SDK.DateValue
30
30
  const DecalSet: typeof SDK.DecalSet
31
+ const DecalTrail: typeof SDK.DecalTrail
32
+ const DynamicBone: typeof SDK.DynamicBone
33
+ const DynamicBoneCollider: typeof SDK.DynamicBoneCollider
31
34
  const Feet: typeof SDK.Feet
32
35
  const File: typeof SDK.File
33
36
  const FollowPath: typeof SDK.FollowPath
34
37
  const FormData: typeof SDK.FormData
35
- const Gearbox: typeof SDK.Gearbox
36
38
  const Geolocation: typeof SDK.Geolocation
37
39
  const Geometry: typeof SDK.Geometry
38
40
  const Gizmos: typeof SDK.Gizmos
@@ -108,6 +110,7 @@ declare global {
108
110
  const Tilemap: typeof SDK.Tilemap
109
111
  const Tileset: typeof SDK.Tileset
110
112
  const Time: typeof SDK.Time
113
+ const Tire: typeof SDK.Tire
111
114
  const TouchStartEvent: typeof SDK.TouchStartEvent
112
115
  const Trail: typeof SDK.Trail
113
116
  const Trigger: typeof SDK.Trigger
@@ -200,6 +203,7 @@ declare global {
200
203
  const registerEditorTool: typeof SDK.registerEditorTool
201
204
  const registerEditorWindow: typeof SDK.registerEditorWindow
202
205
  const registerFont: typeof SDK.registerFont
206
+ const resolveTire: typeof SDK.resolveTire
203
207
  const resumeAnimation: typeof SDK.resumeAnimation
204
208
  const rot180: typeof SDK.rot180
205
209
  const rot270: typeof SDK.rot270
@@ -267,9 +271,16 @@ declare global {
267
271
  type DecalSet = SDK.DecalSet
268
272
  type DecalSetOptions = SDK.DecalSetOptions
269
273
  type DecalSpawnOptions = SDK.DecalSpawnOptions
274
+ type DecalTrail = SDK.DecalTrail
275
+ type DecalTrailOptions = SDK.DecalTrailOptions
270
276
  type DifferentialMode = SDK.DifferentialMode
271
277
  type Displacement = SDK.Displacement
272
278
  type DriveLayout = SDK.DriveLayout
279
+ type DynamicBone = SDK.DynamicBone
280
+ type DynamicBoneCollider = SDK.DynamicBoneCollider
281
+ type DynamicBoneColliders = SDK.DynamicBoneColliders
282
+ type DynamicBoneCurve = SDK.DynamicBoneCurve
283
+ type DynamicBoneFloor = SDK.DynamicBoneFloor
273
284
  type EchoParams = SDK.EchoParams
274
285
  type EdgeSlot = SDK.EdgeSlot
275
286
  type EditorApi = SDK.EditorApi
@@ -292,7 +303,6 @@ declare global {
292
303
  type Gait = SDK.Gait
293
304
  type GamepadAxisName = SDK.GamepadAxisName
294
305
  type GamepadState = SDK.GamepadState
295
- type Gearbox = SDK.Gearbox
296
306
  type GeoOptions = SDK.GeoOptions
297
307
  type GeoPosition = SDK.GeoPosition
298
308
  type GeoWatch = SDK.GeoWatch
@@ -407,6 +417,7 @@ declare global {
407
417
  type PlayOptions = SDK.PlayOptions
408
418
  type PlaySoundOptions = SDK.PlaySoundOptions
409
419
  type Playback = SDK.Playback
420
+ type Point = SDK.Point
410
421
  type PresentOptions = SDK.PresentOptions
411
422
  type Presentable = SDK.Presentable
412
423
  type PushEvent = SDK.PushEvent
@@ -460,6 +471,8 @@ declare global {
460
471
  type StateMachineEvents = SDK.StateMachineEvents
461
472
  type StatesDef<C> = SDK.StatesDef<C>
462
473
  type SteeringConfig = SDK.SteeringConfig
474
+ type SteeringDriver = SDK.SteeringDriver
475
+ type SteeringFeedback = SDK.SteeringFeedback
463
476
  type StepHandler = SDK.StepHandler
464
477
  type StepWarpOptions = SDK.StepWarpOptions
465
478
  type StopOptions = SDK.StopOptions
@@ -485,13 +498,14 @@ declare global {
485
498
  type TilemapSourceDef = SDK.TilemapSourceDef
486
499
  type Tileset = SDK.Tileset
487
500
  type TilesetDef = SDK.TilesetDef
488
- type TireCurve = SDK.TireCurve
501
+ type Tire = SDK.Tire
489
502
  type TouchStartEvent<T = any> = SDK.TouchStartEvent<T>
490
503
  type Trail = SDK.Trail
491
504
  type Transition = SDK.Transition
492
505
  type TransitionName = SDK.TransitionName
493
506
  type TransitionSpec = SDK.TransitionSpec
494
507
  type TransitionTransform = SDK.TransitionTransform
508
+ type TrianglePick = SDK.TrianglePick
495
509
  type Trigger = SDK.Trigger
496
510
  type Trigger2D = SDK.Trigger2D
497
511
  type UIBottomSheet = SDK.UIBottomSheet
@@ -546,8 +560,6 @@ declare global {
546
560
  type WebSocket = SDK.WebSocket
547
561
  type Wheel = SDK.Wheel
548
562
  type WheelSuspension = SDK.WheelSuspension
549
- type WheelTire = SDK.WheelTire
550
- type WheelVisual = SDK.WheelVisual
551
563
  type With<N, A extends Aspect<any, any, any>> = SDK.With<N, A>
552
564
  type __GizmoBuffer = SDK.__GizmoBuffer
553
565
  // ---- server globals (available in *.server.ts; using them in client code is a build error) ----
@@ -56,6 +56,8 @@ export declare class Canvas {
56
56
  private _surfaceId;
57
57
  private _tex2d?;
58
58
  private _uiBound;
59
+ private _lastBakeLen;
60
+ private _warnedGrowth;
59
61
  constructor(width: number, height: number, opts?: {
60
62
  pixelRatio?: number;
61
63
  });
@@ -24,6 +24,14 @@ export type DecalOptions = {
24
24
  fadeIn?: number;
25
25
  /** Seconds of fade at the end of `life` (ignored with life 0). Default 0. */
26
26
  fadeOut?: number;
27
+ /** Opacity multipliers at the image's BOTTOM and TOP edge (−Y / +Y), interpolated along it — a
28
+ * tire mark that darkens as the slide deepens. Default `[1, 1]`. */
29
+ gradient?: [number, number];
30
+ /** Tilt of the image's bottom and top edge, as a unit-space slope (Δy per Δx across the box):
31
+ * the edge pivots on the corner of the side it keeps and cuts INTO the box on the other, so two
32
+ * boxes can meet on one shared line — what `DecalTrail` uses to mitre its joints. `0` = square.
33
+ * Default `[0, 0]`. */
34
+ caps?: [number, number];
27
35
  };
28
36
  export type DecalSetOptions = DecalOptions & {
29
37
  /** The material — `Material.decal({ map })` by default (`map` below is its shortcut). A custom
@@ -51,8 +59,10 @@ export type DecalSetOptions = DecalOptions & {
51
59
  /** HDR boost of the image (0 = none). Default material only. */
52
60
  emissive?: number;
53
61
  name?: string;
54
- /** Coarse draw order, 0 (first) … 7 (last); default 4 — see `Mesh.renderPriority`. Decals are
55
- * blended and sort with the other blended draws of their priority. */
62
+ /** Coarse draw order, 0 (first) … 7 (last) — see `Mesh.renderPriority`. Default 3: a decal is
63
+ * part of the surface it sits on, so it draws before every other blended thing (meshes 4,
64
+ * particles 5) — the engine's depth sort compares object centres, and a set spread over the
65
+ * level has no useful centre. */
56
66
  renderPriority?: number;
57
67
  };
58
68
  /** `spawn` placement: where the image sits on the surface. */
@@ -99,5 +109,40 @@ export declare class DecalSet extends Node {
99
109
  remove(slot: number): this;
100
110
  clear(): this;
101
111
  private _writePlacement;
102
- private _write;
112
+ }
113
+ /** A `DecalTrail`'s look: the strip's width, and what every segment carries. */
114
+ export type DecalTrailOptions = Omit<DecalOptions, "size" | "caps" | "gradient"> & {
115
+ /** Width of the strip on the surface, world units. */
116
+ width: number;
117
+ };
118
+ /**
119
+ * A continuous strip of decals along a path — a tire's skid mark, a dragged body, a tread track.
120
+ * Feed it points (`add`) as the thing moves; every new point becomes one box-decal SEGMENT from the
121
+ * previous point, its image's up along the travel, and the joints between segments are MITRED: each
122
+ * box is cut along the bisector it shares with its neighbour (`caps`), so the strip has no overlaps
123
+ * darkening the outside of a bend and no wedges of gap on the inside. The opacity given with each
124
+ * point is interpolated along the segment (`gradient`), so a mark can darken as a slide deepens and
125
+ * fade as it ends. Segments are the set's decals — same ring, same `max` budget: a set of 900 holds
126
+ * 900 segments across every trail drawn from it.
127
+ *
128
+ * The previous segment is REWRITTEN when the next point arrives (its far cap becomes the shared
129
+ * mitre), through `DecalSet.update`'s path — a recycled slot is simply left alone by the engine.
130
+ */
131
+ export declare class DecalTrail {
132
+ private _prev;
133
+ private _seg;
134
+ readonly set: DecalSet;
135
+ readonly options: DecalTrailOptions;
136
+ constructor(set: DecalSet, options: DecalTrailOptions);
137
+ /** Extend the strip to `point` on a surface with `normal`, `opacity` (0..1) at that point. The
138
+ * first call after a start / `end()` only anchors the strip. Returns the new segment's slot, or
139
+ * −1 when nothing was drawn (the anchor, a point that did not move, no host support). */
140
+ add(point: Vec3Like, normal: Vec3Like, opacity?: number): number;
141
+ /** Finish the strip. With a `point`, one last segment runs out to it at opacity 0 — a mark that
142
+ * tapers away instead of stopping dead. The next `add` anchors a new strip. */
143
+ end(point?: Vec3Like, normal?: Vec3Like): this;
144
+ private _rewrite;
145
+ /** Write one segment's box: the strip between its joints, extended past each mitred joint by the
146
+ * mitre's reach (w/2 · |slope|) so the tilted cap still passes through the joint's centre. */
147
+ private _box;
103
148
  }
@@ -25,6 +25,9 @@ export declare class Geometry {
25
25
  static box(size?: Vec3Like | number): Geometry;
26
26
  static sphere(options?: SphereOptions): Geometry;
27
27
  static cylinder(options?: CylinderOptions): Geometry;
28
+ /** A capsule along Y, centred at the origin: a cylinder `length` long (between the two cap centres)
29
+ * with a hemisphere of `radius` on each end — one closed surface, smooth across the seams. */
30
+ static capsule(options?: CapsuleOptions): Geometry;
28
31
  static plane(options?: PlaneOptions): Geometry;
29
32
  }
30
33
  export type SphereOptions = {
@@ -32,6 +35,15 @@ export type SphereOptions = {
32
35
  widthSegments?: number;
33
36
  heightSegments?: number;
34
37
  };
38
+ export type CapsuleOptions = {
39
+ radius?: number;
40
+ /** Distance between the two cap centres (the straight part); 0 = a sphere. Default 1. */
41
+ length?: number;
42
+ /** Segments around the axis. */
43
+ widthSegments?: number;
44
+ /** Rings per hemisphere (pole to equator). */
45
+ capSegments?: number;
46
+ };
35
47
  export type CylinderOptions = {
36
48
  radius?: number;
37
49
  radiusTop?: number;
@@ -27,6 +27,13 @@ export type LightmapLoadOptions = {
27
27
  * and its underside does not — the same shading the atlas gives the statics. Before that the volume
28
28
  * was one flat 0.5·E on every face and this knob was the workaround. Kept as a trim. */
29
29
  volumeBoost?: number;
30
+ /** What an OCCLUDER-only static (a prop without lightmap UVs — traced into the receivers' atlas but
31
+ * carrying no rect of its own) does in real time. `"baked"` (default): its shadow is in the bake, so
32
+ * it stops casting and the shadow pass draws only the movers — on a level of thousands of foliage
33
+ * cards that is a third of the GPU frame. `"realtime"`: it keeps casting, which is the only way its
34
+ * shadow reaches OTHER non-receiver props (a rock under a tree); the honest fix for that is lightmap
35
+ * UVs on the props (`lecodes assets doctor --lightmap-uv`), which makes them receivers. */
36
+ occluderShadows?: "baked" | "realtime";
30
37
  };
31
38
  export type LightmapInfo = {
32
39
  size: number;
@@ -45,6 +52,8 @@ export type LightmapInfo = {
45
52
  applied: number;
46
53
  total: number;
47
54
  missing: string[];
55
+ /** Occluder-only statics whose real-time shadow was switched off (`occluderShadows: "baked"`). */
56
+ occluders: number;
48
57
  };
49
58
  export type LightmapFiles = {
50
59
  data: string;
@@ -159,6 +159,9 @@ export declare class Material {
159
159
  * `metallicFactor`) plus `lightmap`, `lightmapST`, `ambientScale`, `sunStrength` (the sun / IBL terms come from
160
160
  * filament's per-frame uniforms inside the shader). */
161
161
  static lightmap(): Material;
162
+ /** `Material.lightmap()`'s masked twin (`blending: masked`, same shader and parameters): what the engine
163
+ * gives a lightmapped model's alpha-MASK materials. The cutoff comes from the glTF material. */
164
+ static lightmapMasked(): Material;
162
165
  /** The terrain splat material (docs/terrain-plan.md §1.5): four albedo (+ normal-map) layers blended by
163
166
  * a control map on the terrain's own grid, per-layer `tiling` (metres per repeat) / `roughness` /
164
167
  * `normalScale` / `triplanar`, lightmap-aware (the same `lightmap` / `lightmapST` block as
@@ -166,6 +169,7 @@ export declare class Material {
166
169
  * unset layer is white and an unbaked terrain is fully lit. */
167
170
  static terrain(): Material;
168
171
  private static _lmTemplate;
172
+ private static _lmMaskedTemplate;
169
173
  static _lightmapShading: LightmapShading;
170
174
  /** Which shader lightmapped models take — a graphics-quality tier, engine-wide. `"full"` is filament's
171
175
  * lit path over the baked atlas (IBL specular, real-time point lights such as a muzzle flash, sun
@@ -1,5 +1,5 @@
1
1
  import { type Vec3Like } from "../math/vec";
2
- import { Geometry, type CylinderOptions, type PlaneOptions, type SphereOptions } from "./Geometry";
2
+ import { Geometry, type CapsuleOptions, type CylinderOptions, type PlaneOptions, type SphereOptions } from "./Geometry";
3
3
  import { Material } from "./Material";
4
4
  import { Node } from "./Node";
5
5
  /** Common transform/render options every primitive factory accepts. */
@@ -21,6 +21,10 @@ export declare class Mesh extends Node {
21
21
  /** Replace the geometry in place — the node, its transform and its material stay, the vertex and
22
22
  * index buffers are rebuilt. What an editor overlay or a debug drawer redraws with. */
23
23
  setGeometry(geometry: Geometry): this;
24
+ private _renderPriority?;
25
+ private _culling?;
26
+ private _castShadows?;
27
+ private _receiveShadows?;
24
28
  /** A Mesh always carries a material (slot 0) — see Node.setMaterial for the slot API. */
25
29
  get material(): Material;
26
30
  set material(m: Material);
@@ -38,6 +42,8 @@ export declare class Mesh extends Node {
38
42
  }): Mesh;
39
43
  static sphere(options?: MeshOptions & SphereOptions): Mesh;
40
44
  static cylinder(options?: MeshOptions & CylinderOptions): Mesh;
45
+ /** A capsule along Y (`radius`, `length` between the cap centres) — a character's or a collider's shape in one mesh. */
46
+ static capsule(options?: MeshOptions & CapsuleOptions): Mesh;
41
47
  static plane(options?: MeshOptions & PlaneOptions): Mesh;
42
48
  /** Wrap a custom Geometry. */
43
49
  static from(geometry: Geometry, options?: MeshOptions): Mesh;
@@ -1,6 +1,34 @@
1
1
  import { type FetchResponse } from "../runtime/fetch";
2
2
  import { Node } from "./Node";
3
3
  import { Animator, type LodMode } from "./animation/Animator";
4
+ /** One polygon under a screen point — what `Model.pickTriangle` returns. `bones` are the vertex's raw
5
+ * JOINTS_0 / WEIGHTS_0 pairs (weight > 0; empty = unweighted), `bind` its position in mesh space,
6
+ * `world` its skinned position this frame. */
7
+ export interface TrianglePick {
8
+ /** the Model's root entity, and the mesh node's own entity (0 when the host has no entity for it) */
9
+ entity: number;
10
+ node: number;
11
+ nodeName: string;
12
+ mesh: string;
13
+ primitive: number;
14
+ material: string;
15
+ /** triangle index within the primitive (index-buffer order), and whether the ray came from behind */
16
+ triangle: number;
17
+ backface: boolean;
18
+ distance: number;
19
+ point: [number, number, number];
20
+ bary: [number, number, number];
21
+ skin: string;
22
+ vertices: {
23
+ index: number;
24
+ bind: [number, number, number];
25
+ world: [number, number, number];
26
+ bones: {
27
+ name: string;
28
+ weight: number;
29
+ }[];
30
+ }[];
31
+ }
4
32
  export declare class Model extends Node {
5
33
  /** The model's Animator — always present, its clip table = the GLB's embedded clips. Configure
6
34
  * more (external clips, blend spaces, layers) with `model.aspect(Animator, {...})`. */
@@ -31,6 +59,12 @@ export declare class Model extends Node {
31
59
  get lod(): LodMode;
32
60
  set lod(v: LodMode);
33
61
  private static _cullDefault;
62
+ /** DEBUG: the closest polygon of this model under a screen point (logical px — `Input.mouse.position`,
63
+ * a touch event's clientX/Y), tested against the CPU-skinned CURRENT pose, both faces. Names the
64
+ * triangle, its three vertices (bind + skinned positions) and their raw bone weights, so a stretched
65
+ * or misbound polygon can be traced to its binding. One full CPU skin of the model per call: click-rate
66
+ * only. null = miss, or a host without the pick (desktop today). */
67
+ pickTriangle(screenX: number, screenY: number): TrianglePick | null;
34
68
  /** Duplicate this model — a deep copy of the GLB (meshes, skeleton, animation clips), attached to
35
69
  * the same parent and scene and sharing this model's current transform. The clone has its own
36
70
  * independent animation state (reach it via clone.anim). Mirrors this model's culling flag. */
@@ -116,6 +116,12 @@ type Noise = {
116
116
  strength?: number;
117
117
  frequency?: number;
118
118
  speed?: number;
119
+ /** How much of the displacement a particle earns with AGE (default 0 = all of it from birth).
120
+ * The field is sampled by position, so at 0 every particle born at the same spot is pushed the
121
+ * same way and a plume's root wanders off its emitter by up to `strength` metres; at 1 a particle
122
+ * is born exactly where the emitter put it and drifts into the field over its life — what you
123
+ * want whenever the source is visible (an exhaust pipe, a contact patch, a muzzle). */
124
+ ramp?: number;
119
125
  };
120
126
  /** Ground plane for bouncing particles (all render modes). `height` is in the simulation space:
121
127
  * world y with `space: 'world'`, emitter-local y otherwise. */
@@ -169,6 +175,11 @@ export type ParticlesOptions = ParticlesMaterialOptions & {
169
175
  * without this the engine picks who covers whom per frame (smoke popping over a fireball).
170
176
  * Default 0; a fireball wants 2, its smoke 1, a smoke trail -1. */
171
177
  order?: number;
178
+ /** Coarse draw order among ALL blended draws, 0 (first) … 7 (last) — see `Mesh.renderPriority`.
179
+ * Default 5: meshes sit at 4 and decals at 3, so smoke covers a car's glass and its skid marks
180
+ * whatever the camera does (the engine's depth sort compares object centres, and an emitter's
181
+ * centre says nothing about where its cloud is). `order` breaks ties inside one priority. */
182
+ renderPriority?: number;
172
183
  shape?: Shape;
173
184
  /** Extra velocity over life (radial burst that decays, axis drift) — see `VelocityOverLife`. */
174
185
  velocityOverLife?: VelocityOverLife;
@@ -212,6 +223,8 @@ export declare class Particles extends Node {
212
223
  set inheritVelocity(val: number);
213
224
  set rateOverDistance(val: number);
214
225
  set order(val: number);
226
+ /** Coarse draw order, 0 … 7 — see `Mesh.renderPriority`. Write-only. */
227
+ set renderPriority(v: number);
215
228
  set velocityOverLife(v: VelocityOverLife);
216
229
  set shape(shape: Shape);
217
230
  /** (mesh mode) Replace the particle mesh; live particles keep flying as the new shape. */
@@ -4,7 +4,7 @@ import { Presentable, type PresentOptions } from "../ui/presentable";
4
4
  import type { ClickEvent, TouchStartEvent } from "../runtime/touch";
5
5
  import type { FetchResponse } from "../runtime/fetch";
6
6
  import { Camera } from "./Camera";
7
- import { SceneAudio } from "./SceneAudio";
7
+ import { SceneAudio } from "./audio/SceneAudio";
8
8
  import { type ControlsHandle, type ControlsOptions } from "./controls";
9
9
  import { Material } from "./Material";
10
10
  import { Node } from "./Node";
@@ -0,0 +1,173 @@
1
+ import { Aspect } from "../../core/Aspect";
2
+ import type { FieldMeta } from "../../core/fields";
3
+ import { Vec3, type Vec3Like } from "../../math/vec";
4
+ import { Node } from "../Node";
5
+ /** One value for the chain, or `[root, tip]` interpolated down it by depth. */
6
+ export type DynamicBoneCurve = number | readonly [root: number, tip: number];
7
+ /** `'none'`, `'probe'` (a ray from the chain root down against the physics world, every frame), a
8
+ * height (a horizontal plane at that world y) or a world point (a plane through it, `floorNormal` up). */
9
+ export type DynamicBoneFloor = "none" | "probe" | number | Vec3Like;
10
+ /** `'auto'` = every DynamicBoneCollider under the model; `'humanoid'` = those plus capsules generated
11
+ * on the standard humanoid bones (hips, spine, head, limbs — the Ragdoll's layout); `'none'`; or the
12
+ * nodes carrying the colliders to use. */
13
+ export type DynamicBoneColliders = "auto" | "humanoid" | "none" | Node[];
14
+ /** The cloth's OUTSIDE, which makes every collider one-sided: `'none'` = a collider pushes a bone to its
15
+ * nearest surface (tails, ropes); `'auto'` = away from the root bone's own axis (a cloak's strips round a
16
+ * spine face out from it); a vector = a fixed direction in the root bone's frame (a cape on the back:
17
+ * `[0, 0, -1]`). With a side the body is under the cloth by definition: an arm that swung through the
18
+ * cape pushes it away, and a bone that ended up inside a capsule leaves it on the outside — never
19
+ * through it to be held against the chest from the wrong side. */
20
+ export type DynamicBoneSide = "none" | "auto" | Vec3Like;
21
+ /** A capsule (or a sphere) riding a bone — what dynamic bones stay out of. Attach to a body bone:
22
+ * `hips.aspect(DynamicBoneCollider, { radius: 0.12, to: 'Spine' })`. The capsule runs from
23
+ * `offset` (the bone's origin by default) to the `to` bone's origin, or to `end` (both in the bone's
24
+ * local space); neither = a sphere at `offset`. Picked up by every DynamicBone on the same model
25
+ * with `colliders: 'auto'` / `'humanoid'`, or listed explicitly. */
26
+ export declare class DynamicBoneCollider extends Aspect<"dynamicBoneCollider", Node> {
27
+ static readonly aspect = "dynamicBoneCollider";
28
+ /** Radius in metres, in the world: the bone's scale does not touch it (`offset` / `end` are in the bone's own units). */
29
+ radius: number;
30
+ /** Start of the capsule in the bone's local space (default: the bone's origin). */
31
+ offset: Vec3Like;
32
+ /** End of the capsule in the bone's local space (a sphere when neither `end` nor `to` is set). */
33
+ end?: Vec3Like;
34
+ /** The bone the capsule reaches (its origin), instead of `end`. */
35
+ to?: string;
36
+ static fields: FieldMeta<DynamicBoneCollider>;
37
+ private _id;
38
+ private _segment;
39
+ onAttach(): void;
40
+ onReconfigure(): void;
41
+ onDetach(): void;
42
+ }
43
+ export declare class DynamicBone extends Aspect<"dynamicBone", Node> {
44
+ static readonly aspect = "dynamicBone";
45
+ /** Only these bones (by name) join the chain — the root always does, a bone left out takes its
46
+ * subtree with it. Default: the root's whole subtree. */
47
+ bones?: string[];
48
+ /** Depth limit under the root (0 = no limit). */
49
+ depth: number;
50
+ /** Particle radius for the collisions, metres. */
51
+ radius: DynamicBoneCurve;
52
+ /** 0..1 — how fast a bone returns to the animated shape (per 1/60 s). 0 = a rope, 1 = rigid. */
53
+ stiffness: DynamicBoneCurve;
54
+ /** 0..1 — velocity lost per 1/60 s. 0.02 swings like a rope, 0.1 settles like a tail, 0.3 is honey. */
55
+ damping: DynamicBoneCurve;
56
+ /** m/s² downward. A hanging rest pose feels none of it (the bone length cancels it); a pose that
57
+ * sticks out droops by as much as `stiffness` lets it. */
58
+ gravity: DynamicBoneCurve;
59
+ /** Max deviation from the animated direction, degrees per bone (0 = free). */
60
+ angleLimit: DynamicBoneCurve;
61
+ /** Relative mass per bone. A bone-length constraint moves its two ends in inverse proportion to
62
+ * their masses (the root is kinematic): `[3, 1]` makes a tail's base carry its tip, `1` shares
63
+ * evenly like a rope. */
64
+ mass: DynamicBoneCurve;
65
+ /** Bones (by name) that ride the animation exactly while their subtrees still simulate: a sheet's
66
+ * several roots under one anchor (a cloak's seven strips under the spine), so ONE chain owns them all
67
+ * and `link` ties neighbouring strips. */
68
+ pinned?: string[];
69
+ /** Metres a PINNED bone may be pushed off its animated place by a collider — a soft pin. Its particle
70
+ * collides and takes the edge pushes like a free bone (the strip below hangs from where it IS) and is
71
+ * drawn back to the animation when the collider leaves; the bone's local translation follows. A hard
72
+ * pin (0) at a capsule's edge made the first free bone below it jitter: the edge push on a segment
73
+ * with an immovable end is a lever, and a touch near the root threw the child out. 2–3 cm for a
74
+ * cloak's roots under a shoulder capsule. */
75
+ pinGive: number;
76
+ /** 0..1 of the character's travel the chain takes rigidly (0 = the full whip on a dash, 1 = it
77
+ * moves with the body and only the pose's own motion swings it). */
78
+ follow: number;
79
+ /** World wind, m/s². */
80
+ wind: Vec3Like;
81
+ /** The BODY'S TURN, ≥ 0. Above 0 the chain lives in the parent bone's ROTATING frame: `damping` acts
82
+ * against the body's rigid motion (its travel plus its spin), so a heavily damped cloak RIDES a turn —
83
+ * sweeps round with the back — instead of standing in the world while the character spins under it and
84
+ * being dragged round after. The turn's inertia comes back as the frame's forces, scaled by this value:
85
+ * a centrifugal push `ω² · r` along each bone's animated direction from the axis (the hem, farthest
86
+ * from the spine, flies out and, on the chain's length, up). The frame's own physics is built in: the
87
+ * whip back when the turn starts and forward when it stops, Coriolis. 1 = the physical push; more = a
88
+ * bigger fling. With low damping the frame reproduces a free particle (nothing is counted twice);
89
+ * an animation's spine sway is ~1 % of a 900°/s turn. A rotation over 90° in one frame (a respawn
90
+ * facing) counts as a snap, not a spin. 0 = the plain translational frame, no turn forces. */
91
+ spin: number;
92
+ /** With `spin`: the cloth's INERTIA against the body's turn, seconds — the cloth's own rotation follows the
93
+ * body's with this time constant. It falls behind when a turn starts (by about ω·τ: 45° at 900°/s and 0.05),
94
+ * rides once caught up, and keeps turning past the back when the body stops (the same angle, eased out over
95
+ * τ). Smooth by construction. 0 = glued to the turn; large = the cloth stays in the world while the body spins. */
96
+ spinInertia: number;
97
+ /** 0..1 strength of the ties between neighbouring columns at the same depth (a cloak). */
98
+ link: number;
99
+ /** 0..1 blend from the animation (0) to the simulation (1). At 0 nothing is simulated and the
100
+ * chain re-arms on the animated pose when it comes back. */
101
+ weight: number;
102
+ /** Substep rate, Hz (≤ 4 substeps per frame). */
103
+ rate: number;
104
+ /** A root jump longer than this in one frame (metres) resets the chain instead of whipping it. */
105
+ teleport: number;
106
+ /** What the particles stay above: `'none'`, `'probe'`, a height, or a world point. */
107
+ floor: DynamicBoneFloor;
108
+ /** The floor plane's normal when `floor` is a point. */
109
+ floorNormal: Vec3Like;
110
+ /** Friction against the floor, a Coulomb coefficient: a resting bone's slide loses up to
111
+ * `floorFriction · gravity · dt` of speed per substep (0 = ice, 1 = a rubber sole). While a bone
112
+ * rests on the floor or a collider, `stiffness` and `angleLimit` let go of it: the surface's
113
+ * reaction outranks the spring, or a hem folded on the floor would run away along it. */
114
+ floorFriction: number;
115
+ /** Constraint passes per substep (1..8, default 4). Each pass shares every bone length between its
116
+ * ends, so a pull needs passes to travel down a long chain: a long rope may want 8, a short tail is
117
+ * fine with 2. */
118
+ iterations: number;
119
+ /** What the chain collides with (see DynamicBoneColliders). */
120
+ colliders: DynamicBoneColliders;
121
+ /** The cloth's outside — makes the colliders one-sided (see DynamicBoneSide). */
122
+ side: DynamicBoneSide;
123
+ /** How firmly `guide()` targets are HELD through the constraint passes, 0..1. At 0 a guide is applied once
124
+ * before the passes and the length / link passes may drag the bone back toward its un-guided neighbours
125
+ * within the same frame; at 0.5 it is re-pulled after every pass with half its weight (converges with the
126
+ * passes), at 1 with its full weight (a target the bone length cannot reach then jitters). */
127
+ guideHold: number;
128
+ /** The chain's EDGES collide with the capsules too — every bone segment and every link, not only the bones'
129
+ * particles: a capsule thinner than a bone's length (a forearm) no longer passes through the cloth between
130
+ * two bones. Off = particles only. */
131
+ edges: boolean;
132
+ static fields: FieldMeta<DynamicBone>;
133
+ private _id;
134
+ private _chain;
135
+ private _depths;
136
+ private _blob;
137
+ private _sent;
138
+ private _floorKey;
139
+ private _colliderKey;
140
+ private _collidersDirty;
141
+ private _collidersRef;
142
+ private _own;
143
+ private _colliderSet;
144
+ /** The bones of the chain, the root first. */
145
+ get chain(): readonly Node[];
146
+ onAttach(): void;
147
+ onReconfigure(): void;
148
+ /** Every field is live: what changed since the last frame is pushed here, before the engine's stage. */
149
+ update(): void;
150
+ onDetach(): void;
151
+ /** Snap the chain onto the animated pose next frame (a teleport, a camera cut). */
152
+ reset(): void;
153
+ /** GUIDES: world points the bones' particles are drawn to each substep before the constraints —
154
+ * weight 1 = there before them, the rest of the chain hangs, collides and links as before (a cape's
155
+ * strips riding the arms). Replaces the previous set; an empty list clears. A late-phase script
156
+ * (after the animator, before the engine's chain stage) refreshes it every frame. */
157
+ guide(rows: readonly {
158
+ bone: Node;
159
+ at: Vec3Like;
160
+ weight: number;
161
+ }[]): void;
162
+ /** The particles' world positions (the bones, then the leaves' virtual tips) — for a debug draw. */
163
+ get particles(): Vec3[];
164
+ private _collect;
165
+ private _fill;
166
+ /** Neighbour links: the bones at the same depth, in tree order, tied pairwise. */
167
+ private _links;
168
+ private _sync;
169
+ private _pushFloor;
170
+ private _pushColliders;
171
+ /** Capsules on the standard humanoid bones, sized like the Ragdoll's parts. */
172
+ private _makeHumanoid;
173
+ }
@@ -1,7 +1,7 @@
1
- import { Aspect } from "../core/Aspect";
2
- import { type Vec3Like } from "../math/vec";
3
- import { Quat } from "../math/quat";
4
- import type { Node } from "./Node";
1
+ import { Aspect } from "../../core/Aspect";
2
+ import { type Vec3Like } from "../../math/vec";
3
+ import { Quat } from "../../math/quat";
4
+ import type { Node } from "../Node";
5
5
  type Target = Node | Vec3Like;
6
6
  /** Two-bone analytic IK (limbs). Attach to the END bone: `foot.aspect(IK.TwoBone, { target })`
7
7
  * solves upper (grandparent) + mid (parent) so the end reaches `target`; `pole` steers the bend
@@ -1,9 +1,9 @@
1
- import { Aspect } from "../core/Aspect";
2
- import { Vec3, type Vec3Like } from "../math/vec";
3
- import { Animator } from "./animation/Animator";
4
- import type { FeetOptions } from "./animation/Feet";
5
- import type { WarpOptions } from "./animation/Warp";
6
- import type { Node } from "./Node";
1
+ import { Aspect } from "../../core/Aspect";
2
+ import { Vec3, type Vec3Like } from "../../math/vec";
3
+ import { Animator } from "./Animator";
4
+ import type { FeetOptions } from "./Feet";
5
+ import type { WarpOptions } from "./Warp";
6
+ import type { Node } from "../Node";
7
7
  /** How fast the character wants to go: the gait picks its clips and its speed. */
8
8
  export type Gait = "walk" | "run" | "sprint";
9
9
  /** What the engine shows: the idle, a start, the gait loop, a turn while moving, a stop, or a turn on the spot. */
@@ -1,9 +1,9 @@
1
- import { Aspect } from "../core/Aspect";
2
- import type { FieldMeta } from "../core/fields";
3
- import { type PlaySoundOptions, type Rolloff } from "../audio/audio";
4
- import type { Sound } from "../audio/Sound";
5
- import type { Voice } from "../audio/Voice";
6
- import type { Node } from "./Node";
1
+ import { Aspect } from "../../core/Aspect";
2
+ import type { FieldMeta } from "../../core/fields";
3
+ import { type PlaySoundOptions, type Rolloff } from "../../audio/audio";
4
+ import type { Sound } from "../../audio/Sound";
5
+ import type { Voice } from "../../audio/Voice";
6
+ import type { Node } from "../Node";
7
7
  export type AudioCone = {
8
8
  /** Degrees of full volume around the node's −Z. */
9
9
  inner: number;
@@ -1,7 +1,7 @@
1
- import { Aspect } from "../core/Aspect";
2
- import { type ReverbParams, type ReverbPreset } from "../audio/Bus";
3
- import { type Vec3Like } from "../math/vec";
4
- import type { Node } from "./Node";
1
+ import { Aspect } from "../../core/Aspect";
2
+ import { type ReverbParams, type ReverbPreset } from "../../audio/Bus";
3
+ import { type Vec3Like } from "../../math/vec";
4
+ import type { Node } from "../Node";
5
5
  export declare class AudioZone extends Aspect<"audioZone", Node> {
6
6
  static readonly aspect = "audioZone";
7
7
  private _box;
@@ -1,4 +1,4 @@
1
- import type { Node } from "./Node";
1
+ import type { Node } from "../Node";
2
2
  export declare class SceneAudio {
3
3
  private _listener;
4
4
  private _doppler;
@@ -1,8 +1,8 @@
1
- import { Aspect } from "../core/Aspect";
2
- import type { FieldMeta } from "../core/fields";
3
- import { Vec3, type Vec3Like } from "../math/vec";
1
+ import { Aspect } from "../../core/Aspect";
2
+ import type { FieldMeta } from "../../core/fields";
3
+ import { Vec3, type Vec3Like } from "../../math/vec";
4
4
  import { NavCrowd } from "./NavMesh";
5
- import type { Node } from "./Node";
5
+ import type { Node } from "../Node";
6
6
  export type NavAgentState = "idle" | "moving" | "arrived" | "blocked" | "offmesh";
7
7
  export type NavAgentAvoidance = "off" | "low" | "medium" | "high";
8
8
  export type NavAgentDrive = "position" | "controller";
@@ -1,7 +1,7 @@
1
- import { System } from "../core/Aspect";
2
- import { Vec3, type Vec3Like } from "../math/vec";
3
- import type { Node } from "./Node";
4
- import type { Scene } from "./Scene";
1
+ import { System } from "../../core/Aspect";
2
+ import { Vec3, type Vec3Like } from "../../math/vec";
3
+ import type { Node } from "../Node";
4
+ import type { Scene } from "../Scene";
5
5
  export declare const NAV_AGENT: {
6
6
  readonly RADIUS: 0;
7
7
  readonly HEIGHT: 1;