@threenative/core 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/capabilities.json +1344 -88
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CYKk2WTu.d.ts} +7 -0
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-C1SnMoJ-.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-D_6r-k4Y.d.ts} +365 -7
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CMklJs6r.d.ts} +1 -1
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1040 -19
- package/dist/index.js +5111 -470
- package/dist/playtest.d.ts +4 -4
- package/dist/playtest.js +44 -9
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-Cy4qeBOA.d.ts} +247 -13
- package/dist/ui-layer.d.ts +13 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +3 -3
- package/mcp/blender-server.mjs +1 -1
- package/mcp/engine-server.mjs +33 -5
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +179 -19
|
@@ -74,6 +74,13 @@ interface IAssetLoader {
|
|
|
74
74
|
* stay 0 for a game with no manifest, where no size is knowable before the bytes arrive.
|
|
75
75
|
*/
|
|
76
76
|
readonly progress: {
|
|
77
|
+
/**
|
|
78
|
+
* The logical paths asked for and not yet settled, in request order. A loading screen that
|
|
79
|
+
* only shows a ratio cannot say *what* it is waiting for, which is the difference between
|
|
80
|
+
* "still loading" and "stuck on `akagi.glb`" — and a stall report that names nothing is a
|
|
81
|
+
* bug report nobody can act on.
|
|
82
|
+
*/
|
|
83
|
+
readonly pending: readonly string[];
|
|
77
84
|
readonly requested: number;
|
|
78
85
|
readonly requestedBytes: number;
|
|
79
86
|
readonly settled: number;
|
|
@@ -14,6 +14,15 @@ interface IAudioBusOptions {
|
|
|
14
14
|
readonly maxVoices?: number;
|
|
15
15
|
}
|
|
16
16
|
interface IAudioPlayOptions {
|
|
17
|
+
/**
|
|
18
|
+
* What this cue is, for anything reading back what the game played — a playtest above all.
|
|
19
|
+
*
|
|
20
|
+
* Nothing about the sound changes. Every other audio check answers "is the file right": that it
|
|
21
|
+
* exists, decodes, and is inside its byte budget. None of them can answer "did the game say the
|
|
22
|
+
* general-quarters line twice", which is the class of defect players actually report, so the bus
|
|
23
|
+
* keeps a ledger of the labels it was given.
|
|
24
|
+
*/
|
|
25
|
+
readonly cue?: string;
|
|
17
26
|
readonly fade?: number;
|
|
18
27
|
readonly loop?: boolean;
|
|
19
28
|
readonly volume?: number;
|
|
@@ -49,6 +58,21 @@ interface IAudioPlayOptions {
|
|
|
49
58
|
readonly lowpassHz?: number;
|
|
50
59
|
}
|
|
51
60
|
interface IAudioRuntimeSnapshot {
|
|
61
|
+
/**
|
|
62
|
+
* How many times each labelled cue has sounded, across every live bus.
|
|
63
|
+
*
|
|
64
|
+
* This is the only observation that can answer "what did the game actually say, and how often".
|
|
65
|
+
* Every other audio check in the repository is about the *file* — that it exists, decodes and is
|
|
66
|
+
* inside its budget — and all of them stay green while a one-shot line plays a second time
|
|
67
|
+
* halfway through a match, which is the defect players report. A game opts a cue in by passing
|
|
68
|
+
* `cue` to `play`/`playAt`; unlabelled sounds never appear here.
|
|
69
|
+
*/
|
|
70
|
+
readonly cues: Readonly<Record<string, number>>;
|
|
71
|
+
/** The most recent labelled cues in the order they sounded, bounded per bus. */
|
|
72
|
+
readonly recentCues: ReadonlyArray<{
|
|
73
|
+
readonly atMs: number;
|
|
74
|
+
readonly cue: string;
|
|
75
|
+
}>;
|
|
52
76
|
readonly queued: number;
|
|
53
77
|
readonly voices: number;
|
|
54
78
|
/** Retired voices held for reuse. Bounded by peak concurrency, never by session length. */
|
|
@@ -67,6 +91,13 @@ interface IAudioRuntimeSnapshot {
|
|
|
67
91
|
*/
|
|
68
92
|
readonly unsupported: readonly string[];
|
|
69
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Forgets every recorded cue.
|
|
96
|
+
*
|
|
97
|
+
* @situation clear the recorded audio cue counts between tests so one test cannot read another's plays
|
|
98
|
+
* @example resetAudioCueLedger();
|
|
99
|
+
*/
|
|
100
|
+
declare function resetAudioCueLedger(): void;
|
|
70
101
|
declare class AudioBus {
|
|
71
102
|
#private;
|
|
72
103
|
readonly listener: AudioListener;
|
|
@@ -153,4 +184,4 @@ declare class AudioBus {
|
|
|
153
184
|
}
|
|
154
185
|
declare function audioRuntimeSnapshot(): IAudioRuntimeSnapshot;
|
|
155
186
|
|
|
156
|
-
export { AudioBus as A, type IAudioBusOptions as I, audioRuntimeSnapshot as a, type IAudioPlayOptions as b };
|
|
187
|
+
export { AudioBus as A, type IAudioBusOptions as I, audioRuntimeSnapshot as a, type IAudioPlayOptions as b, resetAudioCueLedger as r };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as three from 'three';
|
|
2
2
|
import { Camera, Vector2, Vector3, Scene, OrthographicCamera } from 'three';
|
|
3
|
-
import { I as IRendererLike } from './renderer-
|
|
3
|
+
import { I as IRendererLike } from './renderer-Cy4qeBOA.js';
|
|
4
4
|
|
|
5
5
|
interface IViewportSize {
|
|
6
6
|
readonly aspect: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { I as IAssetLoader, a as IAssetLoaderOptions } from './assets-
|
|
2
|
-
import {
|
|
1
|
+
import { I as IAssetLoader, a as IAssetLoaderOptions } from './assets-CYKk2WTu.js';
|
|
2
|
+
import { F as FramePassKind, H as IRenderPassSample, m as IFramePhaseSample, n as IPipelineCensus, I as IRendererLike, ad as IRendererOptions, a as IFrameBudgetWindow, i as IFrameBudgetOptions } from './renderer-Cy4qeBOA.js';
|
|
3
3
|
import { Vector2, Vector3, Object3D, Camera, Intersection, Scene as Scene$1 } from 'three';
|
|
4
|
-
import { V as Viewport, C as CanvasLayer, I as IViewportOptions } from './canvas-layer-
|
|
4
|
+
import { V as Viewport, C as CanvasLayer, I as IViewportOptions } from './canvas-layer-C1SnMoJ-.js';
|
|
5
5
|
import { StoreApi } from 'zustand/vanilla';
|
|
6
6
|
|
|
7
7
|
type ThreeNativeOrientation = "landscape" | "portrait" | "sensor";
|
|
@@ -167,6 +167,76 @@ interface IThreeNativeModelsConfig {
|
|
|
167
167
|
readonly simplifyRatio?: number;
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
|
+
/** Quality policy for automatic discrete LOD; it picks the projected pixel-error budget. */
|
|
171
|
+
type ThreeNativeLodPreset = "aggressive" | "balanced" | "quality";
|
|
172
|
+
/** What the `minTriangles` pre-filter is measured against. */
|
|
173
|
+
type ThreeNativeLodMinTrianglesScope = "primitive" | "asset";
|
|
174
|
+
/** Generation knobs shared by every preset. All are ceilings or filters, not promises. */
|
|
175
|
+
interface IThreeNativeLodGenerationConfig {
|
|
176
|
+
/**
|
|
177
|
+
* Increasing geometric-error targets in normalized mesh-extent units; 1–16 positive finite
|
|
178
|
+
* numbers that strictly increase. Default `[0.002, 0.006, 0.02, 0.06]`. Each is simplified from
|
|
179
|
+
* LOD0 independently and a target that cannot reduce is dropped.
|
|
180
|
+
*/
|
|
181
|
+
readonly errorTargets?: readonly number[];
|
|
182
|
+
/** Levels in the chain **including LOD0**; integer 1–8, default 4. `1` emits nothing derived. */
|
|
183
|
+
readonly maxLevels?: number;
|
|
184
|
+
/**
|
|
185
|
+
* Fraction of its predecessor's triangles a derived level must save to be kept; finite in
|
|
186
|
+
* `[0, 1)`, default `0.2`. This is the benefit gate — a mesh that cannot reach it is skipped
|
|
187
|
+
* with `insufficient-reduction`, which is a normal outcome, not an error.
|
|
188
|
+
*/
|
|
189
|
+
readonly minSaving?: number;
|
|
190
|
+
/**
|
|
191
|
+
* Cheap pre-filter floor in triangles; positive integer, default `128`. It only avoids
|
|
192
|
+
* clearly-pointless work (the simplifier's fixed per-call cost); `minSaving` is the real gate.
|
|
193
|
+
*/
|
|
194
|
+
readonly minTriangles?: number;
|
|
195
|
+
/**
|
|
196
|
+
* What `minTriangles` is measured against; `"primitive"` or `"asset"`, default `"asset"`. The
|
|
197
|
+
* asset scope measures the whole model, so a model split into many small primitives is still
|
|
198
|
+
* eligible on its total.
|
|
199
|
+
*/
|
|
200
|
+
readonly minTrianglesScope?: ThreeNativeLodMinTrianglesScope;
|
|
201
|
+
/**
|
|
202
|
+
* Opt-in far rung that joins a mesh's same-material primitives into one draw per material group.
|
|
203
|
+
* Default `false`: with no option the cook is byte-identical to today. A join never crosses a
|
|
204
|
+
* material, never touches a skinned, morph-target or animated node, and never leaves the mesh's
|
|
205
|
+
* own node, so authored LOD0, node identity, per-node visibility, picking and transforms are
|
|
206
|
+
* untouched; the joined geometry is an additional far rung beside LOD0.
|
|
207
|
+
*/
|
|
208
|
+
readonly join?: boolean;
|
|
209
|
+
}
|
|
210
|
+
/** Screen-space selection knobs. */
|
|
211
|
+
interface IThreeNativeLodRuntimeConfig {
|
|
212
|
+
/** Projected geometric-error budget in raster pixels; positive finite. Default by preset. */
|
|
213
|
+
readonly maxPixelError?: number;
|
|
214
|
+
/** Fraction in `[0, 0.5)`, default 0.15, that stabilizes coarsening at a boundary. */
|
|
215
|
+
readonly hysteresis?: number;
|
|
216
|
+
}
|
|
217
|
+
/** A partial override for one asset; nested objects overlay, they never replace. */
|
|
218
|
+
interface IThreeNativeLodOverride {
|
|
219
|
+
readonly enabled?: boolean;
|
|
220
|
+
readonly generation?: IThreeNativeLodGenerationConfig;
|
|
221
|
+
readonly preset?: ThreeNativeLodPreset;
|
|
222
|
+
readonly runtime?: IThreeNativeLodRuntimeConfig;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Automatic discrete LOD policy. `{}` resolves to enabled/balanced and any explicit block turns
|
|
226
|
+
* generation on; `false` and `{ enabled: false }` are equivalent absolute kill switches that no
|
|
227
|
+
* per-asset override can re-enable. Overrides are keyed by canonical project-relative source asset
|
|
228
|
+
* (`/` separators).
|
|
229
|
+
*
|
|
230
|
+
* Omission currently bakes nothing: the default-on front door opens only after the qualification
|
|
231
|
+
* phase passes. Until then `assets.lod: {}` is the opt-in that resolves to enabled/balanced.
|
|
232
|
+
*/
|
|
233
|
+
interface IThreeNativeLodConfig {
|
|
234
|
+
readonly enabled?: boolean;
|
|
235
|
+
readonly generation?: IThreeNativeLodGenerationConfig;
|
|
236
|
+
readonly overrides?: Readonly<Record<string, boolean | IThreeNativeLodOverride>>;
|
|
237
|
+
readonly preset?: ThreeNativeLodPreset;
|
|
238
|
+
readonly runtime?: IThreeNativeLodRuntimeConfig;
|
|
239
|
+
}
|
|
170
240
|
interface IThreeNativeConfig {
|
|
171
241
|
readonly app?: {
|
|
172
242
|
readonly id?: string;
|
|
@@ -224,6 +294,13 @@ interface IThreeNativeConfig {
|
|
|
224
294
|
};
|
|
225
295
|
/** Source-relative globs omitted from builds; excluded bytes are still reported. */
|
|
226
296
|
readonly exclude?: readonly string[];
|
|
297
|
+
/**
|
|
298
|
+
* Automatic discrete LOD. `assets.lod: {}` opts in with the balanced default; `false` or
|
|
299
|
+
* `{ enabled: false }` is the absolute kill switch, and per-asset overrides key off canonical
|
|
300
|
+
* source asset paths. Omission currently bakes nothing — the default-on front door opens only
|
|
301
|
+
* after the qualification phase passes. See {@link IThreeNativeLodConfig}.
|
|
302
|
+
*/
|
|
303
|
+
readonly lod?: boolean | IThreeNativeLodConfig;
|
|
227
304
|
readonly models?: "none" | IThreeNativeModelsConfig;
|
|
228
305
|
readonly output?: string;
|
|
229
306
|
readonly source?: string;
|
|
@@ -258,6 +335,46 @@ interface IThreeNativeConfig {
|
|
|
258
335
|
* surface, and says so in `TN_ALPHA_ANTIALIASING` rather than reporting itself applied.
|
|
259
336
|
*/
|
|
260
337
|
readonly alphaAntialiasing?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* Whether the engine may render an internal mirror of the scene to collapse repeated draws.
|
|
340
|
+
*
|
|
341
|
+
* On by default, which is the shipping behaviour and is what an unset option means. The mirror
|
|
342
|
+
* is opportunistic and correctness-preserving, but it pays a reconciliation cost per frame, so
|
|
343
|
+
* a game that has measured it as a loss — a scene whose draw count falls without its frame time
|
|
344
|
+
* following — can decline it with `false`. An opted-out game builds no mirror and runs no
|
|
345
|
+
* eligibility scan, so the opt-out costs nothing rather than declining each frame; the authored
|
|
346
|
+
* scene is what renders. The `TN_RENDER_PROJECTION` marker still reports it, with its own
|
|
347
|
+
* reason code rather than one of the measured declines.
|
|
348
|
+
*/
|
|
349
|
+
readonly projection?: boolean;
|
|
350
|
+
/**
|
|
351
|
+
* Projected diameter, in raster pixels, below which the engine does not submit an object to
|
|
352
|
+
* the render camera. On by default at a conservative **0.5 px**: an object under half a pixel
|
|
353
|
+
* cannot light a whole pixel of the frame, so only unresolvable geometry is removed. Set a
|
|
354
|
+
* larger number for a tuned cut — a shipped game's ladder chose 2 px — or `false` to leave
|
|
355
|
+
* every object drawn. The measurement still runs with `false`; `TN_PROJECTION` reports how many
|
|
356
|
+
* objects were considered and skipped. Exempt a single object with `alwaysRender` from
|
|
357
|
+
* `@threenative/core`; the player's camera-attached objects and shadow casters are already kept.
|
|
358
|
+
*/
|
|
359
|
+
readonly minimumProjectedPixels?: number | false;
|
|
360
|
+
/**
|
|
361
|
+
* How much of the scene graph the engine walks for world matrices each frame.
|
|
362
|
+
*
|
|
363
|
+
* `"visible"` — the default — does not recurse into a hidden subtree. three's own
|
|
364
|
+
* `updateMatrixWorld` walks every child whatever its `visible` flag, multiplying a world matrix
|
|
365
|
+
* for a full-detail body whose merged stand-in is showing, a hidden LOD level, a parked model —
|
|
366
|
+
* none of which can draw. The engine composes every visible node exactly as three does, and
|
|
367
|
+
* remembers a hidden node it skipped so the first frame that subtree shows again its whole
|
|
368
|
+
* chain is refreshed before anything reads it. A game that reads a **hidden** object's
|
|
369
|
+
* `matrixWorld` directly must not rely on the walk having reached it: use `getWorldPosition`
|
|
370
|
+
* (which updates the chain) or call `updateWorldMatrix(true, false)` first.
|
|
371
|
+
*
|
|
372
|
+
* `"all"` visits every node, exactly as three's own walk does — for a game that reads hidden
|
|
373
|
+
* world matrices without going through `getWorld*` and cannot say so per object. Both modes
|
|
374
|
+
* report the number of nodes walked in the `TN_PROJECTION` window, so the cost of the walk the
|
|
375
|
+
* default removed is measurable rather than asserted.
|
|
376
|
+
*/
|
|
377
|
+
readonly matrixWorld?: "visible" | "all";
|
|
261
378
|
/**
|
|
262
379
|
* Android-only rendering overrides selected by the engine.
|
|
263
380
|
*
|
|
@@ -279,7 +396,7 @@ interface IThreeNativeConfig {
|
|
|
279
396
|
};
|
|
280
397
|
readonly ui?: {
|
|
281
398
|
/**
|
|
282
|
-
* Which renderer draws `src/ui/`.
|
|
399
|
+
* Which renderer draws `src/ui/`. Defaults to `"web"`.
|
|
283
400
|
*
|
|
284
401
|
* `"web"` runs the same React DOM, Tailwind, CSS, SVG and fonts on every target, through
|
|
285
402
|
* that platform's own browser-class renderer composited over the game surface. What is
|
|
@@ -298,6 +415,182 @@ interface IThreeNativeConfig {
|
|
|
298
415
|
};
|
|
299
416
|
}
|
|
300
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Per-object geometry cost, measured from what the renderer actually submitted.
|
|
420
|
+
*
|
|
421
|
+
* A total triangle counter cannot name the offender: a main character can cost 500 triangles while
|
|
422
|
+
* a barely visible tree submits two million, and both land in the same number. `FrameBudget` stays
|
|
423
|
+
* authoritative for a frame's per-pass totals; this attributes a slice of those totals to the
|
|
424
|
+
* objects that caused them, and reconciles the two so the difference is visible rather than
|
|
425
|
+
* assumed away.
|
|
426
|
+
*
|
|
427
|
+
* Three properties make it a measurement rather than a guess:
|
|
428
|
+
*
|
|
429
|
+
* 1. **It counts submissions, not scene contents.** The instrument is `onBeforeRender`, which the
|
|
430
|
+
* renderer calls once per object, per material group, per pass, *after* frustum and projected-
|
|
431
|
+
* size culling. A `visible` flag is a game's intent; a submission is what the GPU was handed.
|
|
432
|
+
* 2. **It is armed only on request.** Nothing is installed, walked or counted until a consumer asks
|
|
433
|
+
* for one capture, and every hook is removed when that frame ends, is cancelled, or the scene
|
|
434
|
+
* exits. An idle game pays nothing.
|
|
435
|
+
* 3. **It says "unavailable" with a reason.** A packed batch's per-member draws, an indirect
|
|
436
|
+
* counter with no readback, a geometry with no recoverable source detail: each is named, never
|
|
437
|
+
* reported as zero and never quietly dropped, and the unattributed remainder of every pass is
|
|
438
|
+
* reported beside the measured total.
|
|
439
|
+
*
|
|
440
|
+
* @situation find out which scene object is submitting the frame's triangles
|
|
441
|
+
* @situation tell a cheap foreground character from an expensive distant prop
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
/** Rows returned when a request does not name a limit. */
|
|
445
|
+
declare const GEOMETRY_CAPTURE_DEFAULT_LIMIT = 50;
|
|
446
|
+
/** The most rows one request may ask for. A transport carries a report, not a scene dump. */
|
|
447
|
+
declare const GEOMETRY_CAPTURE_MAX_LIMIT = 500;
|
|
448
|
+
/** The inspection ceiling, shared with scene-node observation so one scene has one limit. */
|
|
449
|
+
declare const GEOMETRY_CAPTURE_WALK_CAP = 50000;
|
|
450
|
+
/** How long a request waits for a presented world frame before answering "unavailable". */
|
|
451
|
+
declare const GEOMETRY_CAPTURE_TIMEOUT_MS = 2000;
|
|
452
|
+
/** The `userData` key a loader stamps on a model root so a clone keeps its provenance. */
|
|
453
|
+
declare const GEOMETRY_ASSET_KEY = "tnAssetPath";
|
|
454
|
+
/** The orderings a request may ask for. Ranking happens over the inspected scope, then slices. */
|
|
455
|
+
declare const GEOMETRY_CAPTURE_SORTS: readonly ["triangles", "draws", "projected"];
|
|
456
|
+
type GeometryCaptureSort = (typeof GEOMETRY_CAPTURE_SORTS)[number];
|
|
457
|
+
interface IGeometryCaptureRequest {
|
|
458
|
+
/** Rows to return, 1 to `GEOMETRY_CAPTURE_MAX_LIMIT`. */
|
|
459
|
+
readonly limit?: number;
|
|
460
|
+
readonly sort?: GeometryCaptureSort;
|
|
461
|
+
readonly timeoutMs?: number;
|
|
462
|
+
}
|
|
463
|
+
/** What a rendered object is: the game's own object, or the mirror standing in for some of them. */
|
|
464
|
+
type GeometryOwnershipKind = "exact" | "instancedBatch" | "materialBatch";
|
|
465
|
+
/** Where a row's triangle number came from, so a derived number is never read as a measured one. */
|
|
466
|
+
type GeometryTriangleSource = "renderer" | "batchMembers";
|
|
467
|
+
interface IGeometryPassCost {
|
|
468
|
+
readonly draws: number;
|
|
469
|
+
readonly triangles: number;
|
|
470
|
+
}
|
|
471
|
+
interface IGeometryCapturePass extends IGeometryPassCost {
|
|
472
|
+
readonly kind: FramePassKind;
|
|
473
|
+
/** What the rows below account for. */
|
|
474
|
+
readonly attributedDraws: number;
|
|
475
|
+
readonly attributedTriangles: number;
|
|
476
|
+
/**
|
|
477
|
+
* Measured minus attributed. Negative means the rows over-claim — a derived batch sum, say —
|
|
478
|
+
* which is a finding, not something to clamp away.
|
|
479
|
+
*/
|
|
480
|
+
readonly unattributedDraws: number;
|
|
481
|
+
readonly unattributedTriangles: number;
|
|
482
|
+
}
|
|
483
|
+
interface IGeometryCaptureMesh {
|
|
484
|
+
readonly id: string;
|
|
485
|
+
readonly name: string;
|
|
486
|
+
readonly path: string;
|
|
487
|
+
readonly type: string;
|
|
488
|
+
/** LOD0 triangles of this mesh, when the source detail is recoverable. */
|
|
489
|
+
readonly fullDetailTriangles?: number;
|
|
490
|
+
/** Triangles in the buffer the renderer was pointed at this frame. */
|
|
491
|
+
readonly selectedDetailTriangles?: number;
|
|
492
|
+
readonly submittedTriangles?: number;
|
|
493
|
+
readonly trianglesSource?: GeometryTriangleSource;
|
|
494
|
+
readonly draws: number;
|
|
495
|
+
readonly materials: number;
|
|
496
|
+
readonly instances?: number;
|
|
497
|
+
readonly submissions: Readonly<Partial<Record<FramePassKind, IGeometryPassCost>>>;
|
|
498
|
+
readonly batchOwner?: string;
|
|
499
|
+
readonly visible: boolean;
|
|
500
|
+
readonly inFrustum?: boolean;
|
|
501
|
+
readonly unavailable?: readonly string[];
|
|
502
|
+
}
|
|
503
|
+
interface IGeometryCaptureRow {
|
|
504
|
+
readonly id: string;
|
|
505
|
+
readonly generation: number;
|
|
506
|
+
readonly name: string;
|
|
507
|
+
readonly path: string;
|
|
508
|
+
readonly type: string;
|
|
509
|
+
/** The logical asset this row came from, when a loader stamped one. */
|
|
510
|
+
readonly asset?: string;
|
|
511
|
+
readonly fullDetailTriangles?: number;
|
|
512
|
+
readonly selectedDetailTriangles?: number;
|
|
513
|
+
readonly submittedTriangles?: number;
|
|
514
|
+
readonly trianglesSource?: GeometryTriangleSource;
|
|
515
|
+
readonly lod?: {
|
|
516
|
+
readonly level?: number;
|
|
517
|
+
readonly levels?: number;
|
|
518
|
+
};
|
|
519
|
+
/** Estimated projected diameter in drawing-buffer pixels. An estimate, never a pixel count. */
|
|
520
|
+
readonly projectedPixels?: number;
|
|
521
|
+
/**
|
|
522
|
+
* Where those bounds landed, in drawing-buffer pixels from the top-left. An overlay outlines
|
|
523
|
+
* this; it is the estimated bounds' centre, not a silhouette.
|
|
524
|
+
*/
|
|
525
|
+
readonly projectedCenter?: readonly [number, number];
|
|
526
|
+
readonly viewportFraction?: number;
|
|
527
|
+
readonly cameraDistance?: number;
|
|
528
|
+
readonly inFrustum?: boolean;
|
|
529
|
+
readonly visibility: "submitted" | "notSubmitted";
|
|
530
|
+
/** Submitted copies of this row's geometry, instances included. */
|
|
531
|
+
readonly copies: number;
|
|
532
|
+
readonly instances?: number;
|
|
533
|
+
/** Triangles counted once per distinct geometry, for a unique-inventory reading. */
|
|
534
|
+
readonly uniqueTriangles?: number;
|
|
535
|
+
readonly draws: number;
|
|
536
|
+
readonly materials: number;
|
|
537
|
+
readonly submissions: Readonly<Partial<Record<FramePassKind, IGeometryPassCost>>>;
|
|
538
|
+
readonly batch?: {
|
|
539
|
+
readonly kind: GeometryOwnershipKind;
|
|
540
|
+
readonly owner: string;
|
|
541
|
+
readonly members: number;
|
|
542
|
+
readonly perMemberDrawsAvailable: boolean;
|
|
543
|
+
};
|
|
544
|
+
readonly unavailable?: readonly string[];
|
|
545
|
+
readonly meshes: readonly IGeometryCaptureMesh[];
|
|
546
|
+
}
|
|
547
|
+
interface IGeometryCaptureAsset {
|
|
548
|
+
readonly asset: string;
|
|
549
|
+
readonly objects: number;
|
|
550
|
+
readonly submittedTriangles?: number;
|
|
551
|
+
readonly draws: number;
|
|
552
|
+
/** Triangles of the distinct geometries this asset contributed, counted once each. */
|
|
553
|
+
readonly uniqueTriangles?: number;
|
|
554
|
+
readonly unavailable?: readonly string[];
|
|
555
|
+
}
|
|
556
|
+
interface IGeometryCaptureReport {
|
|
557
|
+
readonly status: "captured" | "unavailable";
|
|
558
|
+
/** Set when `status` is `unavailable`. A stale capture is never returned as a fresh one. */
|
|
559
|
+
readonly reason?: string;
|
|
560
|
+
readonly capturedAtMs?: number;
|
|
561
|
+
readonly durationMs?: number;
|
|
562
|
+
readonly generation?: number;
|
|
563
|
+
readonly tick?: number;
|
|
564
|
+
readonly frame?: number;
|
|
565
|
+
readonly backend?: string;
|
|
566
|
+
readonly camera?: {
|
|
567
|
+
readonly type: "perspective" | "orthographic" | "unknown";
|
|
568
|
+
readonly position: readonly [number, number, number];
|
|
569
|
+
readonly fov?: number;
|
|
570
|
+
readonly zoom?: number;
|
|
571
|
+
readonly near?: number;
|
|
572
|
+
readonly far?: number;
|
|
573
|
+
};
|
|
574
|
+
readonly viewport?: {
|
|
575
|
+
readonly width: number;
|
|
576
|
+
readonly height: number;
|
|
577
|
+
};
|
|
578
|
+
readonly sort?: GeometryCaptureSort;
|
|
579
|
+
readonly limit?: number;
|
|
580
|
+
/** Rows the inspected scope produced before the limit sliced them. */
|
|
581
|
+
readonly matched?: number;
|
|
582
|
+
readonly returned?: number;
|
|
583
|
+
/** True when the limit cut rows; distinct from an incomplete inspection. */
|
|
584
|
+
readonly rowsTruncated?: boolean;
|
|
585
|
+
/** False when the walk cap stopped the inspection, so no ranking is a global claim. */
|
|
586
|
+
readonly inspectionComplete?: boolean;
|
|
587
|
+
readonly partialRanking?: boolean;
|
|
588
|
+
readonly inspectedNodes?: number;
|
|
589
|
+
readonly passes?: readonly IGeometryCapturePass[];
|
|
590
|
+
readonly objects?: readonly IGeometryCaptureRow[];
|
|
591
|
+
readonly assets?: readonly IGeometryCaptureAsset[];
|
|
592
|
+
}
|
|
593
|
+
|
|
301
594
|
/**
|
|
302
595
|
* One action, read either as a button through `pressed`/`justPressed`, a 2D axis through
|
|
303
596
|
* `vector`, or a scalar axis through `axis`. Which one you get depends on the fields you fill in,
|
|
@@ -438,6 +731,7 @@ interface IAfterPhysicsContext {
|
|
|
438
731
|
declare function afterPhysics(context: IAfterPhysicsContext, callback: AfterPhysicsCallback): () => void;
|
|
439
732
|
interface IRenderPerformanceMetrics {
|
|
440
733
|
readonly drawCalls?: number;
|
|
734
|
+
readonly passes?: readonly IRenderPassSample[];
|
|
441
735
|
readonly triangles?: number;
|
|
442
736
|
}
|
|
443
737
|
interface IRenderPerformanceSample extends IRenderPerformanceMetrics {
|
|
@@ -704,6 +998,33 @@ interface IWarmUpOptions {
|
|
|
704
998
|
* This is opt-in because WebGPU does not expose a portable pipeline-serialization API.
|
|
705
999
|
*/
|
|
706
1000
|
readonly cache?: IWarmUpCacheOptions;
|
|
1001
|
+
/**
|
|
1002
|
+
* Render the warm scene once after compiling, so every pass's pipelines exist before the first
|
|
1003
|
+
* frame. Default `true`.
|
|
1004
|
+
*
|
|
1005
|
+
* `compileAsync` builds only the main-pass pipelines. A shadow-casting light's shadow map and a
|
|
1006
|
+
* reflection pass each need their own variants, created synchronously on first use — on a native
|
|
1007
|
+
* Midway launch that was **44–107 pipelines after "ready"**, with freezes up to 615 ms. One
|
|
1008
|
+
* hidden render of the warm scene, with the shadow map forced to update, builds them while the
|
|
1009
|
+
* startup cover is still up. The render is invisible (it does not clear, and its output is
|
|
1010
|
+
* behind the loading layer) and restores every renderer and scene state it touches.
|
|
1011
|
+
*
|
|
1012
|
+
* Set `false` to skip it. The warm-up still reports `passes: []` and `passPipelines: 0`, because
|
|
1013
|
+
* turning a convention off must not turn its measurement off.
|
|
1014
|
+
*/
|
|
1015
|
+
readonly renderPasses?: boolean;
|
|
1016
|
+
/**
|
|
1017
|
+
* Also render objects the scene has hidden for that one covered render. Default `false`.
|
|
1018
|
+
*
|
|
1019
|
+
* `renderPasses` already disables frustum culling, so every caster is submitted to the shadow
|
|
1020
|
+
* pass and every reflected object to the reflection pass even when the startup camera cannot see
|
|
1021
|
+
* it. A hidden LOD level or a parked model is still skipped, though: three never draws a subtree
|
|
1022
|
+
* whose ancestor's `visible` is false, so its shadow and reflection pipelines stay unbuilt. With
|
|
1023
|
+
* this on, the warm-up forces every hidden object visible for that one render — ancestors
|
|
1024
|
+
* included — and restores every original `visible` exactly afterwards. The count is reported as
|
|
1025
|
+
* `visibilityForced`, so a game can see how much of its scene the render had to un-hide.
|
|
1026
|
+
*/
|
|
1027
|
+
readonly includeHidden?: boolean;
|
|
707
1028
|
}
|
|
708
1029
|
/** What the warm-up did, so a caller can report it rather than assume it. */
|
|
709
1030
|
type WarmUpObservationStatus = "complete" | "incomplete" | "unavailable";
|
|
@@ -770,12 +1091,27 @@ interface IWarmUpReport {
|
|
|
770
1091
|
readonly computeTimedOut?: boolean;
|
|
771
1092
|
/** The optional persistent warm-up hint's outcome. */
|
|
772
1093
|
readonly cache?: WarmUpCacheStatus;
|
|
1094
|
+
/**
|
|
1095
|
+
* The passes the hidden warm render exercised: `main` always, plus `shadow` when a shadow-casting
|
|
1096
|
+
* light is present and `reflection` when a reflector node is reachable from a material. Empty when
|
|
1097
|
+
* `renderPasses` was off, so an overridden convention still reports what it did. Absent when the
|
|
1098
|
+
* renderer exposes no `render` at all.
|
|
1099
|
+
*/
|
|
1100
|
+
readonly passes?: readonly string[];
|
|
1101
|
+
/** Backend pipelines observed created by the hidden warm render alone. Absent with `passes`. */
|
|
1102
|
+
readonly passPipelines?: number;
|
|
1103
|
+
/** Objects whose `frustumCulled` the warm render turned off, so off-screen casters were submitted. */
|
|
1104
|
+
readonly cullingForced?: number;
|
|
1105
|
+
/** Hidden objects the warm render forced visible (`includeHidden`); zero when it was off. */
|
|
1106
|
+
readonly visibilityForced?: number;
|
|
773
1107
|
}
|
|
774
1108
|
/** The narrow slice of the renderer this needs. Structural so a test needs no renderer. */
|
|
775
1109
|
interface IWarmUpRenderer {
|
|
776
1110
|
compileAsync?: (scene: Object3D, camera: Camera, targetScene?: Object3D) => Promise<void>;
|
|
777
1111
|
computeAsync?: (node: unknown) => Promise<void>;
|
|
778
1112
|
pipelineCensus?: () => IPipelineCensus;
|
|
1113
|
+
/** The renderer's own render path, used to build every pass's pipelines once. */
|
|
1114
|
+
render?: (scene: Object3D, camera: Camera) => void;
|
|
779
1115
|
raw?: unknown;
|
|
780
1116
|
}
|
|
781
1117
|
/**
|
|
@@ -861,6 +1197,10 @@ interface IStartupStatus {
|
|
|
861
1197
|
* 0 to 1, monotonic and honest: the loader's settled/requested ratio carries the first 0.7
|
|
862
1198
|
* while the start scene loads, 0.8 once the world is entered, 0.9 once first-use compilation
|
|
863
1199
|
* settled, 1 when `whenReady()` resolves.
|
|
1200
|
+
*
|
|
1201
|
+
* Monotonic is enforced, not assumed: this is a high-water mark over the measured load state,
|
|
1202
|
+
* because that state can fall — requesting an asset after an earlier one settled shrinks the
|
|
1203
|
+
* ratio, and a bar that jumps backwards reads to a player as the load restarting.
|
|
864
1204
|
*/
|
|
865
1205
|
readonly progress: number;
|
|
866
1206
|
/** When each milestone happened; members appear as they are reached. */
|
|
@@ -925,6 +1265,12 @@ interface ICtx<TState extends Record<string, unknown> = Record<string, unknown>,
|
|
|
925
1265
|
readonly after: (delay: number, callback: () => void) => ScheduleHandle;
|
|
926
1266
|
/** Register a callback for the engine-owned phase after physics writes solved transforms. */
|
|
927
1267
|
readonly afterPhysics: (callback: AfterPhysicsCallback) => () => void;
|
|
1268
|
+
/**
|
|
1269
|
+
* Register work that runs once per actual world render, after the frame's last fixed update and
|
|
1270
|
+
* before the projection reconciles and the renderer draws. A held, loader-only frame has no world
|
|
1271
|
+
* draw and therefore dispatches nothing.
|
|
1272
|
+
*/
|
|
1273
|
+
readonly beforeRender: (callback: () => void) => () => void;
|
|
928
1274
|
readonly every: (callback: (dt: number) => void) => ScheduleHandle;
|
|
929
1275
|
readonly state: GameStore<TState>;
|
|
930
1276
|
readonly tween: <T extends object>(target: T, properties: {
|
|
@@ -951,13 +1297,19 @@ interface ICtx<TState extends Record<string, unknown> = Record<string, unknown>,
|
|
|
951
1297
|
type PluginCleanup = () => void;
|
|
952
1298
|
interface IGameObservationSampleRequest {
|
|
953
1299
|
readonly entities?: readonly string[];
|
|
1300
|
+
/** Asks for one armed per-object geometry capture. Absent means no capture is collected. */
|
|
1301
|
+
readonly geometry?: IGeometryCaptureRequest;
|
|
954
1302
|
readonly include?: readonly string[];
|
|
955
1303
|
readonly label?: string;
|
|
956
1304
|
readonly resources?: readonly string[];
|
|
957
1305
|
}
|
|
958
1306
|
interface IGameObservationContribution {
|
|
959
1307
|
readonly capabilities: readonly string[];
|
|
960
|
-
|
|
1308
|
+
/**
|
|
1309
|
+
* May answer a promise: an observation that has to wait for the renderer — a geometry capture
|
|
1310
|
+
* waits for one presented world frame — cannot be produced inside the request that asked for it.
|
|
1311
|
+
*/
|
|
1312
|
+
readonly sample: (request: IGameObservationSampleRequest) => Readonly<Record<string, unknown>> | Promise<Readonly<Record<string, unknown>>>;
|
|
961
1313
|
}
|
|
962
1314
|
interface IGameRuntimeObservations {
|
|
963
1315
|
contribute(contribution: IGameObservationContribution): PluginCleanup;
|
|
@@ -1001,6 +1353,11 @@ interface IGamePluginRuntime {
|
|
|
1001
1353
|
readonly startupTimeline?: () => IStartupTimeline;
|
|
1002
1354
|
/** The renderer-owned bounded pipeline capture, when the renderer has not been opted out. */
|
|
1003
1355
|
readonly pipelineCensus?: () => IPipelineCensus;
|
|
1356
|
+
/**
|
|
1357
|
+
* Arms one per-object geometry capture and answers its report after the next presented world
|
|
1358
|
+
* frame. Absent on a runtime with no render loop to arm.
|
|
1359
|
+
*/
|
|
1360
|
+
readonly geometryCapture?: (request?: IGeometryCaptureRequest) => Promise<IGeometryCaptureReport>;
|
|
1004
1361
|
readonly step: number;
|
|
1005
1362
|
}
|
|
1006
1363
|
interface IGamePlatformSource {
|
|
@@ -1100,6 +1457,7 @@ interface IGameConfig<TState extends Record<string, unknown> = Record<string, un
|
|
|
1100
1457
|
*/
|
|
1101
1458
|
readonly step?: number;
|
|
1102
1459
|
readonly start: string;
|
|
1460
|
+
/** Optional slower UI publication interval. Omitted publishes once per rendered frame. */
|
|
1103
1461
|
readonly stateFlushMs?: number;
|
|
1104
1462
|
}
|
|
1105
1463
|
interface IPerspectiveCameraConfig {
|
|
@@ -1124,7 +1482,7 @@ type CameraConfig = IPerspectiveCameraConfig | IOrthogonalCameraConfig;
|
|
|
1124
1482
|
* two channels through an in-process broker, which is what keeps one `src/ui/` honest: a HUD
|
|
1125
1483
|
* that works here works on a phone.
|
|
1126
1484
|
*
|
|
1127
|
-
* Publication is automatic
|
|
1485
|
+
* Publication is automatic at the rendered frame cadence (or the named stateFlushMs override), and it stops
|
|
1128
1486
|
* entirely when nothing is listening — a game whose `ui.renderer` is `native` pays nothing.
|
|
1129
1487
|
*/
|
|
1130
1488
|
interface IGameUi {
|
|
@@ -1163,4 +1521,4 @@ interface IGame<TState extends Record<string, unknown> = Record<string, unknown>
|
|
|
1163
1521
|
}
|
|
1164
1522
|
declare function defineGame<TState extends Record<string, unknown>, TPhysics = undefined>(config: IGameConfig<TState, TPhysics>): IGame<TState, TPhysics>;
|
|
1165
1523
|
|
|
1166
|
-
export {
|
|
1524
|
+
export { type IWarmUpRenderer as $, type AfterPhysicsCallback as A, type IRandom as B, type ContextMenuPolicy as C, type IRawInputPointer as D, type IRawInputPointerEdge as E, type IRawInputState as F, GEOMETRY_ASSET_KEY as G, type IRaycastOptions as H, type IGame as I, type IScenePickerOptions as J, type IThreeNativeAudioConfig as K, type IThreeNativeAudioLoop as L, type IThreeNativeAudioOverride as M, type IThreeNativeAudioSpectrum as N, type IThreeNativeBootSplash as O, type IThreeNativeConfig as P, type IThreeNativeIconVariants as Q, type IThreeNativeLodConfig as R, type IThreeNativeLodGenerationConfig as S, type IThreeNativeLodOverride as T, type IThreeNativeLodRuntimeConfig as U, type IThreeNativeTexturesConfig as V, type ITweenOptions as W, type IWarmUpCacheOptions as X, type IWarmUpObservation as Y, type IWarmUpOptions as Z, type IWarmUpProgress as _, type IGamePluginRuntime as a, type IWarmUpReport as a0, type InputBindings as a1, type InputPlatformSource as a2, type PointerEvent3DListener as a3, type PointerEvent3DType as a4, PointerEvents3D as a5, Scene as a6, type SceneFrame as a7, ScenePicker as a8, type ScheduleHandle as a9, Scheduler as aa, type ThreeNativeBackgroundMode as ab, type ThreeNativeLodMinTrianglesScope as ac, type ThreeNativeLodPreset as ad, type ThreeNativeOrientation as ae, type ThreeNativeUiRenderer as af, type WarmUpCacheStatus as ag, type WarmUpObservationStatus as ah, afterPhysics as ai, createRandom as aj, defineGame as ak, warmUpScene as al, type IGamePluginHooks as b, GEOMETRY_CAPTURE_DEFAULT_LIMIT as c, GEOMETRY_CAPTURE_MAX_LIMIT as d, GEOMETRY_CAPTURE_SORTS as e, GEOMETRY_CAPTURE_TIMEOUT_MS as f, GEOMETRY_CAPTURE_WALK_CAP as g, type GeometryCaptureSort as h, type ICtx as i, type IGameObservationContribution as j, type IGameObservationSampleRequest as k, type IGamePlatformSource as l, type IGeometryCaptureAsset as m, type IGeometryCaptureMesh as n, type IGeometryCapturePass as o, type IGeometryCaptureReport as p, type IGeometryCaptureRequest as q, type IGeometryCaptureRow as r, type IInputAction as s, type IInputGamepad as t, type IPointerDragHandle as u, type IPointerEvent3D as v, type IPointerEvents3D as w, type IPointerEvents3DOptions as x, type IPointerEvents3DPicker as y, type IPointerState as z };
|
package/dist/hot.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { a as audioRuntimeSnapshot } from './audio-
|
|
2
|
-
import { I as IGame } from './game-
|
|
1
|
+
import { a as audioRuntimeSnapshot } from './audio-7i3Xl0l3.js';
|
|
2
|
+
import { I as IGame } from './game-D_6r-k4Y.js';
|
|
3
3
|
import 'three';
|
|
4
|
-
import './assets-
|
|
5
|
-
import './renderer-
|
|
4
|
+
import './assets-CYKk2WTu.js';
|
|
5
|
+
import './renderer-Cy4qeBOA.js';
|
|
6
6
|
import 'three/webgpu';
|
|
7
|
-
import './canvas-layer-
|
|
7
|
+
import './canvas-layer-C1SnMoJ-.js';
|
|
8
8
|
import 'zustand/vanilla';
|
|
9
9
|
|
|
10
10
|
interface IHotDiagnostics {
|
package/dist/hot.js
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
import 'three';
|
|
2
2
|
|
|
3
3
|
// src/audio.ts
|
|
4
|
-
var
|
|
4
|
+
var AUDIO_STATE = /* @__PURE__ */ Symbol.for("threenative.audio.runtime");
|
|
5
|
+
function audioState() {
|
|
6
|
+
const host = globalThis;
|
|
7
|
+
const existing = host[AUDIO_STATE];
|
|
8
|
+
if (existing !== void 0) return existing;
|
|
9
|
+
const created = { buses: /* @__PURE__ */ new Set(), cueCounts: /* @__PURE__ */ new Map(), cueLog: [] };
|
|
10
|
+
host[AUDIO_STATE] = created;
|
|
11
|
+
return created;
|
|
12
|
+
}
|
|
5
13
|
function audioRuntimeSnapshot() {
|
|
6
14
|
let queued = 0;
|
|
7
15
|
let voices = 0;
|
|
8
16
|
let pooled = 0;
|
|
9
17
|
let paused = 0;
|
|
10
18
|
const unsupported = /* @__PURE__ */ new Set();
|
|
11
|
-
|
|
19
|
+
const state = audioState();
|
|
20
|
+
for (const bus of state.buses) {
|
|
12
21
|
queued += bus.queued;
|
|
13
22
|
voices += bus.voices;
|
|
14
23
|
pooled += bus.pooled;
|
|
15
24
|
paused += bus.pausedVoices;
|
|
16
25
|
for (const option of bus.unsupported) unsupported.add(option);
|
|
17
26
|
}
|
|
18
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
cues: Object.fromEntries(state.cueCounts),
|
|
29
|
+
paused,
|
|
30
|
+
pooled,
|
|
31
|
+
queued,
|
|
32
|
+
recentCues: state.cueLog.map((entry) => ({ ...entry })),
|
|
33
|
+
unsupported: [...unsupported].sort(),
|
|
34
|
+
voices
|
|
35
|
+
};
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
// src/hot.ts
|