@threenative/core 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +10 -0
  2. package/capabilities.json +1881 -131
  3. package/dist/assets-kyoF7JlJ.d.ts +103 -0
  4. package/dist/{audio-Dp2mXpD3.d.ts → audio-BFiGneTL.d.ts} +62 -0
  5. package/dist/canvas-layer-BLVijiUJ.d.ts +62 -0
  6. package/dist/{game-CYIaKhgl.d.ts → game-XGrTzapq.d.ts} +350 -164
  7. package/dist/gpu-readback-D2iRvoe9.d.ts +112 -0
  8. package/dist/hot.d.ts +5 -3
  9. package/dist/hot.js +5 -1
  10. package/dist/index.d.ts +813 -143
  11. package/dist/index.js +4548 -941
  12. package/dist/net.d.ts +65 -0
  13. package/dist/net.js +643 -0
  14. package/dist/playtest.d.ts +29 -5
  15. package/dist/playtest.js +181 -35
  16. package/dist/react.d.ts +4 -2
  17. package/dist/{canvas-layer-CtrZHgIh.d.ts → renderer-C6hqZpoG.d.ts} +237 -75
  18. package/dist/world.d.ts +203 -4
  19. package/dist/world.js +2536 -25
  20. package/gpl/LICENSE.GPL +117 -0
  21. package/gpl/convert.py +192 -0
  22. package/gpl/recipes/_common.py +169 -0
  23. package/gpl/recipes/bake_ao.py +111 -0
  24. package/gpl/recipes/decimate.py +64 -0
  25. package/gpl/recipes/retarget.py +131 -0
  26. package/gpl/recipes/unwrap.py +71 -0
  27. package/mcp/blender-server.mjs +632 -0
  28. package/mcp/blender.mjs +27 -0
  29. package/mcp/engine-server.mjs +271 -23
  30. package/mcp/engine.mjs +15 -7
  31. package/mcp/install.d.mts +37 -0
  32. package/mcp/install.mjs +94 -26
  33. package/mcp/servers.d.mts +34 -0
  34. package/mcp/servers.mjs +110 -9
  35. package/package.json +36 -8
  36. package/patches/three@0.185.1.patch +249 -14
  37. package/scripts/ensure-mcp.mjs +20 -13
  38. package/scripts/bundle-engine-mcp.mjs +0 -15
  39. package/scripts/generate-version.mjs +0 -13
package/dist/index.d.ts CHANGED
@@ -1,18 +1,23 @@
1
1
  import * as three from 'three';
2
- import { AnimationMixer, AnimationClip, Object3D, Camera, Vector3, Group, Matrix4, Mesh, BufferGeometry, Material, InstancedMesh, Box3, Scene, Sprite, DataTexture, CatmullRomCurve3, Texture } from 'three';
3
- import { a as IGamePluginRuntime, b as IGamePluginHooks } from './game-CYIaKhgl.js';
4
- export { C as ContextMenuPolicy, c as IAssetLoader, d as IAssetLoaderOptions, e as ICtx, I as IGame, f as IGameObservationContribution, g as IGameObservationSampleRequest, h as IGamePlatformSource, i as IInputAction, j as IInputGamepad, k as IPointerDragHandle, l as IPointerEvent3D, m as IPointerEvents3D, n as IPointerEvents3DOptions, o as IPointerEvents3DPicker, p as IPointerState, q as IRandom, r as IRawInputPointer, s as IRawInputPointerEdge, t as IRawInputState, u as IRaycastOptions, v as IScenePickerOptions, w as IThreeNativeBootSplash, x as IThreeNativeConfig, y as IThreeNativeIconVariants, z as IThreeNativeTexturesConfig, A as ITweenOptions, B as IWarmUpOptions, D as IWarmUpProgress, E as IWarmUpRenderer, F as IWarmUpReport, G as InputBindings, H as InputPlatformSource, P as PointerEvent3DListener, J as PointerEvent3DType, K as PointerEvents3D, S as Scene, L as SceneFrame, M as ScenePicker, N as ScheduleHandle, O as Scheduler, T as ThreeNativeBackgroundMode, Q as ThreeNativeOrientation, R as ThreeNativeUiRenderer, U as createAssetLoader, V as createRandom, W as defineGame, X as warmUpScene } from './game-CYIaKhgl.js';
5
- export { A as AudioBus, I as IAudioBusOptions, b as IAudioPlayOptions } from './audio-Dp2mXpD3.js';
6
- import { I as IRendererLike } from './canvas-layer-CtrZHgIh.js';
7
- export { C as CanvasLayer, F as FRAME_BUDGET_MARKER, a as FRAME_BUDGET_PHASES, b as FRAME_HITCH_MARKER, c as FrameBudget, d as FrameBudgetPhase, e as IFrameBudgetOptions, f as IFrameBudgetSummary, g as IFrameBudgetWindow, h as IFramePhaseSample, i as IRenderChainApplied, j as IRenderChainBudgetWindow, k as IRenderChainDroppedStage, l as IRenderChainOptions, m as IRenderChainRenderer, n as IRenderChainRequest, o as IRenderChainStage, p as IRenderChainStageContext, q as IRenderChainVelocityMeasurement, r as IRenderChainVelocityReport, s as IRenderChainVelocityRequest, t as IRenderChainVelocityResult, u as IVelocityRenderPass, R as RENDER_CHAIN_MARKER, v as RENDER_CHAIN_STAGE_ORDER, w as RENDER_CHAIN_TIERS, x as RenderChain, y as RenderChainSource, z as RenderChainStageName, A as RenderChainTier, B as RenderChainTierRequest, D as RenderChainVelocitySource, V as VELOCITY_OUTPUT_NAME, E as VELOCITY_PREVIOUS_BONE_MATRICES, G as VELOCITY_PREVIOUS_INSTANCE_MATRICES, H as VELOCITY_PREVIOUS_WORLD_MATRIX, J as VelocityTracker, K as ensureVelocityOutput, L as prewarm, M as readRenderChainObservation, N as readRenderChainReport, O as readVelocityPreviousBoneMatrices, P as readVelocityPreviousMatrices, Q as readVelocityPreviousWorldMatrix, S as velocityTexture, T as withVelocityContext } from './canvas-layer-CtrZHgIh.js';
2
+ import { AnimationMixer, Object3D, AnimationClip, Camera, Vector3, Group, Matrix4, Mesh, BufferGeometry, Material, InstancedMesh, ColorRepresentation, Box3, Scene, DirectionalLight, Sprite, DataTexture, CatmullRomCurve3, Texture } from 'three';
3
+ export { I as IAssetLoader, a as IAssetLoaderOptions, c as createAssetLoader, r as reconcileMirroredClips } from './assets-kyoF7JlJ.js';
4
+ export { A as AudioBus, I as IAudioBusOptions, b as IAudioPlayOptions } from './audio-BFiGneTL.js';
5
+ export { C as CanvasLayer } from './canvas-layer-BLVijiUJ.js';
6
+ import { a as IGamePluginRuntime, b as IGamePluginHooks } from './game-XGrTzapq.js';
7
+ export { A as AfterPhysicsCallback, C as ContextMenuPolicy, c as ICtx, I as IGame, d as IGameObservationContribution, e as IGameObservationSampleRequest, f as IGamePlatformSource, g as IInputAction, h as IInputGamepad, i as IPointerDragHandle, j as IPointerEvent3D, k as IPointerEvents3D, l as IPointerEvents3DOptions, m as IPointerEvents3DPicker, n as IPointerState, o as IRandom, p as IRawInputPointer, q as IRawInputPointerEdge, r as IRawInputState, s as IRaycastOptions, t as IScenePickerOptions, u as IThreeNativeAudioConfig, v as IThreeNativeAudioLoop, w as IThreeNativeAudioOverride, x as IThreeNativeAudioSpectrum, y as IThreeNativeBootSplash, z as IThreeNativeConfig, B as IThreeNativeIconVariants, D as IThreeNativeTexturesConfig, E as ITweenOptions, F as IWarmUpCacheOptions, G as IWarmUpObservation, H as IWarmUpOptions, J as IWarmUpProgress, K as IWarmUpRenderer, L as IWarmUpReport, M as InputBindings, N as InputPlatformSource, P as PointerEvent3DListener, O as PointerEvent3DType, Q as PointerEvents3D, S as Scene, R as SceneFrame, T as ScenePicker, U as ScheduleHandle, V as Scheduler, W as ThreeNativeBackgroundMode, X as ThreeNativeOrientation, Y as ThreeNativeUiRenderer, Z as WarmUpCacheStatus, _ as WarmUpObservationStatus, $ as afterPhysics, a0 as createRandom, a1 as defineGame, a2 as warmUpScene } from './game-XGrTzapq.js';
8
8
  import * as three_webgpu from 'three/webgpu';
