@vgai/engine 0.5.13 → 0.5.15
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/dist/adapter/adapter-module.d.ts +447 -0
- package/dist/adapter/adapter-module.d.ts.map +1 -0
- package/dist/adapter/adapter-module.js +308 -0
- package/dist/adapter/authoring.d.ts +357 -10
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +87 -1
- package/dist/adapter/constraint.d.ts +9 -3
- package/dist/adapter/constraint.d.ts.map +1 -1
- package/dist/adapter/finders/finder-result.d.ts +28 -0
- package/dist/adapter/finders/finder-result.d.ts.map +1 -0
- package/dist/adapter/finders/finder-result.js +10 -0
- package/dist/adapter/finders/index.d.ts +38 -0
- package/dist/adapter/finders/index.d.ts.map +1 -0
- package/dist/adapter/finders/index.js +41 -0
- package/dist/adapter/finders/prefabs-from-stories.d.ts +64 -0
- package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -0
- package/dist/adapter/finders/prefabs-from-stories.js +92 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +67 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +226 -0
- package/dist/adapter/first-party-systems.d.ts.map +1 -1
- package/dist/adapter/first-party-systems.js +29 -0
- package/dist/adapter/index.d.ts +7 -3
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +5 -2
- package/dist/adapter/ingest/contract-system-adapters.d.ts +24 -1
- package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
- package/dist/adapter/ingest/contract-system-adapters.js +100 -34
- package/dist/adapter/ingest/game-contract.d.ts +188 -2
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +107 -0
- package/dist/adapter/ingest/mount-readiness.d.ts +84 -0
- package/dist/adapter/ingest/mount-readiness.d.ts.map +1 -0
- package/dist/adapter/ingest/mount-readiness.js +81 -0
- package/dist/adapter/ingest/scene-capture.d.ts +91 -2
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +110 -18
- package/dist/adapter/ingest/structural-ids.d.ts +11 -5
- package/dist/adapter/ingest/structural-ids.d.ts.map +1 -1
- package/dist/adapter/ingest/structural-ids.js +18 -9
- package/dist/adapter/ingest/upstream-pin.d.ts +4 -2
- package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -1
- package/dist/adapter/ingest/upstream-pin.js +4 -2
- package/dist/adapter/ingest/visible-capture-window.d.ts +116 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -0
- package/dist/adapter/ingest/visible-capture-window.js +141 -0
- package/dist/adapter/loop-gate-report.d.ts +3 -4
- package/dist/adapter/loop-gate-report.d.ts.map +1 -1
- package/dist/adapter/loop-gate-report.js +3 -4
- package/dist/adapter/root-adapter.d.ts +22 -2
- package/dist/adapter/root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.js +15 -0
- package/dist/adapter/system-adapter.d.ts +161 -2
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-adapter.js +18 -1
- package/dist/animation/camera-ownership.d.ts +4 -2
- package/dist/animation/camera-ownership.d.ts.map +1 -1
- package/dist/animation/camera-ownership.js +58 -1
- package/dist/animation/runtime-inspection.d.ts +70 -0
- package/dist/animation/runtime-inspection.d.ts.map +1 -0
- package/dist/animation/runtime-inspection.js +25 -0
- package/dist/animation/xstate-animation-binding.d.ts +9 -1
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +48 -1
- package/dist/asset-formats/material.d.ts +1 -1
- package/dist/asset-formats/mesh.d.ts +1 -1
- package/dist/asset-formats/particles.d.ts +13 -0
- package/dist/asset-formats/particles.d.ts.map +1 -1
- package/dist/asset-formats/particles.js +28 -0
- package/dist/behavior/xstate-inspection.d.ts +24 -0
- package/dist/behavior/xstate-inspection.d.ts.map +1 -0
- package/dist/behavior/xstate-inspection.js +25 -0
- package/dist/core/game-loop.d.ts +7 -5
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +39 -18
- package/dist/core/types.d.ts +11 -12
- package/dist/core/types.d.ts.map +1 -1
- package/dist/data/data-asset.js +1 -1
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +5 -1
- package/dist/dev/render-debug-adapter.d.ts +19 -2
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +10 -0
- package/dist/ecs/user-data.d.ts +10 -2
- package/dist/ecs/user-data.d.ts.map +1 -1
- package/dist/ecs/user-data.js +6 -2
- package/dist/input/binding-identity.d.ts +10 -0
- package/dist/input/binding-identity.d.ts.map +1 -0
- package/dist/input/binding-identity.js +36 -0
- package/dist/input/input-manager.d.ts +18 -185
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +78 -68
- package/dist/manifest/load.d.ts +14 -0
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +6 -1
- package/dist/manifest/schema.d.ts +18 -0
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +65 -8
- package/dist/pixi/authoring.d.ts +27 -0
- package/dist/pixi/authoring.d.ts.map +1 -1
- package/dist/pixi/authoring.js +10 -3
- package/dist/pixi/index.d.ts +1 -1
- package/dist/pixi/index.d.ts.map +1 -1
- package/dist/pixi/index.js +1 -1
- package/dist/pixi/ingest.d.ts +8 -0
- package/dist/pixi/ingest.d.ts.map +1 -1
- package/dist/pixi/ingest.js +10 -1
- package/dist/pixi/render-pass-bracket.d.ts +56 -0
- package/dist/pixi/render-pass-bracket.d.ts.map +1 -0
- package/dist/pixi/render-pass-bracket.js +98 -0
- package/dist/pixi/scene-capture.d.ts +32 -1
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/pixi/scene-capture.js +21 -9
- package/dist/pixi/system-adapters.d.ts +29 -0
- package/dist/pixi/system-adapters.d.ts.map +1 -1
- package/dist/pixi/system-adapters.js +27 -0
- package/dist/render/particles-factory.d.ts +16 -2
- package/dist/render/particles-factory.d.ts.map +1 -1
- package/dist/render/particles-factory.js +88 -5
- package/dist/render/soft-particle-depth.d.ts +149 -0
- package/dist/render/soft-particle-depth.d.ts.map +1 -0
- package/dist/render/soft-particle-depth.js +305 -0
- package/dist/render/voxel-surface.d.ts +117 -0
- package/dist/render/voxel-surface.d.ts.map +1 -0
- package/dist/render/voxel-surface.js +263 -0
- package/dist/runtime/create-runtime.js +7 -0
- package/dist/runtime/debug-bridge.d.ts +15 -15
- package/dist/runtime/debug-bridge.d.ts.map +1 -1
- package/dist/runtime/debug-bridge.js +27 -26
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +1 -2
- package/dist/setup/setup-particles.d.ts.map +1 -1
- package/dist/setup/setup-particles.js +9 -0
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +42 -3
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +201 -11
- package/dist-config/config.js.map +1 -1
- package/package.json +4 -2
- package/schemas/engine-capabilities.json +12 -12
- package/schemas/vgai-project.schema.json +42 -3
- package/src/adapter/adapter-module.ts +607 -0
- package/src/adapter/authoring.ts +440 -10
- package/src/adapter/constraint.ts +10 -3
- package/src/adapter/finders/finder-result.ts +29 -0
- package/src/adapter/finders/index.ts +62 -0
- package/src/adapter/finders/prefabs-from-stories.ts +142 -0
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +304 -0
- package/src/adapter/first-party-systems.ts +30 -0
- package/src/adapter/index.ts +32 -1
- package/src/adapter/ingest/contract-system-adapters.ts +116 -32
- package/src/adapter/ingest/game-contract.ts +265 -2
- package/src/adapter/ingest/mount-readiness.ts +140 -0
- package/src/adapter/ingest/scene-capture.ts +199 -22
- package/src/adapter/ingest/structural-ids.ts +21 -9
- package/src/adapter/ingest/upstream-pin.ts +4 -2
- package/src/adapter/ingest/visible-capture-window.ts +213 -0
- package/src/adapter/loop-gate-report.ts +3 -4
- package/src/adapter/root-adapter.ts +22 -2
- package/src/adapter/setup-three-root-adapter.ts +16 -0
- package/src/adapter/system-adapter.ts +188 -2
- package/src/animation/camera-ownership.ts +68 -3
- package/src/animation/runtime-inspection.ts +104 -0
- package/src/animation/xstate-animation-binding.ts +53 -1
- package/src/asset-formats/particles.ts +31 -0
- package/src/behavior/xstate-inspection.ts +51 -0
- package/src/core/game-loop.ts +39 -17
- package/src/core/types.ts +11 -12
- package/src/data/data-asset.ts +1 -1
- package/src/dev/instruments.ts +5 -1
- package/src/dev/render-debug-adapter.ts +22 -2
- package/src/ecs/user-data.ts +10 -2
- package/src/input/binding-identity.ts +37 -0
- package/src/input/input-manager.ts +93 -68
- package/src/manifest/load.ts +23 -2
- package/src/manifest/schema.ts +77 -8
- package/src/pixi/authoring.ts +37 -5
- package/src/pixi/index.ts +2 -0
- package/src/pixi/ingest.ts +15 -2
- package/src/pixi/render-pass-bracket.ts +117 -0
- package/src/pixi/scene-capture.ts +63 -13
- package/src/pixi/system-adapters.ts +41 -0
- package/src/render/particles-factory.ts +89 -6
- package/src/render/soft-particle-depth.ts +336 -0
- package/src/render/voxel-surface.ts +358 -0
- package/src/runtime/create-runtime.ts +7 -0
- package/src/runtime/debug-bridge.ts +32 -31
- package/src/runtime/game.ts +1 -2
- package/src/setup/setup-particles.ts +9 -0
- package/src/world3d-react/r3f-adapter.tsx +48 -2
- package/src/world3d-react/rapier-physics-bridge.tsx +249 -11
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capture window is a budget of VISIBLE time, not of wall-clock time.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS MODULE EXISTS. An ingest mount waits for the game to render its
|
|
5
|
+
* first frame and fails by name when that never happens
|
|
6
|
+
* (`scene-capture.ts`'s `waitForCapture`). That wait used to be a plain
|
|
7
|
+
* `setTimeout`, i.e. a wall-clock deadline started AT BOOT — and a game cannot
|
|
8
|
+
* render while its tab is hidden, because the browser parks `requestAnimation
|
|
9
|
+
* Frame` for a backgrounded document. The two facts together make one
|
|
10
|
+
* deterministic failure: a tab that opens in the BACKGROUND (the normal human
|
|
11
|
+
* path — `vgai edit` auto-opens a tab that routinely lands behind the current
|
|
12
|
+
* window) burns its whole capture window unable to draw, the deadline fires,
|
|
13
|
+
* the mount dies terminally, and foregrounding the tab later changes nothing.
|
|
14
|
+
* Agents, whose tabs happen to be visible, never saw it.
|
|
15
|
+
*
|
|
16
|
+
* So the clock only runs while the document is VISIBLE. Hidden time is not
|
|
17
|
+
* spent, and while a tab is hidden the wait PARKS rather than expiring: the
|
|
18
|
+
* capture trap stays installed, the game's modules stay live, and the first
|
|
19
|
+
* frame the tab draws after the human brings it forward is trapped exactly as
|
|
20
|
+
* it would have been at boot. That is the retry — no second mount, no
|
|
21
|
+
* re-running `load()` (which would re-construct module-level state: the
|
|
22
|
+
* ARCHITECTURE-CORE "LOADING CONSTRUCTS, once" contract), and no forcing of a
|
|
23
|
+
* frame: the game's own loop resumes on its own when the browser un-parks it.
|
|
24
|
+
*
|
|
25
|
+
* There is deliberately no wall-clock cap on the parked state. An unspent
|
|
26
|
+
* budget is not a failure — a human returning to the tab is what spends it —
|
|
27
|
+
* and a cap would be exactly the boot-time clock this module exists to
|
|
28
|
+
* remove. The parked wait is reported instead of being silent: see
|
|
29
|
+
* `CaptureWaitObserver` in `scene-capture.ts` and `vgai status`'s
|
|
30
|
+
* `ingestCaptureWait`.
|
|
31
|
+
*
|
|
32
|
+
* The state machine is a pure function of (banked segments, now, hidden) so
|
|
33
|
+
* the whole accrual rule is testable with no browser and no timers
|
|
34
|
+
* (`packages/engine/test/visible-capture-window.test.ts`); the runtime half
|
|
35
|
+
* below is only the timer arm/disarm around it.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Everything the budget reads about the outside world. Injected so the pure
|
|
39
|
+
* core stays pure and the runtime half is drivable by a fake in tests — and,
|
|
40
|
+
* as a side effect, so this module never hard-depends on `document` existing
|
|
41
|
+
* (unit runners, SSR).
|
|
42
|
+
*/
|
|
43
|
+
export interface VisibilityClock {
|
|
44
|
+
/** Milliseconds, monotonic-ish; `performance.now()`/`Date.now()` both fit. */
|
|
45
|
+
now(): number;
|
|
46
|
+
/** True while the document is hidden (no rAF, so no frame can be captured). */
|
|
47
|
+
hidden(): boolean;
|
|
48
|
+
/** Subscribe to visibility transitions; returns the unsubscribe. */
|
|
49
|
+
subscribe(onChange: () => void): () => void;
|
|
50
|
+
}
|
|
51
|
+
/** The real one: `document.visibilityState` + `visibilitychange`. Falls back
|
|
52
|
+
* to permanently-visible where there is no `document` at all, which is the
|
|
53
|
+
* honest answer for a headless caller — it has no tab to background. */
|
|
54
|
+
export declare function documentVisibilityClock(): VisibilityClock;
|
|
55
|
+
/**
|
|
56
|
+
* The banked halves of the wait plus the segment currently running. Immutable:
|
|
57
|
+
* every transition returns a new value, so a caller can hold one and compare.
|
|
58
|
+
*/
|
|
59
|
+
export interface VisibleBudgetState {
|
|
60
|
+
/** The window, in VISIBLE milliseconds. */
|
|
61
|
+
readonly budgetMs: number;
|
|
62
|
+
/** Visible time banked from completed segments. */
|
|
63
|
+
readonly visibleMs: number;
|
|
64
|
+
/** Hidden time banked from completed segments (reported, never spent). */
|
|
65
|
+
readonly hiddenMs: number;
|
|
66
|
+
/** When the current segment started. */
|
|
67
|
+
readonly since: number;
|
|
68
|
+
/** Whether the current segment is a hidden one. */
|
|
69
|
+
readonly hidden: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** Open the window at `now`, in whichever visibility the document is in. */
|
|
72
|
+
export declare function beginVisibleBudget(budgetMs: number, now: number, hidden: boolean): VisibleBudgetState;
|
|
73
|
+
/**
|
|
74
|
+
* Apply the document's current visibility at `now`: bank the segment that just
|
|
75
|
+
* ended into its own bucket and start the next one. A call that does not change
|
|
76
|
+
* visibility is a no-op *by value* (same accrual, same segment start), so a
|
|
77
|
+
* duplicate `visibilitychange` can never bank a zero-length segment twice or
|
|
78
|
+
* restart the clock.
|
|
79
|
+
*/
|
|
80
|
+
export declare function applyVisibility(state: VisibleBudgetState, now: number, hidden: boolean): VisibleBudgetState;
|
|
81
|
+
/** Visible time spent so far, including the segment in flight. */
|
|
82
|
+
export declare function visibleElapsedMs(state: VisibleBudgetState, now: number): number;
|
|
83
|
+
/** Hidden time so far, including the segment in flight. Never spent — it is
|
|
84
|
+
* reported so a failure message can say what the window did NOT count. */
|
|
85
|
+
export declare function hiddenElapsedMs(state: VisibleBudgetState, now: number): number;
|
|
86
|
+
/** Visible time left in the window; `0` once it is spent. */
|
|
87
|
+
export declare function visibleRemainingMs(state: VisibleBudgetState, now: number): number;
|
|
88
|
+
/** A live view of one running window, for the status wire and the failure message. */
|
|
89
|
+
export interface VisibleCaptureWindow {
|
|
90
|
+
/** The window's size, in visible milliseconds. */
|
|
91
|
+
readonly budgetMs: number;
|
|
92
|
+
/** Visible milliseconds spent so far. */
|
|
93
|
+
elapsedVisibleMs(): number;
|
|
94
|
+
/** Milliseconds this window has spent parked while hidden (not counted). */
|
|
95
|
+
elapsedHiddenMs(): number;
|
|
96
|
+
/** Whether the wait is parked right now because the document is hidden. */
|
|
97
|
+
isHidden(): boolean;
|
|
98
|
+
/** Stop the timer and drop the visibility listener. Idempotent. */
|
|
99
|
+
cancel(): void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Start a window that calls `onExpire` after `budgetMs` of VISIBLE time.
|
|
103
|
+
* While the document is hidden the timer is disarmed entirely (so a throttled
|
|
104
|
+
* background timer cannot fire it late either) and re-armed with the remaining
|
|
105
|
+
* budget when the document comes back.
|
|
106
|
+
*/
|
|
107
|
+
export declare function startVisibleCaptureWindow(opts: {
|
|
108
|
+
budgetMs: number;
|
|
109
|
+
onExpire: () => void;
|
|
110
|
+
/** Defaults to the document's own visibility. */
|
|
111
|
+
clock?: VisibilityClock;
|
|
112
|
+
/** Defaults to `setTimeout`/`clearTimeout`. */
|
|
113
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
114
|
+
clearTimer?: (handle: unknown) => void;
|
|
115
|
+
}): VisibleCaptureWindow;
|
|
116
|
+
//# sourceMappingURL=visible-capture-window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visible-capture-window.d.ts","sourceRoot":"","sources":["../../../src/adapter/ingest/visible-capture-window.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,GAAG,IAAI,MAAM,CAAC;IACd,+EAA+E;IAC/E,MAAM,IAAI,OAAO,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C;AAED;;yEAEyE;AACzE,wBAAgB,uBAAuB,IAAI,eAAe,CAWzD;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,OAAO,GACd,kBAAkB,CAEpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,kBAAkB,EACzB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,OAAO,GACd,kBAAkB,CAUpB;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;2EAC2E;AAC3E,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,sFAAsF;AACtF,MAAM,WAAW,oBAAoB;IACnC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,gBAAgB,IAAI,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,eAAe,IAAI,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,IAAI,OAAO,CAAC;IACpB,mEAAmE;IACnE,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,iDAAiD;IACjD,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,GAAG,oBAAoB,CAmDvB"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capture window is a budget of VISIBLE time, not of wall-clock time.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS MODULE EXISTS. An ingest mount waits for the game to render its
|
|
5
|
+
* first frame and fails by name when that never happens
|
|
6
|
+
* (`scene-capture.ts`'s `waitForCapture`). That wait used to be a plain
|
|
7
|
+
* `setTimeout`, i.e. a wall-clock deadline started AT BOOT — and a game cannot
|
|
8
|
+
* render while its tab is hidden, because the browser parks `requestAnimation
|
|
9
|
+
* Frame` for a backgrounded document. The two facts together make one
|
|
10
|
+
* deterministic failure: a tab that opens in the BACKGROUND (the normal human
|
|
11
|
+
* path — `vgai edit` auto-opens a tab that routinely lands behind the current
|
|
12
|
+
* window) burns its whole capture window unable to draw, the deadline fires,
|
|
13
|
+
* the mount dies terminally, and foregrounding the tab later changes nothing.
|
|
14
|
+
* Agents, whose tabs happen to be visible, never saw it.
|
|
15
|
+
*
|
|
16
|
+
* So the clock only runs while the document is VISIBLE. Hidden time is not
|
|
17
|
+
* spent, and while a tab is hidden the wait PARKS rather than expiring: the
|
|
18
|
+
* capture trap stays installed, the game's modules stay live, and the first
|
|
19
|
+
* frame the tab draws after the human brings it forward is trapped exactly as
|
|
20
|
+
* it would have been at boot. That is the retry — no second mount, no
|
|
21
|
+
* re-running `load()` (which would re-construct module-level state: the
|
|
22
|
+
* ARCHITECTURE-CORE "LOADING CONSTRUCTS, once" contract), and no forcing of a
|
|
23
|
+
* frame: the game's own loop resumes on its own when the browser un-parks it.
|
|
24
|
+
*
|
|
25
|
+
* There is deliberately no wall-clock cap on the parked state. An unspent
|
|
26
|
+
* budget is not a failure — a human returning to the tab is what spends it —
|
|
27
|
+
* and a cap would be exactly the boot-time clock this module exists to
|
|
28
|
+
* remove. The parked wait is reported instead of being silent: see
|
|
29
|
+
* `CaptureWaitObserver` in `scene-capture.ts` and `vgai status`'s
|
|
30
|
+
* `ingestCaptureWait`.
|
|
31
|
+
*
|
|
32
|
+
* The state machine is a pure function of (banked segments, now, hidden) so
|
|
33
|
+
* the whole accrual rule is testable with no browser and no timers
|
|
34
|
+
* (`packages/engine/test/visible-capture-window.test.ts`); the runtime half
|
|
35
|
+
* below is only the timer arm/disarm around it.
|
|
36
|
+
*/
|
|
37
|
+
/** The real one: `document.visibilityState` + `visibilitychange`. Falls back
|
|
38
|
+
* to permanently-visible where there is no `document` at all, which is the
|
|
39
|
+
* honest answer for a headless caller — it has no tab to background. */
|
|
40
|
+
export function documentVisibilityClock() {
|
|
41
|
+
const doc = typeof document === 'undefined' ? null : document;
|
|
42
|
+
return {
|
|
43
|
+
now: () => (typeof performance === 'undefined' ? Date.now() : performance.now()),
|
|
44
|
+
hidden: () => doc?.hidden === true,
|
|
45
|
+
subscribe(onChange) {
|
|
46
|
+
if (!doc)
|
|
47
|
+
return () => { };
|
|
48
|
+
doc.addEventListener('visibilitychange', onChange);
|
|
49
|
+
return () => doc.removeEventListener('visibilitychange', onChange);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Open the window at `now`, in whichever visibility the document is in. */
|
|
54
|
+
export function beginVisibleBudget(budgetMs, now, hidden) {
|
|
55
|
+
return { budgetMs, visibleMs: 0, hiddenMs: 0, since: now, hidden };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Apply the document's current visibility at `now`: bank the segment that just
|
|
59
|
+
* ended into its own bucket and start the next one. A call that does not change
|
|
60
|
+
* visibility is a no-op *by value* (same accrual, same segment start), so a
|
|
61
|
+
* duplicate `visibilitychange` can never bank a zero-length segment twice or
|
|
62
|
+
* restart the clock.
|
|
63
|
+
*/
|
|
64
|
+
export function applyVisibility(state, now, hidden) {
|
|
65
|
+
if (hidden === state.hidden)
|
|
66
|
+
return state;
|
|
67
|
+
const elapsed = Math.max(0, now - state.since);
|
|
68
|
+
return {
|
|
69
|
+
budgetMs: state.budgetMs,
|
|
70
|
+
visibleMs: state.hidden ? state.visibleMs : state.visibleMs + elapsed,
|
|
71
|
+
hiddenMs: state.hidden ? state.hiddenMs + elapsed : state.hiddenMs,
|
|
72
|
+
since: now,
|
|
73
|
+
hidden,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Visible time spent so far, including the segment in flight. */
|
|
77
|
+
export function visibleElapsedMs(state, now) {
|
|
78
|
+
return state.visibleMs + (state.hidden ? 0 : Math.max(0, now - state.since));
|
|
79
|
+
}
|
|
80
|
+
/** Hidden time so far, including the segment in flight. Never spent — it is
|
|
81
|
+
* reported so a failure message can say what the window did NOT count. */
|
|
82
|
+
export function hiddenElapsedMs(state, now) {
|
|
83
|
+
return state.hiddenMs + (state.hidden ? Math.max(0, now - state.since) : 0);
|
|
84
|
+
}
|
|
85
|
+
/** Visible time left in the window; `0` once it is spent. */
|
|
86
|
+
export function visibleRemainingMs(state, now) {
|
|
87
|
+
return Math.max(0, state.budgetMs - visibleElapsedMs(state, now));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Start a window that calls `onExpire` after `budgetMs` of VISIBLE time.
|
|
91
|
+
* While the document is hidden the timer is disarmed entirely (so a throttled
|
|
92
|
+
* background timer cannot fire it late either) and re-armed with the remaining
|
|
93
|
+
* budget when the document comes back.
|
|
94
|
+
*/
|
|
95
|
+
export function startVisibleCaptureWindow(opts) {
|
|
96
|
+
const clock = opts.clock ?? documentVisibilityClock();
|
|
97
|
+
const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
98
|
+
const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
99
|
+
let state = beginVisibleBudget(opts.budgetMs, clock.now(), clock.hidden());
|
|
100
|
+
let timer = null;
|
|
101
|
+
let done = false;
|
|
102
|
+
function disarm() {
|
|
103
|
+
if (timer !== null) {
|
|
104
|
+
clearTimer(timer);
|
|
105
|
+
timer = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function arm() {
|
|
109
|
+
disarm();
|
|
110
|
+
if (done || state.hidden)
|
|
111
|
+
return;
|
|
112
|
+
timer = setTimer(() => {
|
|
113
|
+
timer = null;
|
|
114
|
+
if (done)
|
|
115
|
+
return;
|
|
116
|
+
done = true;
|
|
117
|
+
unsubscribe();
|
|
118
|
+
opts.onExpire();
|
|
119
|
+
}, visibleRemainingMs(state, clock.now()));
|
|
120
|
+
}
|
|
121
|
+
const unsubscribe = clock.subscribe(() => {
|
|
122
|
+
if (done)
|
|
123
|
+
return;
|
|
124
|
+
state = applyVisibility(state, clock.now(), clock.hidden());
|
|
125
|
+
arm();
|
|
126
|
+
});
|
|
127
|
+
arm();
|
|
128
|
+
return {
|
|
129
|
+
budgetMs: opts.budgetMs,
|
|
130
|
+
elapsedVisibleMs: () => visibleElapsedMs(state, clock.now()),
|
|
131
|
+
elapsedHiddenMs: () => hiddenElapsedMs(state, clock.now()),
|
|
132
|
+
isHidden: () => state.hidden,
|
|
133
|
+
cancel() {
|
|
134
|
+
if (done)
|
|
135
|
+
return;
|
|
136
|
+
done = true;
|
|
137
|
+
disarm();
|
|
138
|
+
unsubscribe();
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Capability-gate shortfall reporting (D10, T7.6) — the honesty half of the
|
|
3
|
-
* play-control contract. §(d) draws the `loop` axis (`gated | self-driven`)
|
|
4
|
-
*
|
|
3
|
+
* play-control contract. §(d) draws the `loop` axis (`gated | self-driven`) on
|
|
4
|
+
* its own: a `self-driven` world (an ingested game
|
|
5
5
|
* driving its own rAF/ticker) pauses for real ONLY if its adapter implements an
|
|
6
6
|
* explicit loop-gate capability (`MountedRootBase.setPaused`/`step`) —
|
|
7
7
|
* otherwise it "honestly reports so" rather than a silent no-op (the
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
* has the identical shape: absent or unable to silence a world's audio on pause
|
|
11
11
|
* must report, not pretend.
|
|
12
12
|
*
|
|
13
|
-
* Pure compute + message-formatting
|
|
14
|
-
* (`packages/editor/src/achieved-tier.ts`) — callers own the actual
|
|
13
|
+
* Pure compute + message-formatting — callers own the actual
|
|
15
14
|
* `console.warn`/`editorConsole.warn` call; this module never logs.
|
|
16
15
|
*/
|
|
17
16
|
/** Greppable prefix for a loop-gate shortfall (pause requested, world's loop
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-gate-report.d.ts","sourceRoot":"","sources":["../../src/adapter/loop-gate-report.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"loop-gate-report.d.ts","sourceRoot":"","sources":["../../src/adapter/loop-gate-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;oCACoC;AACpC,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAE5C;6CAC6C;AAC7C,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAE9C,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAcD;iCACiC;AACjC,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAE1E;AAED;iDACiD;AACjD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAE3E"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Capability-gate shortfall reporting (D10, T7.6) — the honesty half of the
|
|
3
|
-
* play-control contract. §(d) draws the `loop` axis (`gated | self-driven`)
|
|
4
|
-
*
|
|
3
|
+
* play-control contract. §(d) draws the `loop` axis (`gated | self-driven`) on
|
|
4
|
+
* its own: a `self-driven` world (an ingested game
|
|
5
5
|
* driving its own rAF/ticker) pauses for real ONLY if its adapter implements an
|
|
6
6
|
* explicit loop-gate capability (`MountedRootBase.setPaused`/`step`) —
|
|
7
7
|
* otherwise it "honestly reports so" rather than a silent no-op (the
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
* has the identical shape: absent or unable to silence a world's audio on pause
|
|
11
11
|
* must report, not pretend.
|
|
12
12
|
*
|
|
13
|
-
* Pure compute + message-formatting
|
|
14
|
-
* (`packages/editor/src/achieved-tier.ts`) — callers own the actual
|
|
13
|
+
* Pure compute + message-formatting — callers own the actual
|
|
15
14
|
* `console.warn`/`editorConsole.warn` call; this module never logs.
|
|
16
15
|
*/
|
|
17
16
|
/** Greppable prefix for a loop-gate shortfall (pause requested, world's loop
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { Container } from 'pixi.js';
|
|
26
26
|
import type * as THREE from 'three';
|
|
27
|
+
import type { WorldRendererConfig } from '../world3d-react/renderer-config';
|
|
27
28
|
import type { AdapterSurface } from './adapter-surface';
|
|
28
29
|
import type { AuthoringAdapter } from './authoring';
|
|
29
30
|
import type { HostContextFor } from './host-context';
|
|
@@ -75,8 +76,8 @@ export interface MountedRootBase {
|
|
|
75
76
|
* not a promise: implement `setPaused`/`step` only where the game exposes
|
|
76
77
|
* a sanctioned pause/step mechanism. Gating a raw-rAF loop from outside
|
|
77
78
|
* was demonstrated and REJECTED (D5 — it halts the loop rather than
|
|
78
|
-
* pausing it)
|
|
79
|
-
*
|
|
79
|
+
* pausing it). Where `setPaused` is absent the host reports the
|
|
80
|
+
* shortfall loudly, naming the mechanism that would close it (T7.6).
|
|
80
81
|
*/
|
|
81
82
|
readonly drivesOwnLoop: boolean;
|
|
82
83
|
update?(dt: number): void;
|
|
@@ -105,6 +106,25 @@ export interface MountedThreeRoot extends MountedRootBase {
|
|
|
105
106
|
/** The live scene + camera the editor inspects/renders for authoring. */
|
|
106
107
|
readonly scene: THREE.Scene;
|
|
107
108
|
readonly camera: THREE.Camera;
|
|
109
|
+
/**
|
|
110
|
+
* The colour pipeline this world was authored for, REPORTED rather than
|
|
111
|
+
* applied — the adapter has already applied it to the renderer its own host
|
|
112
|
+
* handed it (`world3d-react/renderer-config.ts`).
|
|
113
|
+
*
|
|
114
|
+
* It is here because the host that MOUNTS a world is not always the host
|
|
115
|
+
* that DRAWS it. The editor's design session mounts against a
|
|
116
|
+
* non-rasterizing renderer on purpose and then draws the mounted scene with
|
|
117
|
+
* the editor viewport's own `WebGLRenderer`, so the world's declaration
|
|
118
|
+
* lands on a surface with no pixels while the surface with pixels never
|
|
119
|
+
* hears it. Reporting the declaration is what lets that host apply the same
|
|
120
|
+
* config, through the same `applyWorldRendererConfig`, to the renderer that
|
|
121
|
+
* actually produces the frame.
|
|
122
|
+
*
|
|
123
|
+
* Absent means the world declared nothing and the drawing host should leave
|
|
124
|
+
* its own configuration alone — the same "absent = don't touch" rule the
|
|
125
|
+
* config's own fields follow.
|
|
126
|
+
*/
|
|
127
|
+
readonly rendererConfig?: WorldRendererConfig | undefined;
|
|
108
128
|
}
|
|
109
129
|
/** A live, mounted canvas world (T7.3) — the pixi analog of
|
|
110
130
|
* {@link MountedThreeRoot}. `stage` is the pixi world container the host
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/root-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C;;;;;;OAMG;IACH,QAAQ,IAAI,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,IAAI,CAAC,IAAI,IAAI,CAAC;IAEd,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,OAAO,IAAI,IAAI,CAAC;IAEhB,gFAAgF;IAChF,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC;;;;iBAIa;IACb,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;mBAImB;AACnB,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/root-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-three-root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/setup-three-root-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"setup-three-root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/setup-three-root-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoCrD,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,4BAA4B,CAAC;AAO/F,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAe,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGpE,sFAAsF;AACtF,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,wEAAwE;IACxE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+EAA+E;IAC/E,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,+FAA+F;IAC/F,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACxD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC;AAeD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,WAAW,CAErE;AAED,qBAAa,qBAAsB,YAAW,WAAW;IAE3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBACS,MAAM,GAAE,oBAAyB;IAKxD,KAAK,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAs1BpE"}
|
|
@@ -40,6 +40,7 @@ import { createCollisionSystem } from '../physics/collision-system';
|
|
|
40
40
|
import { createPhysicsRegistry } from '../physics/physics-registry';
|
|
41
41
|
import { createTransformWriter } from '../physics/transform-writer';
|
|
42
42
|
import { updateSceneLODs } from '../render/lod';
|
|
43
|
+
import { createSoftParticleDepthPass } from '../render/soft-particle-depth';
|
|
43
44
|
import { disposeSparkRendererWhenIdle, SPARK_DISCOVERY_INTERVAL_MS, sceneHasGaussianSplat, shouldDiscoverGaussianSplat, } from '../render/spark-renderer-lifecycle';
|
|
44
45
|
import { ViewportShadingRenderer } from '../render/viewport-shading';
|
|
45
46
|
import { createDebugRegistry, getDebugRegistry, } from '../runtime/debug-registry';
|
|
@@ -125,6 +126,10 @@ export class SetupThreeRootAdapter {
|
|
|
125
126
|
const audio = headless ? headlessAudio() : setupAudio(camera);
|
|
126
127
|
// --- Particles (three.quarks BatchedRenderer) ---
|
|
127
128
|
const particles = setupParticles(scene);
|
|
129
|
+
// Scene depth for any soft-particle system registered against this world.
|
|
130
|
+
// Inert (not one traversal, not one draw) until `registerParticleSystem`
|
|
131
|
+
// arms it — see `../render/soft-particle-depth.ts`.
|
|
132
|
+
const softParticleDepth = createSoftParticleDepthPass();
|
|
128
133
|
// --- Debug draw / assets ---
|
|
129
134
|
const debugDraw = createDebugDraw(scene);
|
|
130
135
|
const assets = host.assets;
|
|
@@ -218,6 +223,13 @@ export class SetupThreeRootAdapter {
|
|
|
218
223
|
// the assembled capture to a pending captureFrame() and restores the
|
|
219
224
|
// patched context — in the SAME finally as gpuTimer.end(). Spark is
|
|
220
225
|
// added before this so its draws fall inside the captured frame.
|
|
226
|
+
// Soft-particle scene depth goes in FRONT of the frame's own draw:
|
|
227
|
+
// the fade samples the geometry behind the particle, which cannot be
|
|
228
|
+
// the depth attachment the particle is drawn into. Deliberately
|
|
229
|
+
// OUTSIDE the capture and profiler brackets below — it is a prepass,
|
|
230
|
+
// not part of the frame's presentation, and a captured frame should
|
|
231
|
+
// show the draws the user sees.
|
|
232
|
+
softParticleDepth.render(renderer, scene, camera);
|
|
221
233
|
renderDebugWiring?.beforeRender();
|
|
222
234
|
// Issue #1504: CPU render submission, bracketed as its OWN profiler
|
|
223
235
|
// phase nested inside the frame's enclosing `render` phase. This is
|
|
@@ -720,6 +732,9 @@ export class SetupThreeRootAdapter {
|
|
|
720
732
|
renderVitals?.dispose();
|
|
721
733
|
renderVitals = null;
|
|
722
734
|
});
|
|
735
|
+
// This mount created the depth pass, so this mount ends it — the render
|
|
736
|
+
// target it holds is drawing-buffer sized and nothing else can reach it.
|
|
737
|
+
safeStep('softParticleDepth.dispose', () => softParticleDepth.dispose());
|
|
723
738
|
// Release analyser taps BEFORE the audio teardown below. A consumer
|
|
724
739
|
// (the editor's meter poll) should dispose its own handle, but Stop must
|
|
725
740
|
// never leak taps regardless.
|
|
@@ -13,7 +13,58 @@
|
|
|
13
13
|
import type * as THREE from 'three';
|
|
14
14
|
import type { RenderMemorySnapshot } from '../dev/render-memory';
|
|
15
15
|
import type { FrameCapture } from '../dev/webgl-frame-capture';
|
|
16
|
+
import type { PhysicsAdapter2D } from '../pixi/system-adapters';
|
|
16
17
|
import type { Transform, TransformOwner } from './transform';
|
|
18
|
+
export type PhysicsColliderShape = {
|
|
19
|
+
readonly type: 'cuboid';
|
|
20
|
+
readonly halfExtents: readonly [number, number, number];
|
|
21
|
+
} | {
|
|
22
|
+
readonly type: 'ball';
|
|
23
|
+
readonly radius: number;
|
|
24
|
+
} | {
|
|
25
|
+
readonly type: 'capsule';
|
|
26
|
+
readonly halfHeight: number;
|
|
27
|
+
readonly radius: number;
|
|
28
|
+
};
|
|
29
|
+
/** Plain-data projection of one native 3D physics collider. The owning
|
|
30
|
+
* physics implementation keeps the actual collider; editor authoring only
|
|
31
|
+
* receives the geometry needed to inspect and draw it. */
|
|
32
|
+
export interface PhysicsColliderSnapshot {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly shape: PhysicsColliderShape;
|
|
35
|
+
readonly position: readonly [number, number, number];
|
|
36
|
+
readonly rotation: readonly [number, number, number, number];
|
|
37
|
+
/** Absolute world scale used when the native shape was created. Source
|
|
38
|
+
* dimensions divide by this to recover @react-three/rapier's local args. */
|
|
39
|
+
readonly scale: readonly [number, number, number];
|
|
40
|
+
readonly sensor: boolean;
|
|
41
|
+
}
|
|
42
|
+
export type PhysicsJointType = 'fixed' | 'spherical' | 'revolute' | 'prismatic' | 'rope' | 'spring' | 'generic';
|
|
43
|
+
/** Plain-data projection of one native 3D impulse joint. Anchors are exposed
|
|
44
|
+
* in both native body-local space (for persistence) and world space (for
|
|
45
|
+
* viewport instruments); the actual joint remains owned by the physics lib. */
|
|
46
|
+
export interface PhysicsJointSnapshot {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly type: PhysicsJointType;
|
|
49
|
+
readonly body1: string;
|
|
50
|
+
readonly body2: string;
|
|
51
|
+
readonly anchor1: readonly [number, number, number];
|
|
52
|
+
readonly anchor2: readonly [number, number, number];
|
|
53
|
+
readonly worldAnchor1: readonly [number, number, number];
|
|
54
|
+
readonly worldAnchor2: readonly [number, number, number];
|
|
55
|
+
readonly body1Position: readonly [number, number, number];
|
|
56
|
+
readonly body2Position: readonly [number, number, number];
|
|
57
|
+
readonly body1Rotation: readonly [number, number, number, number];
|
|
58
|
+
readonly body2Rotation: readonly [number, number, number, number];
|
|
59
|
+
/** The free axis in body-1 local/world space for revolute/prismatic joints. */
|
|
60
|
+
readonly axis?: readonly [number, number, number];
|
|
61
|
+
readonly worldAxis?: readonly [number, number, number];
|
|
62
|
+
readonly limits?: {
|
|
63
|
+
readonly min: number;
|
|
64
|
+
readonly max: number;
|
|
65
|
+
};
|
|
66
|
+
readonly contactsEnabled: boolean;
|
|
67
|
+
}
|
|
17
68
|
/**
|
|
18
69
|
* Physics coordination so the editor can stably edit an object a simulation
|
|
19
70
|
* would otherwise overwrite every frame: `freeze → apply → unfreeze`.
|
|
@@ -22,11 +73,36 @@ import type { Transform, TransformOwner } from './transform';
|
|
|
22
73
|
* Object3D keying made this seam structurally unreachable for any non-three
|
|
23
74
|
* world: a Pixi or React world could never expose a physics inspector, because
|
|
24
75
|
* the seam spoke a vocabulary it has no values in — which is why the Pixi
|
|
25
|
-
* surface grew a parallel
|
|
76
|
+
* surface grew a parallel {@link PhysicsAdapter2D} (`pixi/system-adapters.ts`)
|
|
26
77
|
* instead of implementing this. Substrate-specific resolution (id → the native
|
|
27
78
|
* object) is the IMPLEMENTER's job, done once at its own boundary.
|
|
79
|
+
*
|
|
80
|
+
* ## `keyedBy`, and why the physics slot's union is TAGGED
|
|
81
|
+
*
|
|
82
|
+
* `SystemAdapters['physics']` holds EITHER shape ({@link PhysicsCarrier}),
|
|
83
|
+
* because a carrier addresses the thing being edited in its surface's own
|
|
84
|
+
* vocabulary and the canvas lane's is the display object itself. But the two
|
|
85
|
+
* shapes carry the SAME four member names and are otherwise indistinguishable
|
|
86
|
+
* at runtime, while the registry that holds them
|
|
87
|
+
* (`editor/src/authoring/active-systems.ts`) is GAME-scoped and surface-blind —
|
|
88
|
+
* one object every editor panel reads. An untagged union would therefore be one
|
|
89
|
+
* nothing could narrow, and every three-lane consumer
|
|
90
|
+
* (`getActivePhysics()?.freeze(nodeId)`, `play-mode.ts`'s `physics.commit(id,
|
|
91
|
+
* transform)`) would have to reach it through a cast asserting a fact nobody
|
|
92
|
+
* checked.
|
|
93
|
+
*
|
|
94
|
+
* So `keyedBy` is a real discriminant, and it is REQUIRED on
|
|
95
|
+
* {@link PhysicsAdapter2D} while OPTIONAL here on purpose: node-id keying is
|
|
96
|
+
* this seam's original and — until the canvas lane — only vocabulary, so every
|
|
97
|
+
* existing implementer stays valid untouched, and the shape that needs telling
|
|
98
|
+
* apart is the one obliged to say so. {@link nodeKeyedPhysics} /
|
|
99
|
+
* {@link displayKeyedPhysics} are the two narrowings; nothing else should test
|
|
100
|
+
* the tag by hand.
|
|
28
101
|
*/
|
|
29
102
|
export interface PhysicsAdapter {
|
|
103
|
+
/** Optional tag; see the interface comment. `'node-id'` is the only value,
|
|
104
|
+
* and omitting it means the same thing. */
|
|
105
|
+
readonly keyedBy?: 'node-id';
|
|
30
106
|
/** Returns `'physics'` when a body drives this node, else another owner. */
|
|
31
107
|
ownerOf(nodeId: string): TransformOwner;
|
|
32
108
|
/** Pause the body driving `nodeId` (kinematic / sleep / detach) for editing. */
|
|
@@ -51,6 +127,17 @@ export interface PhysicsAdapter {
|
|
|
51
127
|
* geometry).
|
|
52
128
|
*/
|
|
53
129
|
contactPoints?(): Float32Array;
|
|
130
|
+
/** Optional native collider inspection for one authored node. Implementers
|
|
131
|
+
* return only shapes they can describe exactly; absence means the editor
|
|
132
|
+
* offers no collider component UI. */
|
|
133
|
+
colliders?(nodeId: string): readonly PhysicsColliderSnapshot[];
|
|
134
|
+
/** Optional gesture preview against the implementation's real collider.
|
|
135
|
+
* Source persistence remains the AuthoringAdapter's responsibility. */
|
|
136
|
+
previewCollider?(colliderId: string, shape: PhysicsColliderShape): void;
|
|
137
|
+
/** Optional native impulse-joint inspection for joints attached to one node. */
|
|
138
|
+
joints?(nodeId: string): readonly PhysicsJointSnapshot[];
|
|
139
|
+
/** Optional gesture preview against the implementation's real local anchor. */
|
|
140
|
+
previewJointAnchor?(jointId: string, endpoint: 0 | 1, anchor: readonly [number, number, number]): void;
|
|
54
141
|
}
|
|
55
142
|
export interface NetPeer {
|
|
56
143
|
id: string;
|
|
@@ -347,10 +434,67 @@ export interface AudioAdapter {
|
|
|
347
434
|
* environment cannot meter (headless world with no real AudioContext) —
|
|
348
435
|
* the UI says so rather than showing frozen zeros. */
|
|
349
436
|
acquireMeters?(): AudioMeterHandle | null;
|
|
437
|
+
/** Optional: tap the real mixed output for a gameplay recording. The
|
|
438
|
+
* returned stream is the implementation's native browser audio stream; the
|
|
439
|
+
* caller must dispose the tap when recording ends. Absence means video can
|
|
440
|
+
* still be recorded, honestly without an audio track. */
|
|
441
|
+
acquireRecordingStream?(): AudioRecordingHandle | null;
|
|
350
442
|
/** Optional: the audio event ring (bounded, drops oldest). `sinceSeq`
|
|
351
443
|
* filters to `seq > sinceSeq` — the consumer's fence. */
|
|
352
444
|
audioEvents?(sinceSeq?: number): AudioDebugEvent[];
|
|
353
445
|
}
|
|
446
|
+
/** One temporary tap of an audio adapter's mixed output. */
|
|
447
|
+
export interface AudioRecordingHandle {
|
|
448
|
+
readonly stream: MediaStream;
|
|
449
|
+
dispose(): void;
|
|
450
|
+
}
|
|
451
|
+
/** One native camera known to a runtime camera controller.
|
|
452
|
+
*
|
|
453
|
+
* `id` is the controller's own stable key (a shot name, virtual-camera id,
|
|
454
|
+
* etc.). `nativeId` is OPTIONAL identity from the underlying scene object —
|
|
455
|
+
* Three.js implementations normally use `Object3D.uuid`; another adapter may
|
|
456
|
+
* use its renderer's equivalent. The editor uses it only to correlate an
|
|
457
|
+
* already-selected native camera with this read-only runtime projection. */
|
|
458
|
+
export interface CameraRuntimeCamera {
|
|
459
|
+
id: string;
|
|
460
|
+
label: string;
|
|
461
|
+
nativeId?: string;
|
|
462
|
+
/** Optional because ownership/cut controllers do not necessarily arbitrate
|
|
463
|
+
* by priority. A camera brain that does may expose the real value it uses. */
|
|
464
|
+
priority?: number;
|
|
465
|
+
enabled?: boolean;
|
|
466
|
+
}
|
|
467
|
+
/** A transition the controller is evaluating now. Plain data only: no
|
|
468
|
+
* renderer camera, tween, graph node, or editor-authored state crosses the
|
|
469
|
+
* seam. */
|
|
470
|
+
export interface CameraRuntimeTransition {
|
|
471
|
+
fromCameraId: string | null;
|
|
472
|
+
toCameraId: string;
|
|
473
|
+
durationSeconds: number;
|
|
474
|
+
elapsedSeconds: number;
|
|
475
|
+
progress: number;
|
|
476
|
+
}
|
|
477
|
+
/** Read-only runtime state for a selected native camera's Inspector section.
|
|
478
|
+
* Runtime follow/aim/collision stays project behavior; this snapshot merely
|
|
479
|
+
* reports what that behavior already decided. */
|
|
480
|
+
export interface CameraRuntimeSnapshot {
|
|
481
|
+
cameras: readonly CameraRuntimeCamera[];
|
|
482
|
+
activeCameraId: string | null;
|
|
483
|
+
/** The behavior/cinematic currently allowed to drive the rendered camera. */
|
|
484
|
+
activeControllerId: string | null;
|
|
485
|
+
ownershipDepth: number;
|
|
486
|
+
transition: CameraRuntimeTransition | null;
|
|
487
|
+
}
|
|
488
|
+
/** Camera-controller INTROSPECTION — not a camera graph or controller API.
|
|
489
|
+
*
|
|
490
|
+
* Games continue to author ordinary native cameras and drive them with their
|
|
491
|
+
* own TS/TSX. A controller registers this adapter only when it can truthfully
|
|
492
|
+
* expose its active camera, priority and blend state. Absence hides the
|
|
493
|
+
* runtime UI; the editor never infers a brain from scene structure. */
|
|
494
|
+
export interface CameraAdapter {
|
|
495
|
+
snapshot(): CameraRuntimeSnapshot;
|
|
496
|
+
subscribe(listener: () => void): Unsubscribe;
|
|
497
|
+
}
|
|
354
498
|
/** One tick-stamped debug event (`ctx.debug.emit`, spec §3.3): `tick`/`simT`
|
|
355
499
|
* are the engine's own counters AT EMISSION, so events and state reads
|
|
356
500
|
* correlate frame-exactly across every door (in-page bridge, relay, panels).
|
|
@@ -438,15 +582,30 @@ export interface RenderDebugAdapter {
|
|
|
438
582
|
captureFrame(): Promise<FrameCapture>;
|
|
439
583
|
memorySnapshot?(): RenderMemorySnapshot;
|
|
440
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* A physics carrier in EITHER surface's vocabulary — node-id keyed for a three
|
|
587
|
+
* world, display-object keyed for a canvas one. Tagged by `keyedBy`; see
|
|
588
|
+
* {@link PhysicsAdapter}'s comment for why, and use the two narrowings below
|
|
589
|
+
* rather than reading the tag directly.
|
|
590
|
+
*/
|
|
591
|
+
export type PhysicsCarrier = PhysicsAdapter | PhysicsAdapter2D;
|
|
592
|
+
/** The carrier a NODE-ID-keyed consumer may call, or `null` — including when
|
|
593
|
+
* the slot holds a display-keyed carrier, which such a consumer must not
|
|
594
|
+
* call and cannot correctly address. */
|
|
595
|
+
export declare function nodeKeyedPhysics(carrier: PhysicsCarrier | null | undefined): PhysicsAdapter | null;
|
|
596
|
+
/** The carrier a DISPLAY-keyed consumer may call, or `null`. Mirror of
|
|
597
|
+
* {@link nodeKeyedPhysics}. */
|
|
598
|
+
export declare function displayKeyedPhysics(carrier: PhysicsCarrier | null | undefined): PhysicsAdapter2D | null;
|
|
441
599
|
/**
|
|
442
600
|
* The set of optional subsystem providers a mounted game may expose. Absence of
|
|
443
601
|
* a provider means "capability not supported" — the editor degrades gracefully.
|
|
444
602
|
*/
|
|
445
603
|
export interface SystemAdapters {
|
|
446
|
-
physics?:
|
|
604
|
+
physics?: PhysicsCarrier;
|
|
447
605
|
networking?: NetworkingAdapter;
|
|
448
606
|
navigation?: NavigationAdapter;
|
|
449
607
|
audio?: AudioAdapter;
|
|
608
|
+
camera?: CameraAdapter;
|
|
450
609
|
debug?: DebugAdapter;
|
|
451
610
|
renderDebug?: RenderDebugAdapter;
|
|
452
611
|
}
|