lecodes-sdk 0.20.2 → 1.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.
Files changed (129) hide show
  1. package/dist/global.d.ts +35 -9
  2. package/dist/types/animate/tween/Animation.d.ts +69 -0
  3. package/dist/types/animate/tween/Timeline.d.ts +55 -0
  4. package/dist/types/animate/tween/animateValue.d.ts +27 -0
  5. package/dist/types/animate/tween/easing.d.ts +29 -0
  6. package/dist/types/animate/tween/spec.d.ts +178 -0
  7. package/dist/types/canvas/Canvas.d.ts +2 -0
  8. package/dist/types/g2/Node2D.d.ts +16 -0
  9. package/dist/types/g2/Sprite.d.ts +11 -1
  10. package/dist/types/gl/Camera.d.ts +15 -1
  11. package/dist/types/gl/DecalSet.d.ts +48 -3
  12. package/dist/types/gl/Foliage.d.ts +47 -0
  13. package/dist/types/gl/Geometry.d.ts +36 -0
  14. package/dist/types/gl/Light.d.ts +25 -7
  15. package/dist/types/gl/Lightmap.d.ts +90 -51
  16. package/dist/types/gl/Material.d.ts +32 -20
  17. package/dist/types/gl/Mesh.d.ts +7 -1
  18. package/dist/types/gl/Model.d.ts +41 -5
  19. package/dist/types/gl/Node.d.ts +18 -0
  20. package/dist/types/gl/Particles.d.ts +53 -1
  21. package/dist/types/gl/Scene.d.ts +21 -1
  22. package/dist/types/gl/animation/AnimationClip.d.ts +19 -0
  23. package/dist/types/gl/animation/Animator.d.ts +27 -0
  24. package/dist/types/gl/animation/DynamicBone.d.ts +184 -0
  25. package/dist/types/gl/animation/IK.d.ts +109 -0
  26. package/dist/types/gl/{Locomotion.d.ts → animation/Locomotion.d.ts} +6 -6
  27. package/dist/types/gl/animation/Warp.d.ts +2 -1
  28. package/dist/types/gl/animation/core.d.ts +35 -4
  29. package/dist/types/gl/{AudioSource.d.ts → audio/AudioSource.d.ts} +6 -6
  30. package/dist/types/gl/{AudioZone.d.ts → audio/AudioZone.d.ts} +4 -4
  31. package/dist/types/gl/{SceneAudio.d.ts → audio/SceneAudio.d.ts} +1 -1
  32. package/dist/types/gl/{NavAgent.d.ts → nav/NavAgent.d.ts} +4 -4
  33. package/dist/types/gl/{NavMesh.d.ts → nav/NavMesh.d.ts} +4 -4
  34. package/dist/types/gl/{CharacterController.d.ts → physics/CharacterController.d.ts} +5 -5
  35. package/dist/types/gl/{Physics.d.ts → physics/Physics.d.ts} +6 -5
  36. package/dist/types/gl/physics/Ragdoll.d.ts +161 -0
  37. package/dist/types/gl/{Shape.d.ts → physics/Shape.d.ts} +3 -3
  38. package/dist/types/gl/{Trigger.d.ts → physics/Trigger.d.ts} +2 -2
  39. package/dist/types/gl/{Terrain.d.ts → terrain/Terrain.d.ts} +10 -8
  40. package/dist/types/gl/{terrainMesh.d.ts → terrain/terrainMesh.d.ts} +1 -1
  41. package/dist/types/gl/vehicle/Vehicle.d.ts +300 -0
  42. package/dist/types/gl/vehicle/Wheel.d.ts +147 -0
  43. package/dist/types/inject.d.ts +35 -27
  44. package/dist/types/runtime/files.d.ts +24 -1
  45. package/dist/types/scene/defineScene.d.ts +50 -31
  46. package/dist/types/ui/UIButton.d.ts +3 -1
  47. package/dist/types/ui/UIInput.d.ts +5 -1
  48. package/dist/types/ui/UINode.d.ts +24 -24
  49. package/dist/types.json +1 -1
  50. package/package.json +1 -1
  51. package/prompts/README.md +142 -142
  52. package/prompts/core-design.md +27 -4
  53. package/prompts/core.md +35 -6
  54. package/prompts/select.ts +19 -4
  55. package/src/animate/tween/Animation.ts +378 -0
  56. package/src/animate/tween/Timeline.ts +175 -0
  57. package/src/animate/tween/animateValue.ts +100 -0
  58. package/src/animate/tween/easing.ts +172 -0
  59. package/src/animate/tween/spec.ts +479 -0
  60. package/src/bridges.d.ts +1760 -1481
  61. package/src/canvas/Canvas.ts +21 -0
  62. package/src/compile/__tests__/assetMacro.test.ts +26 -0
  63. package/src/compile/__tests__/compile.test.ts +11 -0
  64. package/src/compile/__tests__/detectEntry.test.ts +19 -0
  65. package/src/compile/__tests__/serverSplit.test.ts +27 -0
  66. package/src/compile/bundler.ts +34 -4
  67. package/src/compile/compileProject.ts +31 -1
  68. package/src/compile/detectEntry.ts +8 -3
  69. package/src/compile/header.ts +6 -3
  70. package/src/compile/index.ts +3 -0
  71. package/src/compile/sceneEditor.ts +42 -1
  72. package/src/compile/serverSplit.ts +9 -3
  73. package/src/g2/Node2D.ts +38 -0
  74. package/src/g2/Sprite.ts +20 -1
  75. package/src/gl/Camera.ts +34 -1
  76. package/src/gl/DecalSet.ts +132 -5
  77. package/src/gl/Foliage.ts +102 -0
  78. package/src/gl/Geometry.ts +109 -0
  79. package/src/gl/Light.ts +46 -16
  80. package/src/gl/Lightmap.ts +440 -249
  81. package/src/gl/Material.ts +69 -36
  82. package/src/gl/Mesh.ts +120 -102
  83. package/src/gl/Model.ts +167 -124
  84. package/src/gl/Node.ts +40 -1
  85. package/src/gl/Particles.ts +82 -5
  86. package/src/gl/Scene.ts +35 -2
  87. package/src/gl/animation/AnimationClip.ts +52 -0
  88. package/src/gl/animation/Animator.ts +42 -2
  89. package/src/gl/animation/DynamicBone.ts +482 -0
  90. package/src/gl/animation/IK.ts +214 -0
  91. package/src/gl/{Locomotion.ts → animation/Locomotion.ts} +7 -7
  92. package/src/gl/animation/Playback.ts +5 -4
  93. package/src/gl/animation/Warp.ts +5 -2
  94. package/src/gl/animation/core.ts +65 -4
  95. package/src/gl/{AudioSource.ts → audio/AudioSource.ts} +7 -7
  96. package/src/gl/{AudioZone.ts → audio/AudioZone.ts} +75 -75
  97. package/src/gl/{SceneAudio.ts → audio/SceneAudio.ts} +2 -2
  98. package/src/gl/{NavAgent.ts → nav/NavAgent.ts} +5 -5
  99. package/src/gl/{NavMesh.ts → nav/NavMesh.ts} +8 -8
  100. package/src/gl/{CharacterController.ts → physics/CharacterController.ts} +5 -5
  101. package/src/gl/{Physics.ts → physics/Physics.ts} +12 -5
  102. package/src/gl/physics/Ragdoll.ts +451 -0
  103. package/src/gl/{Shape.ts → physics/Shape.ts} +3 -3
  104. package/src/gl/{Trigger.ts → physics/Trigger.ts} +45 -45
  105. package/src/gl/{physicsEvents.ts → physics/physicsEvents.ts} +1 -1
  106. package/src/gl/{Terrain.ts → terrain/Terrain.ts} +14 -12
  107. package/src/gl/{terrainMesh.ts → terrain/terrainMesh.ts} +1 -1
  108. package/src/gl/vehicle/Vehicle.ts +666 -0
  109. package/src/gl/vehicle/Wheel.ts +290 -0
  110. package/src/inject.ts +236 -224
  111. package/src/runtime/files.ts +32 -2
  112. package/src/scene/defineScene.ts +92 -66
  113. package/src/scene/level.ts +2 -2
  114. package/src/ui/UIButton.ts +2 -2
  115. package/src/ui/UIInput.ts +3 -3
  116. package/src/ui/UINode.ts +61 -36
  117. package/dist/types/animate/animate.d.ts +0 -20
  118. package/dist/types/gl/Gearbox.d.ts +0 -86
  119. package/dist/types/gl/IK.d.ts +0 -53
  120. package/dist/types/gl/Ragdoll.d.ts +0 -86
  121. package/dist/types/gl/Vehicle.d.ts +0 -191
  122. package/dist/types/gl/Wheel.d.ts +0 -95
  123. package/src/animate/animate.ts +0 -238
  124. package/src/gl/Gearbox.ts +0 -212
  125. package/src/gl/IK.ts +0 -193
  126. package/src/gl/Ragdoll.ts +0 -270
  127. package/src/gl/Vehicle.ts +0 -473
  128. package/src/gl/Wheel.ts +0 -240
  129. /package/dist/types/gl/{physicsEvents.d.ts → physics/physicsEvents.d.ts} +0 -0