9
- import { StorageTexture, ComputeNode, UniformNode, Node, TextureNode, NodeMaterial, StorageBufferNode, StructTypeNode, Data3DTexture, SpriteNodeMaterial, StorageTextureNode } from 'three/webgpu';
9
+ import { StorageTexture, ComputeNode, UniformNode, Node, TextureNode, NodeMaterial, StorageBufferNode, StructTypeNode, Data3DTexture, ShadowBaseNode, NodeBuilder, NodeFrame, SpriteNodeMaterial, StorageTextureNode } from 'three/webgpu';
10
+ import { I as IRendererLike } from './renderer-C6hqZpoG.js';
11
+ export { D as DEFAULT_PIPELINE_CENSUS_LIMIT, F as FRAME_BUDGET_MARKER, a as FRAME_BUDGET_PHASES, b as FRAME_HITCH_MARKER, c as FrameBudget, d as FrameBudgetPhase, e as IFrameBudgetOptions, f as IFrameBudgetSummary, g as IFrameBudgetWindow, h as IFramePhaseSample, i as IPipelineCensus, j as IPipelineCensusCounts, k as IPipelineCensusEvent, l as IPipelineCensusOptions, m as IPipelineProvenance, n as IPipelineShaderObservation, o as IRenderChainApplied, p as IRenderChainBudgetWindow, q as IRenderChainDroppedStage, r as IRenderChainOptions, s as IRenderChainRenderer, t as IRenderChainRequest, u as IRenderChainStage, v as IRenderChainStageContext, w as IRenderChainVelocityMeasurement, x as IRenderChainVelocityReport, y as IRenderChainVelocityRequest, z as IRenderChainVelocityResult, A as IVelocityRenderPass, P as PIPELINE_CENSUS_CAPABILITY, B as PIPELINE_CENSUS_VERSION, C as PipelineCensus, E as PipelineCensusMode, G as PipelineCensusStatus, R as RENDER_CHAIN_MARKER, H as RENDER_CHAIN_STAGE_ORDER, J as RENDER_CHAIN_TIERS, K as RenderChain, L as RenderChainSource, M as RenderChainStageId, N as RenderChainStageName, O as RenderChainTier, Q as RenderChainTierRequest, S as RenderChainVelocitySource, V as VELOCITY_OUTPUT_NAME, T as VELOCITY_PREVIOUS_BONE_MATRICES, U as VELOCITY_PREVIOUS_INSTANCE_MATRICES, W as VELOCITY_PREVIOUS_WORLD_MATRIX, X as VelocityTracker, Y as createPipelineCensus, Z as ensureVelocityOutput, _ as prewarm, $ as readRenderChainObservation, a0 as readRenderChainReport, a1 as readVelocityPreviousBoneMatrices, a2 as readVelocityPreviousMatrices, a3 as readVelocityPreviousWorldMatrix, a4 as velocityTexture, a5 as withVelocityContext } from './renderer-C6hqZpoG.js';
12
+ import { I as IComputeDriven$1, a as IGPUReadbackSample } from './gpu-readback-D2iRvoe9.js';
13
+ export { C as ComputeDrivenRegistry, G as GPUReadback, b as IGPUReadbackOptions } from './gpu-readback-D2iRvoe9.js';
10
14
  import { Node as Node$1 } from 'three/src/nodes/Nodes.js';
11
15
  import 'zustand/vanilla';
12
16
 
13
17
  interface IAnimationPlayerOptions {
14
18
  readonly clips: readonly AnimationClip[];
15
19
  readonly root: Object3D;
20
+ readonly requiredClips?: readonly string[] | Readonly<Record<string, string>>;
16
21
  /**
17
22
  * Match a travelling clip's playback rate to the ground the body actually covers.
18
23
  *
@@ -49,6 +54,15 @@ interface IStrideReport {
49
54
  readonly synced: boolean;
50
55
  /** True when a rate was measured and deliberately not applied. */
51
56
  readonly overridden: boolean;
57
+ /**
58
+ * True when the clip carries no root motion and its stride was read off the feet instead.
59
+ *
60
+ * `synced: false, overridden: false` is what an idle reports, so without this a game whose walk
61
+ * cycle is silently going unmatched cannot tell itself apart from one with nothing to match. An
62
+ * in-place clip that also yields no foot plant reports `inPlace: true` with a zero
63
+ * `clipGroundSpeed`, which names the asset as the thing to fix.
64
+ */
65
+ readonly inPlace: boolean;
52
66
  }
53
67
  interface IAnimationPlayOptions {
54
68
  readonly fade?: number;
@@ -61,6 +75,7 @@ interface IAnimationPlayOptions {
61
75
  declare class AnimationPlayer {
62
76
  #private;
63
77
  readonly mixer: AnimationMixer;
78
+ readonly root: Object3D;
64
79
  constructor(options: IAnimationPlayerOptions);
65
80
  get current(): string | undefined;
66
81
  get advancedFrames(): number;
@@ -82,6 +97,33 @@ declare class AnimationPlayer {
82
97
  dispose(): void;
83
98
  }
84
99
 
100
+ type NormaliseAxis = "height" | "longest";
101
+ interface INormaliseToMetresOptions {
102
+ readonly metres: number;
103
+ readonly axis: NormaliseAxis;
104
+ /** Crown bone to use for a skinned height measurement. Defaults to a named/highest bone. */
105
+ readonly top?: Object3D | string;
106
+ }
107
+ /**
108
+ * Scale an asset to a real-world size and return the factor applied.
109
+ *
110
+ * Height for a skinned asset comes from its crown bone, not its bind-pose Box3. Longest-axis
111
+ * normalization remains a geometry measurement because it is used for rigid props and weapons.
112
+ */
113
+ declare function normaliseToMetres(object: Object3D, options: INormaliseToMetresOptions): number;
114
+
115
+ interface ISkeletalMesh3DOptions {
116
+ readonly source: Object3D;
117
+ readonly clips?: readonly AnimationClip[];
118
+ readonly requiredClips?: readonly string[] | Readonly<Record<string, string>>;
119
+ readonly size?: INormaliseToMetresOptions;
120
+ readonly strideRoot?: Object3D;
121
+ readonly strideSync?: boolean;
122
+ }
123
+ declare class SkeletalMesh3D extends AnimationPlayer {
124
+ constructor(options: ISkeletalMesh3DOptions);
125
+ }
126
+
85
127
  type BillboardLockAxis = "x" | "y" | "z";
86
128
  interface IBillboard3DOptions {
87
129
  /** Camera whose view direction or position the object follows. */
@@ -216,6 +258,7 @@ declare function zenithTransmittance(parameters: IAtmosphereParameters | IResolv
216
258
  declare function directionalTransmittance(parameters: IAtmosphereParameters | IResolvedAtmosphereParameters, direction: Vector3): Vector3;
217
259
  /** Calculate solar elevation and azimuth from time, latitude, and longitude.
218
260
  * @situation move a sun across a real day at a game's latitude and longitude
261
+ * @situation run a day and night cycle over the game's sky
219
262
  * @constraint dates are interpreted as UTC unless utcOffset is supplied; no fixed sun direction is assumed
220
263
  * @constraint pass a mutable { azimuth, elevation } target to reuse the result object in a steady frame loop
221
264
  * @example const sun = solarPosition({ date, latitude: 49.28, longitude: -123.12, utcOffset: -8 });
@@ -277,7 +320,7 @@ declare class AtmosphereLuts {
277
320
  type IAtmosphereParametersLike = IResolvedAtmosphereParameters | IAtmosphereParameters;
278
321
 
279
322
  /** The structural contract consumed by the compute registry from PRD-242. */
280
- interface IComputeDriven$1 {
323
+ interface IComputeDriven {
281
324
  readonly warmupNodes: readonly unknown[];
282
325
  attachRenderer(renderer: IRendererLike): void;
283
326
  readonly processCadence?: "fixed" | "render";
@@ -299,12 +342,13 @@ type AtmosphereDirection = Vector3 | readonly [number, number, number] | Node<"v
299
342
  * those, and the same object remains useful when a game supplies a completely different look.
300
343
  * @situation render a sunrise that changes as time and place change
301
344
  * @situation add distance haze from the depth of a scene pass
345
+ * @alias bright sky saturated green platforms
302
346
  * @constraint supply rayleigh, mie, ozone, planetRadius, and atmosphereRadius; there is no Earth fallback
303
347
  * @constraint the game creates the sky object, surface, and sun from the returned nodes
304
348
  * @example const atmosphere = new Atmosphere({ rayleigh, mie, ozone, planetRadius, atmosphereRadius });
305
349
  * ctx.add(atmosphere);
306
350
  */
307
- declare class Atmosphere extends Group implements IComputeDriven$1 {
351
+ declare class Atmosphere extends Group implements IComputeDriven {
308
352
  #private;
309
353
  readonly luts: AtmosphereLuts;
310
354
  constructor(options: IAtmosphereOptions);
@@ -341,114 +385,6 @@ declare class Atmosphere extends Group implements IComputeDriven$1 {
341
385
  */
342
386
  declare function solarPositionAt(date: Date | string, latitude: number, longitude: number): ISolarPosition;
343
387
 
344
- /**
345
- * The lifecycle contract for a game-owned GPU simulation.
346
- *
347
- * A compute-driven object owns its kernels, buffers, and appearance. The framework only attaches
348
- * the active renderer, warms the kernels before the world is shown, dispatches process calls at
349
- * the object's declared cadence, and releases the object when its scene ends.
350
- */
351
- interface IComputeDriven {
352
- /** Kernels to compile before the world is shown. Read once, at attach. */
353
- readonly warmupNodes: readonly unknown[];
354
- attachRenderer(renderer: IRendererLike): void;
355
- /**
356
- * The loop phase that dispatches `process`. Defaults to fixed-step; render cadence preserves the
357
- * existing behavior of consumers whose simulation is intentionally tied to presentation.
358
- */
359
- readonly processCadence?: "fixed" | "render";
360
- /** Dispatched once per fixed step, in scene-add order unless render cadence is declared. */
361
- process(renderer: IRendererLike): void;
362
- detach(): void;
363
- readonly released: boolean;
364
- }
365
- /** The ordered registry used by the game loop for all compute-driven scene objects. */
366
- declare class ComputeDrivenRegistry {
367
- #private;
368
- get size(): number;
369
- /** Attach and remember one object. Re-adding the same object is idempotent. */
370
- add(object: Object3D & IComputeDriven, renderer: IRendererLike): void;
371
- /** Release one object without disturbing the order of the remaining objects. */
372
- remove(driven: IComputeDriven): void;
373
- /** Kernels in the same order as their objects were added to the scene. */
374
- get warmupNodes(): readonly unknown[];
375
- /** Dispatch fixed-step objects once; detached scene children are released before dispatch. */
376
- process(renderer: IRendererLike): void;
377
- /** Dispatch render-cadence objects once; detached scene children are released before dispatch. */
378
- processRender(renderer: IRendererLike): void;
379
- /** Release every registered object, continuing after a failure so no resource is stranded. */
380
- clear(): void;
381
- }
382
-
383
- interface IGPUReadbackOptions {
384
- /** The GPU storage attribute to copy — a TSL storage node's `.value`. */
385
- readonly attribute: unknown;
386
- /**
387
- * Frames between readback requests. `1` asks every frame; larger values throttle.
388
- *
389
- * A copy off the GPU costs a queue submission and a mapped buffer, so a game reading a field it
390
- * only consults for physics asks for it every few frames and pays the staleness instead.
391
- */
392
- readonly everyFrames: number;
393
- }
394
- /** A landed copy of GPU memory, with the age of the frame that produced it. */
395
- interface IGPUReadbackSample {
396
- readonly data: Float32Array;
397
- /**
398
- * Frames between the frame whose GPU state these bytes hold and the frame reading them.
399
- *
400
- * This is the number a caller must not be allowed to ignore. A buoyancy solver that treats a
401
- * 4-frame-old surface as this frame's surface floats a hull through the water it is drawn on,
402
- * and nothing in the frame says so.
403
- */
404
- readonly staleFrames: number;
405
- }
406
- /**
407
- * A throttled, fire-and-forget copy of a GPU buffer into CPU memory.
408
- *
409
- * Every sample it hands back carries its own age. That is the whole point: the copy is
410
- * asynchronous, so the bytes are always some frames behind the GPU, and a class that hid that
411
- * would let a caller mistake stale data for live data with no way to find out.
412
- *
413
- * `request()` never awaits and never blocks the frame. One copy is in flight at a time; requests
414
- * made while one is pending are dropped rather than queued, because a backlog of copies of a field
415
- * that has already moved on is latency with no information in it.
416
- */
417
- declare class GPUReadback {
418
- #private;
419
- readonly everyFrames: number;
420
- constructor(options: IGPUReadbackOptions);
421
- get released(): boolean;
422
- /** True while a copy is in flight. A game that wants to pace its own work can read it. */
423
- get pending(): boolean;
424
- /** The newest landed bytes, or `undefined` before the first copy lands. */
425
- get data(): Float32Array | undefined;
426
- /**
427
- * How many frames old the landed bytes are.
428
- *
429
- * Before anything has landed this is the number of frames since construction, which grows
430
- * without bound on purpose: "no data yet" and "data from frame zero" must not read the same.
431
- */
432
- get staleFrames(): number;
433
- /** The newest bytes with their age attached, or `undefined` before the first copy lands. */
434
- get sample(): IGPUReadbackSample | undefined;
435
- /** Requests, landings and failures, for a report that has to say why a sample is old. */
436
- get stats(): {
437
- readonly requests: number;
438
- readonly lands: number;
439
- readonly failures: number;
440
- };
441
- /**
442
- * Advances the frame clock and starts a copy when the throttle allows one.
443
- *
444
- * Safe to call every frame. It returns before the GPU has answered — awaiting it is the one
445
- * thing that would turn this class into the stall it exists to avoid.
446
- */
447
- request(renderer: IRendererLike): void;
448
- /** Drops the attribute reference and the landed bytes. Further requests throw. */
449
- dispose(): void;
450
- }
451
-
452
388
  interface IClothTopologyOptions {
453
389
  /** Original geometry vertex indices that never move. Duplicate positions pin together. */
454
390
  readonly pinned: readonly number[];
@@ -493,7 +429,7 @@ interface ISoftBody3DOptions extends IClothTopologyOptions {
493
429
  * @override readbackEveryFrames enables an explicitly stale CPU position sample; zero disables it
494
430
  * @example const flag = new SoftBody3D(flagMesh, { pinned: topEdge, stiffness: 35, damping: 1.8, gravity: [0, -9.81, 0], wind: [1.5, 0, 0.4] });
495
431
  */
496
- declare class SoftBody3D extends Mesh<BufferGeometry, NodeMaterial> implements IComputeDriven {
432
+ declare class SoftBody3D extends Mesh<BufferGeometry, NodeMaterial> implements IComputeDriven$1 {
497
433
  #private;
498
434
  readonly processCadence: "fixed";
499
435
  readonly warmupNodes: readonly ComputeNode[];
@@ -542,7 +478,7 @@ declare const rayStruct: StructTypeNode;
542
478
  * exports through the core entry point and uses these four named nodes in its own `src/render/`
543
479
  * kernel. The snapshot is static until `rebuild()`.
544
480
  */
545
- declare class GPUSceneBVH extends Group implements IComputeDriven {
481
+ declare class GPUSceneBVH extends Group implements IComputeDriven$1 {
546
482
  #private;
547
483
  readonly indices: StorageBufferNode<"uvec3">;
548
484
  readonly nodes: StorageBufferNode<"struct">;
@@ -640,6 +576,37 @@ declare class InstancedBatch {
640
576
  build(options?: IInstancedBatchBuildOptions): InstancedMesh | undefined;
641
577
  }
642
578
 
579
+ /**
580
+ * One piece on its way into a merged buffer.
581
+ *
582
+ * A `Mesh` is already one of these — pass the meshes straight in and their own transforms are
583
+ * used. Every value here is the game's: the shape, where it sits, and what colour it is.
584
+ */
585
+ interface IMergePart {
586
+ /** The piece's own colour, written flat across its vertices. Omit it on every part for none. */
587
+ readonly color?: ColorRepresentation;
588
+ /** The shape. It is cloned before anything is done to it, so the game's copy is untouched. */
589
+ readonly geometry: BufferGeometry;
590
+ /** Where the piece sits. A `Mesh` brings its own; identity when there is none. */
591
+ readonly matrix?: Matrix4;
592
+ }
593
+ interface IMergePartsOptions {
594
+ /** Named in the error when the merge is refused. Say what was being built. */
595
+ readonly label: string;
596
+ }
597
+ /**
598
+ * Merge game-authored pieces into one buffer, keeping each piece's own colour.
599
+ *
600
+ * Two things go wrong every time an agent bakes a building, a ship or a character out of
601
+ * primitives, and neither is about how any of it looks. `mergeGeometries` returns `null` on
602
+ * mismatched inputs instead of throwing, and the usual mismatch — one non-indexed extrusion among
603
+ * a hundred indexed primitives — is invisible until the whole scene is missing; and a merged
604
+ * buffer draws with one surface, so per-piece colour is gone unless every piece carries a flat
605
+ * `color` attribute written before the merge. Writing that attribute is mechanical. The colours
606
+ * are entirely the game's, one per part, and changing them changes nothing here.
607
+ */
608
+ declare function mergeParts(parts: Iterable<IMergePart>, options: IMergePartsOptions): BufferGeometry;
609
+
643
610
  /**
644
611
  * A mesh that draws only the clusters this camera can resolve.
645
612
  *
@@ -809,7 +776,7 @@ declare class ClusteredBatch {
809
776
  declare const ATLAS_PADDING = 1;
810
777
  /** The machine-readable marker emitted whenever the probe state changes. */
811
778
  declare const PROBE_VOLUME_MARKER = "TN_PROBE_VOLUME";
812
- type IVector3Like = {
779
+ type IVector3Like$1 = {
813
780
  readonly x: number;
814
781
  readonly y: number;
815
782
  readonly z: number;
@@ -822,12 +789,12 @@ interface IProbeVolumeCoefficient {
822
789
  readonly b: number;
823
790
  }
824
791
  /** Probe density in probes per world unit, either isotropic or per-axis. */
825
- type ProbeVolumeDensity = number | readonly [number, number, number] | IVector3Like;
792
+ type ProbeVolumeDensity = number | readonly [number, number, number] | IVector3Like$1;
826
793
  interface IProbeVolumeOptions {
827
794
  /** World-space bounds; the volume does not move with the object after construction. */
828
795
  readonly bounds: Box3 | {
829
- readonly min: IVector3Like;
830
- readonly max: IVector3Like;
796
+ readonly min: IVector3Like$1;
797
+ readonly max: IVector3Like$1;
831
798
  };
832
799
  /** Probe spacing expressed as probes per world unit. */
833
800
  readonly density: ProbeVolumeDensity;
@@ -891,7 +858,7 @@ declare function readProbeVolumeObservation(value: unknown): IProbeVolumeObserva
891
858
  * Bakes are static-lighting-first and explicit. Call `requestBake(scene)` after lights and static
892
859
  * geometry are authored; a completed bake is reused until the game requests another one.
893
860
  */
894
- declare class ProbeVolume extends Object3D implements IComputeDriven {
861
+ declare class ProbeVolume extends Object3D implements IComputeDriven$1 {
895
862
  #private;
896
863
  readonly isProbeVolume = true;
897
864
  readonly processCadence: "render";
@@ -943,6 +910,434 @@ declare class ProbeVolume extends Object3D implements IComputeDriven {
943
910
  probePosition(ix: number, iy: number, iz: number, target?: Vector3): Vector3;
944
911
  }
945
912
 
913
+ /**
914
+ * Renderer-independent bookkeeping for virtual shadow pages.
915
+ *
916
+ * Four pieces, none of which know a GPU exists: stable virtual page addresses and a bounded
917
+ * physical page pool with LRU eviction (`PhysicalPagePool`), camera-centred clip windows snapped
918
+ * to whole pages in a light-space basis (`DirectionalClipmap`), the pages a frame needs from
919
+ * receiver points and visible caster bounds (`ReceiverDemandPass`), and the pages a moving caster
920
+ * dirties (`ShadowInvalidationTracker`). `VirtualShadowNode` in `virtual-shadow.ts` is the only
921
+ * consumer; everything here is pure so its rules are provable in a node-environment spec.
922
+ *
923
+ * Fail closed: malformed input throws at the boundary instead of producing a page that samples
924
+ * nothing.
925
+ */
926
+ /** A plain `{x, y, z}` triple; three's `Vector3` satisfies it without a copy. */
927
+ interface IVector3Like {
928
+ readonly x: number;
929
+ readonly y: number;
930
+ readonly z: number;
931
+ }
932
+ /** An axis-aligned world-space box as two corners. */
933
+ interface IBoundsLike {
934
+ readonly min: IVector3Like;
935
+ readonly max: IVector3Like;
936
+ }
937
+ /** A point in the light-space basis: `u` and `v` across the light, `w` toward it. */
938
+ interface ILightSpacePoint {
939
+ readonly u: number;
940
+ readonly v: number;
941
+ readonly w: number;
942
+ }
943
+ /** The address of one virtual page: a clip level and integer page coordinates. */
944
+ interface IVirtualPageAddress {
945
+ readonly level: number;
946
+ readonly x: number;
947
+ readonly y: number;
948
+ readonly key: string;
949
+ }
950
+ /** One clip level's window of `pagesPerAxis²` virtual pages around the centre. */
951
+ interface IClipWindow {
952
+ readonly level: number;
953
+ readonly extent: number;
954
+ readonly pageWorldSize: number;
955
+ readonly minX: number;
956
+ readonly minY: number;
957
+ readonly maxX: number;
958
+ readonly maxY: number;
959
+ }
960
+ interface IDirectionalClipmapOptions {
961
+ /** Unit-free direction *toward* the source; normalised here. */
962
+ readonly direction: IVector3Like;
963
+ /** Half-width of each level's window in world units, finest first. */
964
+ readonly clipExtents: readonly number[];
965
+ readonly pagesPerAxis: number;
966
+ /** Fraction of an extent inside which a point selects that level; `(0, 1]`. */
967
+ readonly selectionGuard?: number;
968
+ }
969
+ /**
970
+ * Camera-centred clip windows in a light-space basis, snapped to whole pages.
971
+ *
972
+ * The basis is right-handed (`U × V = W`). A page camera placed along `+W` with `up = V` then
973
+ * renders screen X = `V × W` = `+U`, which is the axis the sampler reads the atlas along. The
974
+ * prototype this was ported from built `V = U × W` and every page came out mirrored.
975
+ */
976
+ declare class DirectionalClipmap {
977
+ #private;
978
+ readonly clipExtents: readonly number[];
979
+ readonly pagesPerAxis: number;
980
+ readonly selectionGuard: number;
981
+ readonly levelCount: number;
982
+ basisU: IVector3Like;
983
+ basisV: IVector3Like;
984
+ basisW: IVector3Like;
985
+ centerWorld: IVector3Like;
986
+ centerLight: ILightSpacePoint;
987
+ constructor({ direction, clipExtents, pagesPerAxis, selectionGuard, }: IDirectionalClipmapOptions);
988
+ /** Re-orient the basis; returns true when it changed enough that cached pages are stale. */
989
+ setDirection(direction: IVector3Like): boolean;
990
+ project(worldPoint: IVector3Like): ILightSpacePoint;
991
+ unproject({ u, v, w }: {
992
+ u: number;
993
+ v: number;
994
+ w?: number;
995
+ }): IVector3Like;
996
+ updateCenter(worldPoint: IVector3Like): readonly IClipWindow[];
997
+ getWindow(level: number): IClipWindow;
998
+ pageWorldSize(level: number): number;
999
+ containsPage(level: number, x: number, y: number): boolean;
1000
+ worldToPage(worldPoint: IVector3Like, level: number): IVirtualPageAddress;
1001
+ /** The finest level whose guarded extent contains the point, else the coarsest. */
1002
+ selectLevel(worldPoint: IVector3Like): number;
1003
+ /** Light-space extent and world centre (at `w = 0`) of one page. */
1004
+ pageBounds(level: number, x: number, y: number): {
1005
+ level: number;
1006
+ x: number;
1007
+ y: number;
1008
+ minU: number;
1009
+ minV: number;
1010
+ maxU: number;
1011
+ maxV: number;
1012
+ centerU: number;
1013
+ centerV: number;
1014
+ pageWorldSize: number;
1015
+ centerWorld: IVector3Like;
1016
+ };
1017
+ boundsToPageRange(bounds: IBoundsLike, level: number): {
1018
+ minX: number;
1019
+ maxX: number;
1020
+ minY: number;
1021
+ maxY: number;
1022
+ };
1023
+ boundsToPageKeys(bounds: IBoundsLike, level: number): string[];
1024
+ windowPages(level: number): IVirtualPageAddress[];
1025
+ }
1026
+ /**
1027
+ * Remembers each tracked caster's last bounds and dirties every page the old and new bounds
1028
+ * cover on every level when they change. Unchanged bounds dirty nothing.
1029
+ */
1030
+ declare class ShadowInvalidationTracker {
1031
+ #private;
1032
+ readonly clipmap: DirectionalClipmap;
1033
+ constructor(clipmap: DirectionalClipmap);
1034
+ get trackedCount(): number;
1035
+ /** Record a caster's current bounds; returns true when pages were dirtied. */
1036
+ update(id: number | string, bounds: IBoundsLike): boolean;
1037
+ has(id: number | string): boolean;
1038
+ remove(id: number | string): boolean;
1039
+ /** Dirty the current coverage of every tracked caster; returns the pending key count. */
1040
+ invalidateAll(): number;
1041
+ /** Drop trackers whose id is not in `liveIds`, dirtying the pages they covered. */
1042
+ prune(liveIds: ReadonlySet<number | string>): number;
1043
+ consumeInvalidatedKeys(): Set<string>;
1044
+ clear(): void;
1045
+ }
1046
+
1047
+ /**
1048
+ * Options for {@link VirtualShadowNode}. Every value is a mechanism parameter; the light's own
1049
+ * `shadow` keeps bias, normalBias, intensity, map type and filter, exactly as with a stock shadow.
1050
+ */
1051
+ interface IVirtualShadowOptions {
1052
+ /**
1053
+ * Half-width of each clip level's window in world units, finest first and strictly
1054
+ * increasing. Default `[16, 48, 144]`: three windows, each three times wider than the last.
1055
+ */
1056
+ readonly clipExtents?: readonly number[];
1057
+ /** Texels per level edge. Default: the light's `shadow.mapSize.width`. */
1058
+ readonly mapSize?: number;
1059
+ /**
1060
+ * Texels per edge of each level's mover map — the map tracked casters draw into every frame.
1061
+ * Default: half of `mapSize`, never below 256. Movers are few and close, so half the texels
1062
+ * over the same window reads as the same shadow at a quarter of the fill.
1063
+ */
1064
+ readonly moverMapSize?: number;
1065
+ /**
1066
+ * Fraction of a level's extent inside which a fragment still selects that level, `(0, 1]`,
1067
+ * default 0.9 — the outer ring falls through to the next level so the edge is never sampled.
1068
+ */
1069
+ readonly selectionGuard?: number;
1070
+ /** How far behind the window centre each level camera sits, in world units. Default 200. */
1071
+ readonly lightDistance?: number;
1072
+ /** Depth range each level camera covers past its centre, in world units. Default 400. */
1073
+ readonly depthRange?: number;
1074
+ /** Print the `TN_VIRTUAL_SHADOW` line every `markerEvery` frames; `false` silences it. Default 300. */
1075
+ readonly marker?: boolean | number;
1076
+ }
1077
+ /** Per-frame counters, readable any time through {@link VirtualShadowNode.stats}. */
1078
+ interface IVirtualShadowStats {
1079
+ readonly frame: number;
1080
+ readonly levels: number;
1081
+ /** Levels whose window moved this frame and were re-rendered. */
1082
+ readonly moved: number;
1083
+ /** Levels re-rendered because `invalidateAll()` or explicit tracker invalidation asked for it. */
1084
+ readonly invalidated: number;
1085
+ /** Tracked casters, as of this frame. */
1086
+ readonly movers: number;
1087
+ /** Mover maps rendered this frame: one per level when at least one caster is tracked. */
1088
+ readonly moverRenders: number;
1089
+ /** Levels served from their cached map this frame. */
1090
+ readonly cached: number;
1091
+ /** Levels rendered this frame, for any reason. */
1092
+ readonly rendered: number;
1093
+ /** Fraction of levels served from cache over the node's lifetime. */
1094
+ readonly reuseRatio: number;
1095
+ }
1096
+ declare const VIRTUAL_SHADOW_MARKER = "TN_VIRTUAL_SHADOW";
1097
+ /**
1098
+ * The object layer tracked casters are enabled on, so each level's mover camera sees only them.
1099
+ * Keep it free of other uses; the main camera never needs it (tracked objects keep layer 0).
1100
+ */
1101
+ declare const VIRTUAL_SHADOW_MOVER_LAYER = 29;
1102
+ /**
1103
+ * One directional shadow for a whole open world: camera-centred clip levels, each snapped to its
1104
+ * own texel grid and re-rendered only when its window moves. Movers never touch that cache: a
1105
+ * tracked caster draws into a second, per-level mover map every frame, and a fragment takes the
1106
+ * darker of the two — so a walking stag costs one small render of itself, not a render of the wood.
1107
+ *
1108
+ * Plugs into three's own slot, so every material in the scene receives it with no other change:
1109
+ * `light.shadow.shadowNode = new VirtualShadowNode(light, { clipExtents: [16, 48, 144] })`.
1110
+ *
1111
+ * What it owns is mechanism: level windows, texel snapping, per-level caching, invalidation,
1112
+ * level selection and the statistics. The light's `shadow` keeps bias, normal bias, intensity,
1113
+ * map type and filter, and those source settings are mirrored into each stock level node before
1114
+ * rendering. Per-level map sizes, cameras, `autoUpdate` and `needsUpdate` are owned by this node.
1115
+ * Each level is rendered by the stock {@link ShadowNode} through the renderer's shadow-map type,
1116
+ * so the look is the same code path a plain shadow uses.
1117
+ *
1118
+ * Ported from the virtual-shadow-map prototype's clipmap and invalidation; the sparse page atlas
1119
+ * is deliberately not the first cut — a page needs the scene rendered once per page, and on a
1120
+ * forest of hundreds of instanced meshes three level renders are cheaper than twenty-four page
1121
+ * renders. The page pool and demand pass in `virtual-shadow-pages.ts` stay ready for it.
1122
+ *
1123
+ * @situation crisp shadows close to the player across a large outdoor level
1124
+ * @situation shadow map too coarse over a big terrain
1125
+ * @situation one directional light shadow for a whole open world
1126
+ * @situation shadows shimmer when the camera moves
1127
+ * @constraint the light must be a DirectionalLight with `castShadow` and a target in the scene
1128
+ * @constraint clipExtents are half-widths in world units, finest first, strictly increasing
1129
+ * @constraint call `trackCaster(object)` for movers; it enables layer `VIRTUAL_SHADOW_MOVER_LAYER` on the object and its descendants, tracking or untracking refreshes cached levels once, and subsequent mover movement refreshes only when a window moves
1130
+ * @override bias, biasNode, normalBias, intensity, radius, blurSamples, mapType and filterNode stay on `light.shadow`; mapSize and the other options here have defaults
1131
+ * @example
1132
+ * const sun = new DirectionalLight(0xffffff, 3);
1133
+ * sun.castShadow = true;
1134
+ * sun.shadow.shadowNode = new VirtualShadowNode(sun, { clipExtents: [12, 40, 120] });
1135
+ */
1136
+ declare class VirtualShadowNode extends ShadowBaseNode {
1137
+ #private;
1138
+ static get type(): string;
1139
+ readonly options: Required<Omit<IVirtualShadowOptions, "marker">> & {
1140
+ readonly markerEvery: number;
1141
+ };
1142
+ readonly clipmap: DirectionalClipmap;
1143
+ /**
1144
+ * Compatibility handle for explicit page invalidation. Automatic caster motion uses mover maps;
1145
+ * callers that already update this tracker still invalidate the affected cached levels.
1146
+ */
1147
+ readonly tracker: ShadowInvalidationTracker;
1148
+ constructor(light: DirectionalLight, options?: IVirtualShadowOptions);
1149
+ /** The per-frame counters, as of the last `updateBefore`. */
1150
+ get stats(): IVirtualShadowStats;
1151
+ /** The stock shadow nodes behind each level, for diagnostics. */
1152
+ get levelNodes(): readonly Node[];
1153
+ /** The stock shadow nodes behind each level's mover map, for diagnostics. */
1154
+ get moverNodes(): readonly Node[];
1155
+ /** The placeholder lights, one per level; exposed for tests and debug views. */
1156
+ get levelLights(): readonly Object3D[];
1157
+ /**
1158
+ * Make an object a mover: it leaves the cached level maps and draws into every level's mover
1159
+ * map each frame, so its shadow follows it without a level render. Static geometry never
1160
+ * needs this — a level re-renders whenever its window moves anyway.
1161
+ */
1162
+ trackCaster(object: Object3D): string;
1163
+ untrackCaster(objectOrId: Object3D | string): boolean;
1164
+ /** Force every level to re-render on the next frame — a tree fell, a door opened. */
1165
+ invalidateAll(): void;
1166
+ setup(builder: NodeBuilder): Node | null | undefined;
1167
+ updateBefore(frame: NodeFrame): undefined;
1168
+ dispose(): void;
1169
+ }
1170
+ /**
1171
+ * Parse a `TN_VIRTUAL_SHADOW` console line back into its complete stats, or `undefined`.
1172
+ *
1173
+ * @situation inspect virtual shadow cache and mover counters from a renderer log
1174
+ * @constraint non-marker lines and markers with incomplete or non-numeric stats return `undefined`
1175
+ * @example
1176
+ * const stats = readVirtualShadowMarker(line);
1177
+ * if (stats !== undefined) console.log(stats.reuseRatio);
1178
+ */
1179
+ declare function readVirtualShadowMarker(line: string): IVirtualShadowStats | undefined;
1180
+
1181
+ /**
1182
+ * Two load-time mechanisms every streaming game writes by hand, and gets wrong in the same places.
1183
+ *
1184
+ * A game that shows a loading curtain and builds its world behind it has two loops that decide how
1185
+ * long the curtain stays up, and neither is about how anything looks: the loop that **fetches** the
1186
+ * models and the loop that **attaches** the finished objects to the scene. Written naively, the
1187
+ * first is serial and the second is one-object-per-frame, and both are slow for reasons that have
1188
+ * nothing to do with the game.
1189
+ *
1190
+ * Measured in a real game (a 190 m valley, ~70 GLBs, ~400 attached objects), on the same build and
1191
+ * the same content:
1192
+ *
1193
+ * | Attach loop | Detail tier |
1194
+ * | ------------ | ----------- |
1195
+ * | 1 per frame | 16.5 s |
1196
+ * | 6 per frame | 11.26 s |
1197
+ * | 24 per frame | 9.39 s |
1198
+ * | 256 per frame| **6.89 s** |
1199
+ * | all at once | 7.18 s |
1200
+ *
1201
+ * and the fetch loop: 52 models one at a time took **38.4 s**; six at a time took **8.8 s**.
1202
+ *
1203
+ * Neither of these decides how anything looks. `addInSlices` never creates an object and never
1204
+ * chooses where it goes — it is handed a list and the game's own `add`. `loadAll` never chooses
1205
+ * what to load — it is handed a list and the game's own `load`. Geometry, material, colour,
1206
+ * texture, curve and timing stay with the game in both, and a game can change its appearance
1207
+ * completely without editing either.
1208
+ *
1209
+ * What they are not is code a game can get right portably by itself, which is why they live here.
1210
+ * Yielding correctly is a platform seam: awaiting `requestAnimationFrame` deadlocks a host whose
1211
+ * frames are pumped by the code doing the waiting, racing it against a timer reorders the frame
1212
+ * sequence a harness observes, and a hard-coded `setTimeout(16)` is neither a frame on the native
1213
+ * runtime nor a frame on a machine that cannot hold 60. See `yieldToHost` in `warmup.ts` for the
1214
+ * two wrong versions that came before the one both of these use.
1215
+ */
1216
+ /** How far a sliced attachment has got, reported once per slice. */
1217
+ interface IAddInSlicesProgress {
1218
+ /** Objects attached so far. Never greater than `total`. */
1219
+ readonly added: number;
1220
+ /** Objects the run was given. Known before the first slice. */
1221
+ readonly total: number;
1222
+ }
1223
+ interface IAddInSlicesOptions {
1224
+ /**
1225
+ * Objects attached between presented frames. Default 256.
1226
+ *
1227
+ * The default is large because the reason it used to be small has been taken over by something
1228
+ * better. One per frame was right when nothing else compiled the world: the renderer built a few
1229
+ * newly visible pipelines per presented frame instead of all of them inside one multi-second
1230
+ * frame. It is not right once the framework warms the held scene (`warmUpScene`) — the compile is
1231
+ * then paid either way and the slice only chooses where, so a small slice buys nothing and costs
1232
+ * one present per object.
1233
+ *
1234
+ * 256 is where the measured curve flattens without giving up the yields entirely: over a few
1235
+ * hundred objects it still presents a handful of frames, so a browser watchdog cannot see a hung
1236
+ * page, and it costs nothing against attaching everything in one go.
1237
+ */
1238
+ readonly sliceSize?: number;
1239
+ /** Called once per slice, and once more for a partial final slice. */
1240
+ readonly onProgress?: (progress: IAddInSlicesProgress) => void;
1241
+ /**
1242
+ * How the run hands the loop back to the host between slices. Defaults to yielding one
1243
+ * macrotask, which is one native-runtime loop iteration and therefore one presented frame.
1244
+ */
1245
+ readonly yieldFrame?: () => Promise<void>;
1246
+ /**
1247
+ * Asked before every object; a false answer stops the run.
1248
+ *
1249
+ * This is how a scene that can be torn down mid-attach stays correct. A generation invalidated
1250
+ * by a restart, a scene change, or an HMR reload must stop attaching into a graph that is no
1251
+ * longer current, and it must do so **without throwing** — a throw here is indistinguishable
1252
+ * from a real attachment failure at the catch site, and games end up reporting a teardown as an
1253
+ * asset error. The stop is reported instead, as `stopped` on the report.
1254
+ */
1255
+ readonly while?: () => boolean;
1256
+ /** Silences the `TN_ADD_SLICES` marker. Never silences the report. Default true (on). */
1257
+ readonly marker?: boolean;
1258
+ }
1259
+ /** What a sliced attachment did, so a caller can report it rather than assume it. */
1260
+ interface IAddInSlicesReport {
1261
+ /** Objects actually handed to `add`. */
1262
+ readonly added: number;
1263
+ /** Objects the run was given. */
1264
+ readonly total: number;
1265
+ /** Slices the work was cut into, and therefore the frames the loop got to present, plus one. */
1266
+ readonly slices: number;
1267
+ /** Wall-clock milliseconds the run took, attaching and yielding together. */
1268
+ readonly elapsedMs: number;
1269
+ /** The slice size actually used — the default, or the one the game chose. */
1270
+ readonly sliceSize: number;
1271
+ /** Whether that slice size came from the game rather than from the default. */
1272
+ readonly sliceSizeOverridden: boolean;
1273
+ /** True when `while` ended the run early. `added` is then less than `total`. */
1274
+ readonly stopped: boolean;
1275
+ }
1276
+ /** How far a bounded-concurrency load has got, reported once per settled item. */
1277
+ interface ILoadAllProgress {
1278
+ /** Loads that have resolved. */
1279
+ readonly settled: number;
1280
+ /** Loads the run was given. Known before the first one starts. */
1281
+ readonly total: number;
1282
+ }
1283
+ interface ILoadAllOptions {
1284
+ /**
1285
+ * Loads in flight at once. Default 6.
1286
+ *
1287
+ * Bounded rather than unbounded, because a full-width fan-out coalesces its completion callbacks
1288
+ * into 100-200 ms tasks and freezes the loading screen it is filling — which reads as the hang
1289
+ * the concurrency was added to remove. Six is also the per-host connection limit a browser
1290
+ * applies to HTTP/1.1, so a larger number frequently buys queueing rather than parallelism.
1291
+ */
1292
+ readonly concurrency?: number;
1293
+ /** Called after each load settles. */
1294
+ readonly onProgress?: (progress: ILoadAllProgress) => void;
1295
+ /**
1296
+ * How the run hands the loop back to the host after each settled load. Defaults to yielding one
1297
+ * macrotask, which is what keeps a progress bar moving while the lanes are busy.
1298
+ */
1299
+ readonly yieldFrame?: () => Promise<void>;
1300
+ /** Silences the `TN_LOAD_ALL` marker. Never silences `onProgress`. Default true (on). */
1301
+ readonly marker?: boolean;
1302
+ }
1303
+ /**
1304
+ * Attach a built list of objects to the scene in slices, presenting a frame between each.
1305
+ *
1306
+ * The objects are already built and already the game's; this decides only *when* each one joins
1307
+ * the graph. Order is the list's order, and there is no option to change it — an attach loop that
1308
+ * reordered its input would change which object a positional lookup finds.
1309
+ *
1310
+ * @situation add hundreds of built objects to the scene without one multi-second frame
1311
+ * @situation stream a detail tier in behind a loading curtain without the page looking hung
1312
+ * @example
1313
+ * const report = await addInSlices(detailObjects, (object) => ctx.add(object), {
1314
+ * onProgress: ({ added, total }) => setProgress(added / total),
1315
+ * while: () => generation.live,
1316
+ * });
1317
+ */
1318
+ declare function addInSlices<T>(objects: Iterable<T>, add: (object: T, index: number) => void, options?: IAddInSlicesOptions): Promise<IAddInSlicesReport>;
1319
+ /**
1320
+ * Load a list with bounded concurrency, and hand the results back **in the input's order**.
1321
+ *
1322
+ * The ordering is the whole point and has no override. `Promise.all` already keeps order but runs
1323
+ * everything at once; a hand-rolled worker pool bounds the lanes but pushes results as they land,
1324
+ * so the array comes back in completion order — whatever the network happened to return. A game
1325
+ * that picks from that list positionally then places a different model in the same spot on every
1326
+ * load, and its world is never the same twice. That defect shipped in a real game and is why this
1327
+ * writes each result to its item's own index and never appends.
1328
+ *
1329
+ * Fails closed like `Promise.all`: the first rejection rejects the call, and no lane starts a load
1330
+ * it had not already begun.
1331
+ *
1332
+ * @situation load many models or textures in parallel instead of one at a time
1333
+ * @situation keep a loading screen moving while a list of assets downloads
1334
+ * @example
1335
+ * const species = await loadAll(names, (name) => ctx.assets.model(`flora/${name}.glb`), {
1336
+ * onProgress: ({ settled, total }) => setProgress(settled / total),
1337
+ * });
1338
+ */
1339
+ declare function loadAll<TIn, TOut>(items: readonly TIn[], load: (item: TIn, index: number) => Promise<TOut>, options?: ILoadAllOptions): Promise<TOut[]>;
1340
+
946
1341
  /** One band of the spectrum, drawn on its own patch. */
947
1342
  interface ISpectralOceanCascade {
948
1343
  /** The world-space edge length, in metres, this cascade's grid tiles across. */
@@ -1021,7 +1416,7 @@ interface ISpectralOceanHeight {
1021
1416
  * needs the height to be exact wants an analytic field instead — that is a different contract, and
1022
1417
  * the reason this class has a different name rather than a flag.
1023
1418
  */
1024
- declare class SpectralOcean extends Object3D implements IComputeDriven {
1419
+ declare class SpectralOcean extends Object3D implements IComputeDriven$1 {
1025
1420
  #private;
1026
1421
  readonly resolution: number;
1027
1422
  readonly cascades: readonly ISpectralOceanCascade[];
@@ -1064,7 +1459,7 @@ interface IGPUParticles3DOptions {
1064
1459
  readonly start: (buffers: IGPUParticles3DBuffers) => ComputeNode;
1065
1460
  readonly process: (buffers: IGPUParticles3DBuffers) => ComputeNode;
1066
1461
  }
1067
- declare class GPUParticles3D extends Sprite implements IComputeDriven {
1462
+ declare class GPUParticles3D extends Sprite implements IComputeDriven$1 {
1068
1463
  #private;
1069
1464
  readonly amount: number;
1070
1465
  readonly buffers: IGPUParticles3DBuffers;
@@ -1148,6 +1543,17 @@ interface IWaveFieldWave {
1148
1543
  readonly speed: number;
1149
1544
  readonly phase?: number;
1150
1545
  readonly steepness?: number;
1546
+ /**
1547
+ * Mark this wave as detail: the graph fades it out with the `fade` node passed to
1548
+ * `heightNode` / `normalNode`, and leaves it at full amplitude everywhere else.
1549
+ *
1550
+ * A wave shorter than the distance one screen pixel covers cannot be resolved, and what it
1551
+ * produces instead is a crawling moire that reads as a repeating pattern. Fading it is the fix.
1552
+ * `sample` on the CPU has no camera and therefore no fade, so CPU and GPU height differ by at
1553
+ * most the summed amplitude of the detail waves — keep them small, or float things on the
1554
+ * non-detail waves alone.
1555
+ */
1556
+ readonly detail?: boolean;
1151
1557
  }
1152
1558
  interface IWaveFieldDomainWarp {
1153
1559
  readonly direction?: WaveDirection;
@@ -1162,6 +1568,18 @@ interface IWaveFieldOptions {
1162
1568
  readonly waves: readonly IWaveFieldWave[];
1163
1569
  readonly domainWarp?: readonly IWaveFieldDomainWarp[];
1164
1570
  }
1571
+ /** Where, when, and how finely to evaluate the field in a graph. */
1572
+ interface IWaveFieldGraphOptions {
1573
+ /**
1574
+ * The horizontal point to evaluate at, in whatever space the caller wants the answer in.
1575
+ * Defaults to this vertex's local x and z.
1576
+ */
1577
+ readonly point?: Node<"vec2">;
1578
+ /** The clock. Defaults to the field's own `time` uniform. */
1579
+ readonly time?: Node<"float">;
1580
+ /** A 0..1 multiplier on every wave marked `detail`. Omitted, detail waves stay at full size. */
1581
+ readonly fade?: Node<"float">;
1582
+ }
1165
1583
  interface IWaveFieldSample {
1166
1584
  readonly height: number;
1167
1585
  readonly normal: Vector3;
@@ -1180,8 +1598,120 @@ declare class WaveField {
1180
1598
  /** Update the default graph clock. Explicit sample times remain available for fixed-step code. */
1181
1599
  setTime(value: number): void;
1182
1600
  sample(x: number, z: number, time: number): IWaveFieldSample;
1601
+ /** Surface height at a point, as a graph. The scalar half of what `sample` returns. */
1602
+ heightNode(options?: IWaveFieldGraphOptions): Node<"float">;
1603
+ /**
1604
+ * The analytic surface normal at a point, as a graph — the same value `sample` returns, and the
1605
+ * reason a water material needs no hand-written ripple pattern.
1606
+ *
1607
+ * Differencing the height, or stamping a normal map over the surface, is what puts visible
1608
+ * repeats in water: both quantise a field that has none. This differentiates the wave sum
1609
+ * itself, so the normal repeats only where the waves do, which for wavelengths with no common
1610
+ * multiple is nowhere.
1611
+ *
1612
+ * Evaluate it per fragment — pass `point` in world XZ — and the ripples survive at any distance
1613
+ * from the camera, at the cost of the wave sum running per pixel rather than per vertex.
1614
+ */
1615
+ normalNode(options?: IWaveFieldGraphOptions): Node<"vec3">;
1183
1616
  /** Return a TSL node that displaces local vertices using the same packed values as `sample`. */
1184
- displacementNode(timeNode?: three_webgpu.UniformNode<"float", number>): three_webgpu.Node<"vec3">;
1617
+ displacementNode(timeNode?: three_webgpu.UniformNode<"float", number>): Node<"vec3">;
1618
+ }
1619
+
1620
+ /** How the mirrored pass is sized. Both numbers are cost, not appearance. */
1621
+ interface IWaterReflectionOptions {
1622
+ /**
1623
+ * The mirrored pass's render target, as a fraction of the drawing buffer.
1624
+ *
1625
+ * A reflection is a second draw of the whole world, so this is the one number that decides
1626
+ * whether a water surface is affordable. Half is the usual answer.
1627
+ */
1628
+ readonly resolutionScale: number;
1629
+ /** Whether this surface may appear in other reflectors' passes. Off is one pass; on is n². */
1630
+ readonly bounces?: boolean;
1631
+ }
1632
+ interface IWaterSurfaceOptions {
1633
+ /** World-space height of the surface, in metres. The mirror plane, and where thickness is 0. */
1634
+ readonly level: number;
1635
+ /**
1636
+ * Thickness readings saturate here, in metres.
1637
+ *
1638
+ * A required number because it is the range of the instrument, not a taste: sky behind the
1639
+ * surface has no depth at all, and something has to be reported for it.
1640
+ */
1641
+ readonly maxThickness: number;
1642
+ /** Omit for a surface that reflects nothing; `reflectionAt` then throws rather than lying. */
1643
+ readonly reflection?: IWaterReflectionOptions;
1644
+ }
1645
+ /**
1646
+ * What a horizontal water surface can see: the world mirrored in it, the world beneath it, and
1647
+ * how much water stands between the two.
1648
+ *
1649
+ * This is the render plumbing a water material needs and cannot write portably — a mirrored
1650
+ * camera and its render target, the frame's own colour read back as the light coming up through
1651
+ * the surface, and the scene's depth turned into **metres of water under this pixel**. It decides
1652
+ * nothing about how any of that looks: no colour, no absorption tint, no fresnel weighting, no
1653
+ * glint. Those are the game's, composed from the nodes below in its own `src/render/` material.
1654
+ *
1655
+ * The depth reading is the part worth having. `linearDepth` answers in a normalised 0..1 that
1656
+ * changes meaning with every camera near/far pair, so a material that subtracts two of them gets
1657
+ * a number in no unit at all, and its shoreline moves when the camera's far plane does.
1658
+ * `thicknessAt` returns metres, and metres survive the camera changing.
1659
+ *
1660
+ * ```js
1661
+ * const surface = new WaterSurface3D({ level: 0, maxThickness: 4, reflection: { resolutionScale: 0.5 } });
1662
+ * const offset = normal.xz.mul(0.02);
1663
+ * material.colorNode = mix(
1664
+ * surface.refractionAt(offset).mul(siltTint), // the game's colours,
1665
+ * surface.reflectionAt(offset), // composed by the game,
1666
+ * fresnel, // with the game's own fresnel.
1667
+ * );
1668
+ * material.opacityNode = surface.thicknessAt().div(surface.maxThickness);
1669
+ * ```
1670
+ */
1671
+ declare class WaterSurface3D {
1672
+ #private;
1673
+ readonly maxThickness: number;
1674
+ /**
1675
+ * The object whose plane is mirrored, kept outside the scene graph on purpose.
1676
+ *
1677
+ * A water level is a fact about the world, not about the mesh that happens to draw it: parent
1678
+ * the mirror to the surface mesh, as three's own examples do, and a non-uniform scale anywhere
1679
+ * up that mesh's ancestry skews the plane the reflection is taken about. This one is placed
1680
+ * from `level` and nothing else, so it stays level.
1681
+ */
1682
+ readonly target: Object3D | undefined;
1683
+ constructor(options: IWaterSurfaceOptions);
1684
+ /** The world-space height of the surface, in metres. */
1685
+ get level(): number;
1686
+ /** Move the surface — a tide, a sluice, a flooding room. The mirror plane follows. */
1687
+ setLevel(value: number): void;
1688
+ get released(): boolean;
1689
+ /**
1690
+ * The world mirrored in the surface, at an optional screen-space offset.
1691
+ *
1692
+ * The offset is where a game spends its surface normal: a still mirror takes none, and ripples
1693
+ * are the normal's horizontal part scaled by however far the game wants the reflection to slide.
1694
+ */
1695
+ reflectionAt(offset?: Node<"vec2">): Node<"vec3">;
1696
+ /**
1697
+ * The frame beneath the surface — everything already drawn this frame, read at an offset.
1698
+ *
1699
+ * An offset that lands on something **in front of** the water is refused and the fragment falls
1700
+ * back to a straight read. Without that, a rock standing in the shallows smears across the
1701
+ * water in front of it: the classic refraction bleed, and the reason a hand-rolled offset looks
1702
+ * wrong the first time every game writes one.
1703
+ */
1704
+ refractionAt(offset?: Node<"vec2">): Node<"vec3">;
1705
+ /**
1706
+ * Metres of water between this fragment and whatever is drawn behind it, clamped to
1707
+ * `maxThickness`. Zero exactly where the bed meets the surface, which is the shoreline.
1708
+ *
1709
+ * Sky behind the surface has no depth and reads as `maxThickness`, not as zero: an unbounded
1710
+ * horizon is deep water, not dry land.
1711
+ */
1712
+ thicknessAt(offset?: Node<"vec2">): Node<"float">;
1713
+ /** Drop the mirrored pass and its render target. */
1714
+ dispose(): void;
1185
1715
  }
1186
1716
 
1187
1717
  /**
@@ -1491,25 +2021,165 @@ declare class SpriteAnimator3D {
1491
2021
  setFrame(index: number): this;
1492
2022
  }
1493
2023
 
1494
- type NormaliseAxis = "height" | "longest";
1495
- interface INormaliseToMetresOptions {
1496
- readonly metres: number;
1497
- readonly axis: NormaliseAxis;
1498
- /** Crown bone to use for a skinned height measurement. Defaults to a named/highest bone. */
1499
- readonly top?: Object3D | string;
2024
+ /** Names of every bone in traversal order. Empty for an unskinned model. */
2025
+ declare function skeletonBones(root: Object3D): readonly string[];
2026
+ /** Parent a child to a named bone while preserving the child's authored world scale. */
2027
+ declare function attachToBone(root: Object3D, boneName: string, child: Object3D): Object3D;
2028
+ /** How far a bone sits from the object it is supposed to be touching. */
2029
+ interface IBoneContactReport {
2030
+ readonly bone: string;
2031
+ readonly target: string;
2032
+ /** Metres from the bone to the nearest point of the target's world bounds. Zero when inside. */
2033
+ readonly distance: number;
2034
+ readonly inside: boolean;
2035
+ readonly bonePosition: ThreePoseVector;
2036
+ readonly targetPoint: ThreePoseVector;
1500
2037
  }
1501
2038
  /**
1502
- * Scale an asset to a real-world size and return the factor applied.
2039
+ * Measure whether a named bone reaches a game object, in metres.
1503
2040
  *
1504
- * Height for a skinned asset comes from its crown bone, not its bind-pose Box3. Longest-axis
1505
- * normalization remains a geometry measurement because it is used for rigid props and weapons.
2041
+ * This turns "his hands are on the keyboard" and "he is sitting on the chair" into numbers a
2042
+ * scenario can assert. It walks the target's vertices through `measureThreePose`, so call it on
2043
+ * a check or a debug sample rather than every frame.
1506
2044
  */
1507
- declare function normaliseToMetres(object: Object3D, options: INormaliseToMetresOptions): number;
2045
+ declare function boneContact(root: Object3D, boneName: string, target: Object3D): IBoneContactReport;
1508
2046
 
1509
- /** Names of every bone in traversal order. Empty for an unskinned model. */
1510
- declare function skeletonBones(root: Object3D): readonly string[];
1511
- /** Parent a child to a named bone while preserving the child's authored world scale. */
1512
- declare function attachToBone(root: Object3D, boneName: string, child: Object3D): Object3D;
2047
+ /** One track of a clip, and whether it resolves to a real property on a root. */
2048
+ interface IClipTrackBinding {
2049
+ readonly track: string;
2050
+ /** The node the track names, or `null` when nothing under the root carries that name. */
2051
+ readonly node: string | null;
2052
+ readonly bound: boolean;
2053
+ /** Three.js's own reason the track binds nothing. `null` when it binds. */
2054
+ readonly reason: string | null;
2055
+ }
2056
+ interface IClipBindingReport {
2057
+ readonly clip: string;
2058
+ readonly tracks: number;
2059
+ readonly bound: number;
2060
+ /** Every track that drives nothing, in clip order. */
2061
+ readonly unbound: readonly IClipTrackBinding[];
2062
+ }
2063
+ interface IClipCoverageReport {
2064
+ readonly clip: string;
2065
+ readonly bones: number;
2066
+ readonly driven: readonly string[];
2067
+ /** Bones no bound track touches. They hold whatever the previous clip left them in. */
2068
+ readonly undriven: readonly string[];
2069
+ }
2070
+ interface IClipPoseSubject {
2071
+ readonly root: Object3D;
2072
+ readonly clip: AnimationClip;
2073
+ }
2074
+ interface IClipPoseErrorOptions {
2075
+ /** Measured bone name to reference bone name. Defaults to the names the two rigs share. */
2076
+ readonly bones?: Readonly<Record<string, string>>;
2077
+ /** Poses compared across the clip. Defaults to 8. */
2078
+ readonly samples?: number;
2079
+ }
2080
+ interface IBonePoseError {
2081
+ readonly bone: string;
2082
+ readonly reference: string;
2083
+ readonly meanDegrees: number;
2084
+ readonly maxDegrees: number;
2085
+ readonly maxAtSeconds: number;
2086
+ }
2087
+ interface IClipPoseErrorReport {
2088
+ readonly clip: string;
2089
+ readonly referenceClip: string;
2090
+ readonly samples: number;
2091
+ readonly meanDegrees: number;
2092
+ readonly maxDegrees: number;
2093
+ /** Every compared bone, worst mean first. */
2094
+ readonly bones: readonly IBonePoseError[];
2095
+ }
2096
+ /**
2097
+ * Report which of a clip's tracks resolve to a real property on `root`.
2098
+ *
2099
+ * A retarget that writes the wrong glTF target path produces tracks named `<bone>.undefined`:
2100
+ * they load without error, bind nothing, and the character plays its bind pose.
2101
+ */
2102
+ declare function clipTrackBindings(root: Object3D, clip: AnimationClip): IClipBindingReport;
2103
+ /**
2104
+ * Report which bones of `root` a clip does not drive.
2105
+ *
2106
+ * An undriven bone keeps whatever the previous clip left it in, so a rig whose clips cover 22 of
2107
+ * 65 bones carries the last walk cycle's hand shape into every pose that follows.
2108
+ */
2109
+ declare function clipBoneCoverage(root: Object3D, clip: AnimationClip): IClipCoverageReport;
2110
+ /**
2111
+ * Score how far a clip's pose is from the same pose on a reference rig, in degrees.
2112
+ *
2113
+ * Each bone is compared as its world rotation *relative to its own rig's bind pose*, as a whole
2114
+ * quaternion. The delta makes the two rigs' bind conventions cancel, so rigs whose arms sit 90
2115
+ * degrees apart at rest still score zero when the retarget is right; the whole quaternion makes
2116
+ * twist count, which a bone-direction check cannot see — a forearm rolled about its own axis
2117
+ * points exactly where it should while the skin between elbow and wrist tears into a smear.
2118
+ *
2119
+ * Both rigs are driven and then restored to the transforms they arrived with.
2120
+ */
2121
+ declare function clipPoseError(subject: IClipPoseSubject, reference: IClipPoseSubject, options?: IClipPoseErrorOptions): IClipPoseErrorReport;
2122
+
2123
+ /**
2124
+ * One rig's parent→child bone distances, captured at one moment.
2125
+ *
2126
+ * A rigid skeleton preserves every parent→child distance under any pose — that is what makes
2127
+ * bone length the one number that names a broken pose without a screenshot (PRD-324).
2128
+ */
2129
+ interface IBoneLengthSnapshot {
2130
+ readonly bones: number;
2131
+ /** Bone name → world distance to its parent bone, in metres. Bones without a bone parent are absent. */
2132
+ readonly lengths: Readonly<Record<string, number>>;
2133
+ }
2134
+ /** One bone whose parent→child distance changed between the snapshot and now. */
2135
+ interface IBoneLengthDeviation {
2136
+ readonly bone: string;
2137
+ /** The distance at the snapshot, in metres. */
2138
+ readonly bindLength: number;
2139
+ /** The distance now, in metres. */
2140
+ readonly posedLength: number;
2141
+ /** `posedLength / bindLength`; `Infinity` when the bind distance was zero. */
2142
+ readonly ratio: number;
2143
+ /** `|posedLength − bindLength|`, in metres. */
2144
+ readonly delta: number;
2145
+ }
2146
+ interface IBoneLengthDeviationReport {
2147
+ readonly bones: number;
2148
+ /** Bones actually compared: named in both the snapshot and now. */
2149
+ readonly compared: number;
2150
+ /** The largest relative change across compared bones. Zero for a rigid pose. */
2151
+ readonly maxDeviation: number;
2152
+ /** The worst deviating bone, or `null` when the pose is rigid. */
2153
+ readonly worst: IBoneLengthDeviation | null;
2154
+ /** Every bone past `tolerance`, worst first. */
2155
+ readonly deviations: readonly IBoneLengthDeviation[];
2156
+ /** True when no bone moved past `tolerance` relative to its bind distance. */
2157
+ readonly rigid: boolean;
2158
+ }
2159
+ interface IBoneLengthDeviationsOptions {
2160
+ /**
2161
+ * Allowed relative change per bone before it is named. Default `0.01` — one per cent of its
2162
+ * own bind length, which float noise sits far below and a pose defect sits far above.
2163
+ */
2164
+ readonly tolerance?: number;
2165
+ }
2166
+ /**
2167
+ * Measure every parent→child bone distance, in world space, as the rig stands right now.
2168
+ *
2169
+ * World distances, so a uniform ancestor scale is part of the number: capture the baseline and
2170
+ * the comparison under the same ancestor transform (in the usual shape, both after the rig's
2171
+ * normalisation) and the scale cancels in the comparison.
2172
+ */
2173
+ declare function boneLengths(root: Object3D): IBoneLengthSnapshot;
2174
+ /**
2175
+ * Compare a rig's parent→child bone distances now against a captured snapshot.
2176
+ *
2177
+ * A rigid skeleton preserves every parent→child distance under any pose, so any named bone is
2178
+ * a defect with an address: something moved a bone away from its parent — a position or scale
2179
+ * track in the wrong space, a bind mismatch, a clipped hierarchy — and no screenshot needs to
2180
+ * be squinted at to say which one (PRD-324).
2181
+ */
2182
+ declare function boneLengthDeviations(root: Object3D, bind: IBoneLengthSnapshot, options?: IBoneLengthDeviationsOptions): IBoneLengthDeviationReport;
1513
2183
 
1514
2184
  /**
1515
2185
  * The version this library reports.
@@ -1519,6 +2189,6 @@ declare function attachToBone(root: Object3D, boneName: string, child: Object3D)
1519
2189
  * unavoidable here — core is bundled for browsers and cannot read `package.json` at runtime — so
1520
2190
  * the spec now asserts this equals the manifest instead of asserting a number somebody typed.
1521
2191
  */
1522
- declare const version = "0.3.0";
2192
+ declare const version = "0.3.1";
1523
2193
 
1524
- export { ATLAS_PADDING, ATMOSPHERE_LUT_RESOLUTIONS, AnimationPlayer, Atmosphere, type AtmosphereDirection, AtmosphereLuts, type AtmosphereRgb, Billboard3D, type BillboardLockAxis, CameraShake, type CameraShakeCurve, ClusteredBatch, ClusteredMesh, ComputeDrivenRegistry, FluidField2D, GPUParticles3D, GPUReadback, GPUSceneBVH, type GPUSceneBVHTraceFunction, GroundSnap, type IAnimationPlayOptions, type IAnimationPlayerOptions, type IAtmosphereLutResolution, type IAtmosphereLutResolutions, type IAtmosphereOptions, type IAtmosphereParameterPatch, type IAtmosphereParameters, type IAtmosphereScenePass, type IBillboard3DOptions, type ICameraShakeOffset, type ICameraShakeOptions, type IClusterTable, type IClusteredBatchBuildOptions, type IClusteredBatchOptions, type IClusteredMeshOptions, type IClusteredPlacement, type IComputeDriven, type IFluidFieldOptions, type IFluidFieldSampler, type IFluidFieldVector2, type IGPUReadbackOptions, type IGPUReadbackSample, type IGPUSceneBVHMaterialGroup, type IGPUSceneBVHOptions, IGamePluginHooks, IGamePluginRuntime, type IGroundSnapOptions, type IInstancedBatchBuildOptions, type IInstancedBatchOptions, type IInstancedPlacement, type IMeasureThreePoseOptions, type INormaliseToMetresOptions, type IPathFollow3DOptions, type IPathFollow3DProjection, type IPathFollow3DSample, type IPlatformInfo, type IProbeVolumeBakeProgress, type IProbeVolumeCoefficient, type IProbeVolumeObservation, type IProbeVolumeOptions, type IReplayOptions, type IReplayRecording, type IReplayRecordingSample, type IResolvedAtmosphereParameters, type ISoftBody3DOptions, type ISoftBodyCollision, type ISolarPosition, type ISolarPositionInput, type ISpectralOceanCascade, type ISpectralOceanHeight, type ISpectralOceanOptions, type ISpriteAnimator3DOptions, type ISpriteFrame3D, type IStrideReport, type IThreePoseBounds, type IThreePoseMeasurement, type ITracerPool3DOptions, type ITracerSpawnOptions, type IWaveFieldDomainWarp, type IWaveFieldOptions, type IWaveFieldSample, type IWaveFieldWave, InstancedBatch, LUT_RESOLUTIONS, type NormaliseAxis, PROBE_VOLUME_MARKER, PathFollow3D, type PlatformFormFactor, type PlatformOS, type PlatformRuntime, ProbeVolume, type ProbeVolumeDensity, type Recording, type ReplayPointer, SoftBody3D, SpectralOcean, SpriteAnimator3D, type SpritePlaybackMode, type ThreePoseQuaternion, type ThreePoseVector, TracerPool3D, type WaveDirection, WaveField, attachToBone, bvhIntersectFirstHit, createReplayDriver, directionFromSolarPosition, directionalTransmittance, getPlatform, isMobile, isNative, isTouchscreenAvailable, isWeb, measureThreePose, normaliseToMetres, parseReplayRecording, posedBounds, rayStruct, readProbeVolumeObservation, replay, resolveAtmosphereLutResolutions, resolveAtmosphereParameters, skeletonBones, softCircleDataTexture, solarPosition, solarPositionAt, updateAtmosphereParameters, updateClusteredMeshes, version, zenithTransmittance };
2194
+ export { ATLAS_PADDING, ATMOSPHERE_LUT_RESOLUTIONS, AnimationPlayer, Atmosphere, type AtmosphereDirection, AtmosphereLuts, type AtmosphereRgb, Billboard3D, type BillboardLockAxis, CameraShake, type CameraShakeCurve, ClusteredBatch, ClusteredMesh, FluidField2D, GPUParticles3D, GPUSceneBVH, type GPUSceneBVHTraceFunction, GroundSnap, type IAddInSlicesOptions, type IAddInSlicesProgress, type IAddInSlicesReport, type IAnimationPlayOptions, type IAnimationPlayerOptions, type IAtmosphereLutResolution, type IAtmosphereLutResolutions, type IAtmosphereOptions, type IAtmosphereParameterPatch, type IAtmosphereParameters, type IAtmosphereScenePass, type IBillboard3DOptions, type IBoneContactReport, type IBoneLengthDeviation, type IBoneLengthDeviationReport, type IBoneLengthDeviationsOptions, type IBoneLengthSnapshot, type IBonePoseError, type ICameraShakeOffset, type ICameraShakeOptions, type IClipBindingReport, type IClipCoverageReport, type IClipPoseErrorOptions, type IClipPoseErrorReport, type IClipPoseSubject, type IClipTrackBinding, type IClusterTable, type IClusteredBatchBuildOptions, type IClusteredBatchOptions, type IClusteredMeshOptions, type IClusteredPlacement, IComputeDriven$1 as IComputeDriven, type IFluidFieldOptions, type IFluidFieldSampler, type IFluidFieldVector2, IGPUReadbackSample, type IGPUSceneBVHMaterialGroup, type IGPUSceneBVHOptions, IGamePluginHooks, IGamePluginRuntime, type IGroundSnapOptions, type IInstancedBatchBuildOptions, type IInstancedBatchOptions, type IInstancedPlacement, type ILoadAllOptions, type ILoadAllProgress, type IMeasureThreePoseOptions, type IMergePart, type IMergePartsOptions, type INormaliseToMetresOptions, type IPathFollow3DOptions, type IPathFollow3DProjection, type IPathFollow3DSample, type IPlatformInfo, type IProbeVolumeBakeProgress, type IProbeVolumeCoefficient, type IProbeVolumeObservation, type IProbeVolumeOptions, type IReplayOptions, type IReplayRecording, type IReplayRecordingSample, type IResolvedAtmosphereParameters, type ISkeletalMesh3DOptions, type ISoftBody3DOptions, type ISoftBodyCollision, type ISolarPosition, type ISolarPositionInput, type ISpectralOceanCascade, type ISpectralOceanHeight, type ISpectralOceanOptions, type ISpriteAnimator3DOptions, type ISpriteFrame3D, type IStrideReport, type IThreePoseBounds, type IThreePoseMeasurement, type ITracerPool3DOptions, type ITracerSpawnOptions, type IVirtualShadowOptions, type IVirtualShadowStats, type IWaterReflectionOptions, type IWaterSurfaceOptions, type IWaveFieldDomainWarp, type IWaveFieldGraphOptions, type IWaveFieldOptions, type IWaveFieldSample, type IWaveFieldWave, InstancedBatch, LUT_RESOLUTIONS, type NormaliseAxis, PROBE_VOLUME_MARKER, PathFollow3D, type PlatformFormFactor, type PlatformOS, type PlatformRuntime, ProbeVolume, type ProbeVolumeDensity, type Recording, type ReplayPointer, SkeletalMesh3D, SoftBody3D, SpectralOcean, SpriteAnimator3D, type SpritePlaybackMode, type ThreePoseQuaternion, type ThreePoseVector, TracerPool3D, VIRTUAL_SHADOW_MARKER, VIRTUAL_SHADOW_MOVER_LAYER, VirtualShadowNode, WaterSurface3D, type WaveDirection, WaveField, addInSlices, attachToBone, boneContact, boneLengthDeviations, boneLengths, bvhIntersectFirstHit, clipBoneCoverage, clipPoseError, clipTrackBindings, createReplayDriver, directionFromSolarPosition, directionalTransmittance, getPlatform, isMobile, isNative, isTouchscreenAvailable, isWeb, loadAll, measureThreePose, mergeParts, normaliseToMetres, parseReplayRecording, posedBounds, rayStruct, readProbeVolumeObservation, readVirtualShadowMarker, replay, resolveAtmosphereLutResolutions, resolveAtmosphereParameters, skeletonBones, softCircleDataTexture, solarPosition, solarPositionAt, updateAtmosphereParameters, updateClusteredMeshes, version, zenithTransmittance };