@@ -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. */
@@ -164,11 +170,25 @@ export type ParticlesOptions = ParticlesMaterialOptions & {
164
170
  * spread evenly along the path — trail density independent of speed, no per-frame clumps.
165
171
  * Meant for `space: 'world'`. */
166
172
  rateOverDistance?: number;
173
+ /** Spread those spawn points along a CURVE through the emitter's recent path instead of the
174
+ * straight line between where it was last frame and where it is now. A straight line cuts the
175
+ * corner by however far the path bows inside one frame, which grows with the frame TIME — so a
176
+ * fast curving emitter looks faceted, and looks worse the slower the machine. Default false
177
+ * (the straight line); costs three stored positions and one curve evaluation per spawn.
178
+ *
179
+ * The curve needs a point one frame AHEAD, so each frame's spawns are placed provisionally and
180
+ * nudged into place on the next frame, once it exists. Nothing else observes them in between. */
181
+ smooth?: boolean;
167
182
  /** Draw order among blended systems at the same depth — higher draws later, i.e. on top (Unity's
168
183
  * sortingOrder). Systems are depth-sorted by their node, so the emitters of one effect tie and
169
184
  * without this the engine picks who covers whom per frame (smoke popping over a fireball).
170
185
  * Default 0; a fireball wants 2, its smoke 1, a smoke trail -1. */
171
186
  order?: number;
187
+ /** Coarse draw order among ALL blended draws, 0 (first) … 7 (last) — see `Mesh.renderPriority`.
188
+ * Default 5: meshes sit at 4 and decals at 3, so smoke covers a car's glass and its skid marks
189
+ * whatever the camera does (the engine's depth sort compares object centres, and an emitter's
190
+ * centre says nothing about where its cloud is). `order` breaks ties inside one priority. */
191
+ renderPriority?: number;
172
192
  shape?: Shape;
173
193
  /** Extra velocity over life (radial burst that decays, axis drift) — see `VelocityOverLife`. */
174
194
  velocityOverLife?: VelocityOverLife;
@@ -211,7 +231,11 @@ export declare class Particles extends Node {
211
231
  set space(val: "local" | "world");
212
232
  set inheritVelocity(val: number);
213
233
  set rateOverDistance(val: number);
234
+ /** Lay a frame's spawns along a curve through the emitter's path, not the straight chord. */
235
+ set smooth(val: boolean);
214
236
  set order(val: number);
237
+ /** Coarse draw order, 0 … 7 — see `Mesh.renderPriority`. Write-only. */
238
+ set renderPriority(v: number);
215
239
  set velocityOverLife(v: VelocityOverLife);
216
240
  set shape(shape: Shape);
217
241
  /** (mesh mode) Replace the particle mesh; live particles keep flying as the new shape. */
@@ -247,8 +271,28 @@ export type TrailOptions = Omit<ParticlesMaterialOptions, "render" | "stretch">
247
271
  width?: ParticleValue;
248
272
  /** Minimum emitter movement (world units) between recorded points. Default 0.05. */
249
273
  minDistance?: number;
250
- /** Point capacity (default 128). */
274
+ /** Point capacity (default 128). A spawn that does not fit is DROPPED, so a fast emitter at a
275
+ * small `minDistance` wants headroom: a sword tip covers metres inside one `time` window. */
251
276
  maxPoints?: number;
277
+ /** Follow a CURVE through the emitter's recent path instead of the straight line between frames.
278
+ * A trail is where this shows most — the strip is the path — and it shows worse the lower the
279
+ * frame rate, since a longer frame bows further off its own chord. Default false; see
280
+ * `ParticlesOptions.smooth`. */
281
+ smooth?: boolean;
282
+ /** Which way the strip's WIDTH points.
283
+ *
284
+ * `'camera'` (default) rolls the strip about its own length to stay flat to the viewer — it can
285
+ * never disappear, which is why it is the default, but it bears no relation to the thing that drew
286
+ * it. An AXIS (`'x'`/`'y'`/`'z'`) uses that axis of the emitter's own transform, as it was when
287
+ * each point was laid: on a node riding a sword's blade, `'z'` (the blade) makes the strip the
288
+ * surface the blade actually swept, and `width` stops being a made-up number — it is the blade. */
289
+ orient?: "camera" | "x" | "y" | "z";
290
+ /** With `orient` on an axis: the floor under the strip's APPARENT width, 0..1 of its real width,
291
+ * below which it rolls back toward the camera. An honestly oriented strip goes edge-on — and
292
+ * vanishes — whenever the swing happens in the plane of view; this is the hybrid that keeps it
293
+ * readable. 0 = never roll (honest, and it will vanish), 1 = always (the same as `'camera'`).
294
+ * Default 0.4: a sweep still reads as a sweep, and a swing toward the camera still shows. */
295
+ faceCamera?: number;
252
296
  color?: ParticleColor;
253
297
  /** Opacity along the trail. Defaults to fading the tail out (`curve().to(0)`). */
254
298
  opacity?: ParticleValue;
@@ -261,6 +305,8 @@ export type TrailOptions = Omit<ParticlesMaterialOptions, "render" | "stretch">
261
305
  export declare class Trail extends Node {
262
306
  private _material;
263
307
  private _rateDistance;
308
+ private _orient;
309
+ private _faceCamera;
264
310
  constructor(options?: TrailOptions);
265
311
  private _send;
266
312
  /** Trail length in seconds — how long each laid-down point lives. */
@@ -269,6 +315,12 @@ export declare class Trail extends Node {
269
315
  set opacity(val: ParticleValue);
270
316
  set color(value: ParticleColor);
271
317
  set minDistance(val: number);
318
+ /** Follow a curve through the emitter's path instead of the straight chord between frames. */
319
+ set smooth(val: boolean);
320
+ /** Which way the strip's width points — see `TrailOptions.orient`. Keeps the current `faceCamera`. */
321
+ set orient(val: "camera" | "x" | "y" | "z");
322
+ /** The hybrid's floor — see `TrailOptions.faceCamera`. */
323
+ set faceCamera(val: number);
272
324
  /** Pause/resume laying down points — existing ones still age out, so the trail fades naturally
273
325
  * after e.g. a sword swing ends. */
274
326
  set emitting(val: boolean);
@@ -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";
@@ -174,6 +174,19 @@ export declare class Scene implements Presentable {
174
174
  setLodBias(bias: number): void;
175
175
  /** Runtime form of `bloom` / `bloomIntensity` (a graphics-settings menu). */
176
176
  setBloom(enabled: boolean, intensity?: number): void;
177
+ /**
178
+ * How bright the environment (IBL) lights the scene, in lux — `SceneOptions.environmentIntensity`
179
+ * after the fact. Live: it changes the probe's intensity, not the probe, so it costs nothing and
180
+ * can be dragged. (`setDefaultIbl`, the call that installs a probe, rebuilds the cubemap from the
181
+ * ktx every time — never drive a slider through a scene option that has to re-open.)
182
+ *
183
+ * `lecodes lightmap bake` reads the same number off the live probe, so a scene dimmed here bakes
184
+ * dimmed. Hosts without the call keep whatever the scene opened with, and the getter still
185
+ * reports what was asked for.
186
+ */
187
+ get environmentIntensity(): number;
188
+ set environmentIntensity(lux: number);
189
+ private static _warnedEnvIntensity;
177
190
  /** The scene's stencil buffer on / off (see `SceneOptions.stencil`). */
178
191
  setStencil(enabled: boolean): void;
179
192
  add(...nodes: Node[]): this;
@@ -192,6 +205,13 @@ export declare class Scene implements Presentable {
192
205
  destroy(): void;
193
206
  createOverlay(options?: SceneOptions): Scene;
194
207
  warmRender(): Promise<void>;
208
+ /** Precompile the scene's shader variants so nothing is compiled mid-game. Every material the host
209
+ * knows is queued for the sun / shadow / fog / skinning variants and the dynamic-light key — the
210
+ * first point light (a muzzle flash, an explosion) would otherwise rebuild every lit shader in view
211
+ * on that frame. Call it once the sun and fog are set, typically under a loading screen; materials
212
+ * loaded later are queued in the background as they are created. Resolves at once on a host
213
+ * without the bridge. */
214
+ precompileShaders(): Promise<void>;
195
215
  readonly cl: (() => void)[];
196
216
  _backButtonCallback?: () => void;
197
217
  private _vd?;
@@ -50,6 +50,25 @@ export declare class AnimationClip {
50
50
  * keeps the ones inside the window, re-timed. Chainable. */
51
51
  addEvent(time: number, name: string): this;
52
52
  private _pushEvents;
53
+ /** ANCHOR SPANS for a bone — what an anchored IK chain ending in `bone` rides while this clip plays
54
+ * (`IK.TwoBone` with `anchor`; the sockets come from `anim.sockets(...)`). `donor` names the socket
55
+ * set of the gun the take was AUTHORED on ('' = the live set: no delta); `spans` = `[from, to,
56
+ * socket, rotation?, pin?]` in SECONDS — the socket the hand is on through that window (`''` = free:
57
+ * it rides the joint itself), `rotation` 0..1 how much of the socket's turn it takes (default 1),
58
+ * `pin` 0..1 how much it sits ON the socket instead of keeping its own relation to it (default 0: a
59
+ * magazine is REACHED, and the take's motion is what carries the hand there).
60
+ * Gaps between spans = the chain's own socket, pinned by the chain's `anchorPin` — that is where a
61
+ * hand holding the gun is nailed to the grip, so the layers below cannot slide it. A clip WITHOUT
62
+ * anchors takes no part: the hand keeps the take's own relation to the gun. Part of the clip, like
63
+ * events; `AnimationClip.from` carries them re-timed. Chainable.
64
+ * reload.anchors('hand_l', { donor: 'tr15', spans: [[0.9, 1.4, 'mag'], [1.4, 2.0, ''], [2.0, 2.4, 'mag'], [2.4, 2.7, 'bolt']] }) */
65
+ anchors(bone: string, def: {
66
+ donor?: string;
67
+ spans?: readonly (readonly [number, number, string, number?, number?])[];
68
+ }): this;
69
+ /** Drop a bone's anchor spans. */
70
+ clearAnchors(bone: string): this;
71
+ private _pushAnchors;
53
72
  /** Load ONE clip from a GLB: the file's only/first clip, or the one named / at the given index. */
54
73
  static load(source: string | FetchResponse, clip?: string | number): Promise<AnimationClip>;
55
74
  /** Load EVERY clip of a GLB (library files), keyed by clip name. */
@@ -1,4 +1,6 @@
1
1
  import { Aspect } from "../../core/Aspect";
2
+ import { type Vec3Like } from "../../math/vec";
3
+ import { type QuatLike } from "../../math/quat";
2
4
  import type { Node } from "../Node";
3
5
  import type { AnimationClip } from "./AnimationClip";
4
6
  import { type ActiveClip, type ClipInfo, type ClipEventHandler, type LayerOptions, type LoopDef, type LoopOptions, type PlayOptions, type StopOptions } from "./core";
@@ -75,6 +77,31 @@ export declare class Animator extends Aspect<"anim", Node> {
75
77
  * turn too); on for a rig whose root carries the heading (`lecodes assets retarget --root-rotation yaw`). */
76
78
  get rootRotation(): boolean;
77
79
  set rootRotation(on: boolean);
80
+ /** The body's WORLD velocity this frame (m/s): what `anim.warp`'s stride and orientation fit the clips
81
+ * to. A `Locomotion` feeds it itself; when you drive the gait by hand set it every frame (the
82
+ * CharacterController's velocity, or your own). Reads back the last value fed. */
83
+ get motion(): readonly [number, number, number];
84
+ set motion(v: {
85
+ x: number;
86
+ y: number;
87
+ z: number;
88
+ } | readonly [number, number, number]);
89
+ private _motion;
90
+ /** SOCKETS: named frames on a bone of this rig — a weapon's grip / magazine / bolt empties, placed in
91
+ * the scene editor. A NODE socket is read by the engine every frame (a socket on a moving part
92
+ * follows it); a `{ position, quaternion }` one is fixed in the bone's space. The default set is the
93
+ * LIVE one (the weapon in the hands); `set: 'tr15'` names a DONOR set — the same sockets on the gun a
94
+ * take was authored on, which a clip's `anchors()` refer to. Chainable.
95
+ * arms.anim.sockets('ik_hand_gun', { grip_r: w.gripR, grip_l: w.gripL, mag: w.mag })
96
+ * arms.anim.sockets('ik_hand_gun', { grip_l: { position: [0.03, -0.12, -0.05] } }, { set: 'tr15' }) */
97
+ sockets(joint: Node | string, table: Record<string, Node | {
98
+ position?: Vec3Like;
99
+ quaternion?: QuatLike;
100
+ }>, options?: {
101
+ set?: string;
102
+ }): this;
103
+ /** Drop a socket (the live set, or `set`). */
104
+ removeSocket(name: string, set?: string): this;
78
105
  /** `'auto'` (default): a character small on screen or out of view is evaluated every 2nd / 4th frame
79
106
  * without its finger, toe and twist joints; `'full'`: every frame, every joint (a hero seen through a
80
107
  * scope). Independent of `Model.lod`, the mesh level. */
@@ -0,0 +1,184 @@
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 SIDE of every collider marked `oneSided`: `'none'` = no side, every collider pushes a bone to
15
+ * its nearest surface (tails, ropes); `'auto'` = away from the root bone's own axis (a cloak's strips round
16
+ * a spine face out from it); a vector = a fixed direction in the root bone's frame (a cape hanging BEHIND
17
+ * the arms: the body's backward). A one-sided collider that is moving AWAY from that side — an arm swinging
18
+ * forward under the cape — puts a bone it has run into over to the side instead of carrying it: the arm
19
+ * passes through the cloth and the cape hangs behind it again, instead of being dragged round the body.
20
+ * Still, or moving toward the cloth, it pushes like any other collider, so cloth draped on an arm at rest
21
+ * stays where it is. Two-sided colliders (the body's) never read the side. */
22
+ export type DynamicBoneSide = "none" | "auto" | Vec3Like;
23
+ /** A capsule (or a sphere) riding a bone — what dynamic bones stay out of. Attach to a body bone:
24
+ * `hips.aspect(DynamicBoneCollider, { radius: 0.12, to: 'Spine' })`. The capsule runs from
25
+ * `offset` (the bone's origin by default) to the `to` bone's origin, or to `end` (both in the bone's
26
+ * local space); neither = a sphere at `offset`. Picked up by every DynamicBone on the same model
27
+ * with `colliders: 'auto'` / `'humanoid'`, or listed explicitly. */
28
+ export declare class DynamicBoneCollider extends Aspect<"dynamicBoneCollider", Node> {
29
+ static readonly aspect = "dynamicBoneCollider";
30
+ /** Radius in metres, in the world: the bone's scale does not touch it (`offset` / `end` are in the bone's own units). */
31
+ radius: number;
32
+ /** Start of the capsule in the bone's local space (default: the bone's origin). */
33
+ offset: Vec3Like;
34
+ /** End of the capsule in the bone's local space (a sphere when neither `end` nor `to` is set). */
35
+ end?: Vec3Like;
36
+ /** The bone the capsule reaches (its origin), instead of `end`. */
37
+ to?: string;
38
+ /** ONE-SIDED: the cloth belongs on the chain's `side` of this capsule (see DynamicBone.side). While the capsule
39
+ * moves away from that side it puts a bone it has run into over to the side instead of carrying it — an arm
40
+ * under a cape may push the cape back, never drag it forward round the body. Needs a `side` on the DynamicBone. */
41
+ oneSided: boolean;
42
+ static fields: FieldMeta<DynamicBoneCollider>;
43
+ private _id;
44
+ private _segment;
45
+ onAttach(): void;
46
+ onReconfigure(): void;
47
+ onDetach(): void;
48
+ }
49
+ export declare class DynamicBone extends Aspect<"dynamicBone", Node> {
50
+ static readonly aspect = "dynamicBone";
51
+ /** Only these bones (by name) join the chain — the root always does, a bone left out takes its
52
+ * subtree with it. Default: the root's whole subtree. */
53
+ bones?: string[];
54
+ /** Depth limit under the root (0 = no limit). */
55
+ depth: number;
56
+ /** Particle radius for the collisions, metres. */
57
+ radius: DynamicBoneCurve;
58
+ /** 0..1 — how fast a bone returns to the animated shape (per 1/60 s). 0 = a rope, 1 = rigid. */
59
+ stiffness: DynamicBoneCurve;
60
+ /** 0..1 — velocity lost per 1/60 s. 0.02 swings like a rope, 0.1 settles like a tail, 0.3 is honey. */
61
+ damping: DynamicBoneCurve;
62
+ /** m/s² downward. A hanging rest pose feels none of it (the bone length cancels it); a pose that
63
+ * sticks out droops by as much as `stiffness` lets it. */
64
+ gravity: DynamicBoneCurve;
65
+ /** Max deviation from the animated direction, degrees per bone (0 = free). */
66
+ angleLimit: DynamicBoneCurve;
67
+ /** Relative mass per bone. A bone-length constraint moves its two ends in inverse proportion to
68
+ * their masses (the root is kinematic): `[3, 1]` makes a tail's base carry its tip, `1` shares
69
+ * evenly like a rope. */
70
+ mass: DynamicBoneCurve;
71
+ /** Bones (by name) that ride the animation exactly while their subtrees still simulate: a sheet's
72
+ * several roots under one anchor (a cloak's seven strips under the spine), so ONE chain owns them all
73
+ * and `link` ties neighbouring strips — neighbours in THIS order, so list the strips round the ring
74
+ * (front-left … back … front-right); the front stays open. */
75
+ pinned?: string[];
76
+ /** Metres a PINNED bone may be pushed off its animated place by a collider — a soft pin. Its particle
77
+ * collides and takes the edge pushes like a free bone (the strip below hangs from where it IS) and is
78
+ * drawn back to the animation when the collider leaves; the bone's local translation follows. A hard
79
+ * pin (0) at a capsule's edge made the first free bone below it jitter: the edge push on a segment
80
+ * with an immovable end is a lever, and a touch near the root threw the child out. 2–3 cm for a
81
+ * cloak's roots under a shoulder capsule. */
82
+ pinGive: number;
83
+ /** 0..1 of the character's travel the chain takes rigidly (0 = the full whip on a dash, 1 = it
84
+ * moves with the body and only the pose's own motion swings it). */
85
+ follow: number;
86
+ /** World wind, m/s². */
87
+ wind: Vec3Like;
88
+ /** The BODY'S TURN, ≥ 0. Above 0 the chain lives in the parent bone's ROTATING frame: `damping` acts
89
+ * against the body's rigid motion (its travel plus its spin), so a heavily damped cloak RIDES a turn —
90
+ * sweeps round with the back — instead of standing in the world while the character spins under it and
91
+ * being dragged round after. The turn's inertia comes back as the frame's forces, scaled by this value:
92
+ * a centrifugal push `ω² · r` along each bone's animated direction from the axis (the hem, farthest
93
+ * from the spine, flies out and, on the chain's length, up). The frame's own physics is built in: the
94
+ * whip back when the turn starts and forward when it stops, Coriolis. 1 = the physical push; more = a
95
+ * bigger fling. With low damping the frame reproduces a free particle (nothing is counted twice);
96
+ * an animation's spine sway is ~1 % of a 900°/s turn. A rotation over 90° in one frame (a respawn
97
+ * facing) counts as a snap, not a spin. 0 = the plain translational frame, no turn forces. */
98
+ spin: number;
99
+ /** With `spin`: the cloth's INERTIA against the body's turn, seconds — the cloth's own rotation follows the
100
+ * body's with this time constant. It falls behind when a turn starts (by about ω·τ: 45° at 900°/s and 0.05),
101
+ * rides once caught up, and keeps turning past the back when the body stops (the same angle, eased out over
102
+ * τ). Smooth by construction. 0 = glued to the turn; large = the cloth stays in the world while the body spins. */
103
+ spinInertia: number;
104
+ /** 0..1 strength of the ties between neighbouring columns at the same depth (a cloak). */
105
+ link: number;
106
+ /** 0..1 blend from the animation (0) to the simulation (1). At 0 nothing is simulated and the
107
+ * chain re-arms on the animated pose when it comes back. */
108
+ weight: number;
109
+ /** Substep rate, Hz (≤ 4 substeps per frame). */
110
+ rate: number;
111
+ /** A root jump longer than this in one frame (metres) resets the chain instead of whipping it. */
112
+ teleport: number;
113
+ /** What the particles stay above: `'none'`, `'probe'`, a height, or a world point. */
114
+ floor: DynamicBoneFloor;
115
+ /** The floor plane's normal when `floor` is a point. */
116
+ floorNormal: Vec3Like;
117
+ /** Friction against the floor, a Coulomb coefficient: a resting bone's slide loses up to
118
+ * `floorFriction · gravity · dt` of speed per substep (0 = ice, 1 = a rubber sole). While a bone
119
+ * rests on the floor or a collider, `stiffness` and `angleLimit` let go of it: the surface's
120
+ * reaction outranks the spring, or a hem folded on the floor would run away along it. */
121
+ floorFriction: number;
122
+ /** Constraint passes per substep (1..8, default 4). Each pass shares every bone length between its
123
+ * ends, so a pull needs passes to travel down a long chain: a long rope may want 8, a short tail is
124
+ * fine with 2. */
125
+ iterations: number;
126
+ /** What the chain collides with (see DynamicBoneColliders). */
127
+ colliders: DynamicBoneColliders;
128
+ /** The cloth's outside — makes the colliders one-sided (see DynamicBoneSide). */
129
+ side: DynamicBoneSide;
130
+ /** How firmly `guide()` targets are HELD through the constraint passes, 0..1. At 0 a guide is applied once
131
+ * before the passes and the length / link passes may drag the bone back toward its un-guided neighbours
132
+ * within the same frame; at 0.5 it is re-pulled after every pass with half its weight (converges with the
133
+ * passes), at 1 with its full weight (a target the bone length cannot reach then jitters). */
134
+ guideHold: number;
135
+ /** The chain's EDGES collide with the capsules too — every bone segment and every link, not only the bones'
136
+ * particles: a capsule thinner than a bone's length (a forearm) no longer passes through the cloth between
137
+ * two bones. Off = particles only. */
138
+ edges: boolean;
139
+ static fields: FieldMeta<DynamicBone>;
140
+ private _id;
141
+ private _chain;
142
+ private _depths;
143
+ private _blob;
144
+ private _sent;
145
+ private _floorKey;
146
+ private _colliderKey;
147
+ private _collidersDirty;
148
+ private _collidersRef;
149
+ private _own;
150
+ private _colliderSet;
151
+ /** The bones of the chain, the root first. */
152
+ get chain(): readonly Node[];
153
+ onAttach(): void;
154
+ onReconfigure(): void;
155
+ /** Every field is live: what changed since the last frame is pushed here, before the engine's stage. */
156
+ update(): void;
157
+ onDetach(): void;
158
+ /** Snap the chain onto the animated pose next frame (a teleport, a camera cut). */
159
+ reset(): void;
160
+ /** GUIDES: world points the bones' particles are drawn to each substep before the constraints —
161
+ * weight 1 = there before them, the rest of the chain hangs, collides and links as before (a cape's
162
+ * strips riding the arms). Replaces the previous set; an empty list clears. A late-phase script
163
+ * (after the animator, before the engine's chain stage) refreshes it every frame. */
164
+ guide(rows: readonly {
165
+ bone: Node;
166
+ at: Vec3Like;
167
+ weight: number;
168
+ }[]): void;
169
+ /** The particles' world positions (the bones, then the leaves' virtual tips) — for a debug draw. */
170
+ get particles(): Vec3[];
171
+ private _collect;
172
+ private _fill;
173
+ /** Neighbour links: the bones at the same depth tied pairwise, the columns in the order of `pinned` (a cloak's
174
+ * strips listed round the ring) — columns not listed there follow in tree order. Tree order alone is the
175
+ * engine's child order, which for a GLB is the file's node order REVERSED (Filament prepends each child), so
176
+ * strips added to a rig later were tied across the body instead of to their neighbours: the ermine's cloak
177
+ * had one true neighbour pair out of eight, the rest were rods through the chest. */
178
+ private _links;
179
+ private _sync;
180
+ private _pushFloor;
181
+ private _pushColliders;
182
+ /** Capsules on the standard humanoid bones, sized like the Ragdoll's parts. */
183
+ private _makeHumanoid;
184
+ }
@@ -0,0 +1,109 @@
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
+ import type { Core } from "./core";
6
+ type Target = Node | Vec3Like;
7
+ /** What both chains share: the native handle, the parameter push, the readback. */
8
+ declare abstract class Chain<Name extends string> extends Aspect<Name, Node> {
9
+ protected _rig?: Core;
10
+ protected _ik: number;
11
+ protected _weight: number;
12
+ protected _enabled: boolean;
13
+ protected abstract readonly kind: 0 | 1;
14
+ /** 0–1 contribution (blend in/out, e.g. foot planting only while grounded). */
15
+ get weight(): number;
16
+ set weight(v: number);
17
+ /** Solve every frame (default). */
18
+ get enabled(): boolean;
19
+ set enabled(v: boolean);
20
+ /** After the last solve: how far the end bone still is from its target, metres (0 = reached). */
21
+ get error(): number;
22
+ onAttach(): void;
23
+ onDetach(): void;
24
+ onReconfigure(): void;
25
+ protected params(): Float32Array;
26
+ /** Push the description to the engine (a no-op until attached). */
27
+ protected abstract push(): void;
28
+ }
29
+ /** Two-bone analytic IK (limbs). Attach to the END bone: `foot.aspect(IK.TwoBone, { target })` solves
30
+ * upper (grandparent) + mid (parent) so the end reaches `target`; `pole` steers the bend (knee /
31
+ * elbow) — a Node or world point; `anchor` makes it ride a socket of the rig instead (see the file
32
+ * header). */
33
+ declare class TwoBone extends Chain<"ik"> {
34
+ static readonly aspect = "ik";
35
+ protected readonly kind: 0;
36
+ private _target?;
37
+ private _pole?;
38
+ private _rotation?;
39
+ private _rotationWeight;
40
+ private _anchor;
41
+ private _anchorTime;
42
+ private _anchorPin;
43
+ /** Where the end bone should be (a Node — read by the engine every frame — or a world position). */
44
+ get target(): Target | undefined;
45
+ set target(v: Target | undefined);
46
+ /** Bend hint — the mid joint is pulled toward it (a Node or a world point). */
47
+ get pole(): Target | undefined;
48
+ set pole(v: Target | undefined);
49
+ /** World rotation the END bone takes after the solve — a Node (its world rotation) or a Quat. Unset =
50
+ * the end bone keeps its animated rotation (a hand on a grip, a foot on a slope want it set). Blended
51
+ * by `weight · rotationWeight`. An anchored chain takes its rotation from the socket instead. */
52
+ get rotation(): Node | Quat | undefined;
53
+ set rotation(v: Node | Quat | undefined);
54
+ /** 0–1 contribution of `rotation` (on top of `weight`); for an anchored chain, of the socket's turn. */
55
+ get rotationWeight(): number;
56
+ set rotationWeight(v: number);
57
+ /** ANCHORED: the name of the LIVE socket the end bone rides (`anim.sockets(...)`) — its target and
58
+ * rotation come from the socket and the playing clips' anchor spans; `target` / `pole` / `rotation`
59
+ * are ignored. '' = a plain chain. */
60
+ get anchor(): string;
61
+ set anchor(v: string);
62
+ /** Anchored: how fast the hand moves between sockets — the halflife (s) of the spring the applied
63
+ * delta follows the wanted one with (default 0.05). */
64
+ get anchorTime(): number;
65
+ set anchorTime(v: number);
66
+ /** Anchored: 0..1 how hard the end bone is PINNED on its socket while the playing clips leave it there
67
+ * (a GAP in their spans — the hand is holding the gun). 1 = it sits exactly on the socket, so nothing
68
+ * in the pose below can slide it along the gun; 0 (default) = it keeps the take's own relation to the
69
+ * socket, moved by the delta. A clip's SPANS carry their own pin and default to 0, because a hand on
70
+ * its way to a magazine must keep the take's motion — so this knob is about holding, not reaching. */
71
+ get anchorPin(): number;
72
+ set anchorPin(v: number);
73
+ /** After the last solve: the anchor delta applied — how far (m) and how much (rad) the live socket
74
+ * moved the hand off the take's own pose. 0 for a plain chain. */
75
+ get anchorDelta(): {
76
+ distance: number;
77
+ angle: number;
78
+ };
79
+ protected push(): void;
80
+ }
81
+ /** Aim a bone at a target (head / eyes / turret). Attach to the bone itself:
82
+ * `head.aspect(IK.LookAt, { target: camera, limit: 70 })`. `axis` is the bone's LOCAL forward
83
+ * (the direction that should point at the target) — rigs differ; default −Z, Mixamo heads look
84
+ * along +Z of the head bone in most exports, so pass `axis: [0, 0, 1]` there if it faces backwards. */
85
+ declare class LookAt extends Chain<"lookAt"> {
86
+ static readonly aspect = "lookAt";
87
+ protected readonly kind: 1;
88
+ private _target?;
89
+ private _axis;
90
+ private _limit;
91
+ /** What to look at (a Node — read by the engine every frame — or a world position). */
92
+ get target(): Target | undefined;
93
+ set target(v: Target | undefined);
94
+ /** The bone's local axis that should point at the target. */
95
+ get axis(): Vec3Like;
96
+ set axis(v: Vec3Like);
97
+ /** Max deflection from the animated direction, in degrees (default 80). */
98
+ get limit(): number;
99
+ set limit(v: number);
100
+ protected push(): void;
101
+ }
102
+ /** Inverse kinematics aspects — attach to bones (see file header). */
103
+ export declare const IK: {
104
+ TwoBone: typeof TwoBone;
105
+ LookAt: typeof LookAt;
106
+ };
107
+ export type IKTwoBone = TwoBone;
108
+ export type IKLookAt = LookAt;
109
+ export {};
@@ -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. */
@@ -2,7 +2,8 @@
2
2
  export type WarpOptions = {
3
3
  /** Fit the stride to the speed the body actually travels at. `[min, max]` clamps the scale (default
4
4
  * 0.85…1.2). A CORRECTION: a pack whose takes already read right at the speeds it is played at wants
5
- * none of this; open the range for a pack that must cover speeds it was never recorded at. */
5
+ * none of this; open the range for a pack that must cover speeds it was never recorded at. The clip's
6
+ * own speed is its root motion — an in-place loop takes part only with a declared pace (see above). */
6
7
  stride?: boolean | [number, number];
7
8
  /** Turn the lower body toward where the body really travels; a number caps the turn in degrees
8
9
  * (default 20). The whole twist lives in one joint: a few degrees read as a lean, a lot as a broken
@@ -13,15 +13,24 @@ export type PlayOptions = {
13
13
  fade?: number;
14
14
  /** Transition seconds for the way in only (overrides `fade`). */
15
15
  fadeIn?: number;
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. */
16
+ /** One-shots: the transition BACK to the loop after the end (overrides `fade`; `0` = cut at the end).
17
+ * The clip plays to its last frame; the loop then takes over and the difference between the two poses
18
+ * (and their velocities) decays over this time, so no part of the clip is cut — to play less of it,
19
+ * give it an `end`. On a layer with no loop the clip HOLDS its last frame (a rock that broke stays
20
+ * broken) — `anim.stop({ fade })` is the way back to rest. */
20
21
  fadeOut?: number;
21
22
  /** Playback rate for this clip (default 1). */
22
23
  speed?: number;
23
24
  /** Rewind even if the clip is already playing (default: rewind only if it isn't). */
24
25
  restart?: boolean;
26
+ /** Play a WINDOW of the clip, seconds of the clip: enter at `start` (default 0; only when the play
27
+ * rewinds — see `restart`) and treat `end` (default the clip's end) as the end — the hand-over fires
28
+ * there, the last frame held there, events beyond it never fire. Only the swing of a longer take, the
29
+ * wind-up without the recovery. A window that lives in the clip table belongs in
30
+ * `AnimationClip.from(clip, { from, to })` instead. Not combined with `phase`. Hosts without the
31
+ * window play the whole clip. */
32
+ start?: number;
33
+ end?: number;
25
34
  /** Enter the clip at a point in the GAIT CYCLE instead of at its start: `'match'` = the phase the
26
35
  * layer shows right now (the walk's left foot is down → the turn clip starts where its left foot
27
36
  * is down too, so nothing slides), or a number 0–1. Clips without a gait cycle ignore it. */
@@ -302,6 +311,9 @@ export declare class Core {
302
311
  private clipNames;
303
312
  /** Create the native animator on demand. False = nothing to animate (warned). */
304
313
  ensure(): boolean;
314
+ /** The native animator for the RIG alone — an IK chain or a socket on a model that has no clips
315
+ * still needs the joint tree described to the engine. False = no transform hierarchy (warned). */
316
+ ensureRig(): boolean;
305
317
  destroy(): void;
306
318
  newLayer(options?: LayerOptions): LayerRec;
307
319
  addLayer(options: LayerOptions): Layer;
@@ -392,12 +404,31 @@ export declare class Core {
392
404
  /** Stride / orientation warping, as the engine's fixed-order parameter array. */
393
405
  private _warp?;
394
406
  setWarp(params: Float32Array): void;
407
+ /** The body's world velocity this frame (m/s) — the game side of the stride / orientation fit. Nothing
408
+ * to feed before the animator exists; hosts without the warp stage have no method. */
409
+ setMotion(vx: number, vy: number, vz: number): void;
395
410
  /** The feet stage (lock / ground IK), as the engine's fixed-order parameter array. */
396
411
  private _feetParams?;
397
412
  setFeetParams(params: Float32Array): void;
398
413
  /** One foot's lock state after this frame's evaluation into `out` (8 floats); false = no such foot
399
414
  * or no feet stage on this host. */
400
415
  footState(side: 0 | 1, out: Float32Array): boolean;
416
+ private static _warnedIk;
417
+ private hasIk;
418
+ /** A chain ending in `endEntity` (kind 0 two-bone: the bone, its parent, its grandparent; 1 look-at). 0 = none. */
419
+ ikCreate(kind: 0 | 1, endEntity: number): number;
420
+ ikDestroy(ik: number): void;
421
+ ikSet(ik: number, params: Float32Array): void;
422
+ ikNodes(ik: number, target: number, pole: number, rotation: number): void;
423
+ ikAnchor(ik: number, socket: string): void;
424
+ ikState(ik: number, out: Float32Array): boolean;
425
+ /** Sockets by "set/name", replayed when the animator is (re)created. */
426
+ private readonly _sockets;
427
+ /** Define a socket on joint `joint` (a bone's entity): a NODE socket (read by the engine every frame) or a
428
+ * fixed TRS in the joint's space. set "" = the live set, any other name = a donor's. */
429
+ setSocket(set: string, name: string, joint: number, node: number, trs: Float32Array | null): void;
430
+ removeSocket(set: string, name: string): void;
431
+ private pushSocket;
401
432
  private readonly _steps;
402
433
  onStepHandler(cb: StepHandler): void;
403
434
  offStepHandler(cb: StepHandler): void;
@@ -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;