@vgai/engine 0.4.1 → 0.5.0-canary.20260719.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -15
- package/package.json +11 -25
- package/schemas/engine-capabilities.json +10 -10
- package/schemas/entity2d.schema.json +468 -0
- package/schemas/mat.schema.json +2 -33
- package/schemas/prefab.schema.json +16 -172
- package/schemas/scn2d.schema.json +42 -23
- package/schemas/vgai-game.schema.json +34 -0
- package/schemas/vscn.schema.json +16 -172
- package/src/adapter/authoring.ts +152 -2
- package/src/adapter/colyseus-networking-adapter.ts +35 -1
- package/src/adapter/first-party-systems.ts +7 -1
- package/src/adapter/game-adapter.ts +13 -0
- package/src/adapter/index.ts +25 -0
- package/src/adapter/rapier-physics-adapter.ts +55 -2
- package/src/adapter/system-adapter.ts +249 -2
- package/src/adapter/vgai-scene-game-adapter.ts +149 -25
- package/src/ai/navigation.ts +28 -0
- package/src/animation/clip-map.ts +1 -8
- package/src/animation/theatre-director.ts +50 -0
- package/src/animation/xstate-animation-binding.ts +6 -0
- package/src/audio/audio-introspection.ts +290 -0
- package/src/audio/tone-context.ts +46 -0
- package/src/dev/chrome-trace.ts +153 -0
- package/src/dev/performance-profiler.ts +93 -6
- package/src/dev/render-debug-adapter.ts +199 -0
- package/src/dev/render-memory.ts +243 -0
- package/src/dev/webgl-frame-capture.ts +424 -0
- package/src/ecs/component-manager.ts +43 -10
- package/src/ecs/game-component.ts +39 -10
- package/src/input/input-manager.ts +24 -19
- package/src/input/input-types.ts +1 -1
- package/src/loader.ts +7 -0
- package/src/manifest/load.ts +14 -0
- package/src/manifest/schema.ts +65 -0
- package/src/react/game-state.tsx +1 -1
- package/src/render/render-batch-system.ts +26 -12
- package/src/render/spark-renderer-lifecycle.ts +64 -0
- package/src/runtime/create-runtime.ts +33 -5
- package/src/runtime/debug-bridge.ts +5 -5
- package/src/runtime/game.ts +102 -20
- package/src/runtime/mount-manifest.ts +1 -1
- package/src/runtime/render-control.ts +121 -0
- package/src/runtime/types.ts +1 -1
- package/src/scene/asset-loaders.ts +77 -3
- package/src/scene/instance-mesh.ts +25 -0
- package/src/scene/material-factory.ts +4 -15
- package/src/scene/mesh-shadow.ts +18 -0
- package/src/scene/particles-factory.ts +59 -0
- package/src/scene/scene-loader.ts +41 -16
- package/src/scene/schema/instances.ts +1 -2
- package/src/scene/schema/material.ts +83 -94
- package/src/scene/schema/mesh.ts +76 -90
- package/src/scene/schema/scene-file.ts +1 -2
- package/src/scene/user-data.ts +30 -14
- package/src/setup/setup-renderer.ts +6 -1
- package/src/world2d/asset-paths2d.ts +44 -0
- package/src/world2d/collision-2d.ts +7 -14
- package/src/world2d/entity2d-asset.ts +22 -0
- package/src/world2d/index.ts +27 -2
- package/src/world2d/physics2d-transform.ts +173 -0
- package/src/world2d/physics2d-units.ts +10 -0
- package/src/world2d/pixi-game-adapter.ts +148 -36
- package/src/world2d/scene2d-identity.ts +49 -0
- package/src/world2d/scene2d-loader.ts +243 -119
- package/src/world2d/schema/entity2d.ts +51 -33
- package/src/world2d/schema/physics2d.ts +14 -3
- package/src/world2d/schema/sprite.ts +32 -4
- package/src/world2d/schema/tilemap.ts +26 -9
- package/src/world2d/transform-writer-2d.ts +29 -11
- package/src/world2d/types.ts +21 -8
- package/src/world3d-react/behavior.tsx +138 -0
- package/src/world3d-react/engine-bridge.ts +48 -0
- package/src/world3d-react/index.ts +44 -0
- package/src/world3d-react/r3f-adapter.tsx +303 -0
- package/src/world3d-react/world-context.ts +294 -0
- package/vendor/realism-effects/LICENSE.md +21 -0
- package/vendor/realism-effects/UPSTREAM.md +19 -0
- package/vendor/realism-effects/dist/index.cjs +3447 -0
- package/vendor/realism-effects/dist/index.d.ts +59 -0
- package/vendor/realism-effects/dist/index.js +3434 -0
- package/vendor/realism-effects/package.json +23 -0
- package/src/character/cloth-sim.ts +0 -533
- package/src/character/spring-chain.ts +0 -307
- package/src/humanoid/body.ts +0 -663
- package/src/humanoid/clips.ts +0 -149
- package/src/humanoid/compose.ts +0 -209
- package/src/humanoid/generate.ts +0 -189
- package/src/humanoid/index.ts +0 -36
- package/src/humanoid/schema.ts +0 -108
- package/src/humanoid/skeleton.ts +0 -345
- package/src/react/humanoid-bake.document.tsx +0 -337
- package/src/scene/geometries/index.ts +0 -7
- package/src/scene/geometries/terrain.ts +0 -42
- package/src/scene/geometry-registry.ts +0 -42
- package/src/scene/instance-registry.ts +0 -84
- package/src/scene/instancers/grid.ts +0 -38
- package/src/scene/instancers/index.ts +0 -7
- package/src/scene/material-registry.ts +0 -73
- package/src/scene/materials/index.ts +0 -7
- package/src/scene/materials/water.ts +0 -56
- package/src/world2d/components-2d.ts +0 -86
- package/tools/humanoid-bake.tool.ts +0 -274
|
@@ -9,16 +9,61 @@
|
|
|
9
9
|
import type RAPIER from '@dimforge/rapier3d-compat';
|
|
10
10
|
import type * as THREE from 'three';
|
|
11
11
|
import type { PhysicsRegistry } from '../physics/physics-registry';
|
|
12
|
+
import type { PhysicsContext } from '../setup/setup-physics';
|
|
12
13
|
import type { PhysicsAdapter } from './system-adapter';
|
|
13
14
|
import type { Transform, TransformOwner } from './transform';
|
|
14
15
|
|
|
15
16
|
export function createRapierPhysicsAdapter(
|
|
16
17
|
registry: PhysicsRegistry,
|
|
17
|
-
|
|
18
|
+
physics?: PhysicsContext | null,
|
|
18
19
|
): PhysicsAdapter {
|
|
19
20
|
/** Saved body types while frozen, so unfreeze can restore them. */
|
|
20
21
|
const frozen = new Map<RAPIER.RigidBody, number>();
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Reused backing store for `contactPoints` (grown ×2 on demand, never
|
|
25
|
+
* shrunk) — the seam contract says the returned view is only valid until
|
|
26
|
+
* the next call, exactly so this never allocates per frame once warm.
|
|
27
|
+
*/
|
|
28
|
+
let contactBuf = new Float32Array(64 * 3);
|
|
29
|
+
let contactCount = 0;
|
|
30
|
+
|
|
31
|
+
const pushContact = (x: number, y: number, z: number): void => {
|
|
32
|
+
if ((contactCount + 1) * 3 > contactBuf.length) {
|
|
33
|
+
const grown = new Float32Array(contactBuf.length * 2);
|
|
34
|
+
grown.set(contactBuf);
|
|
35
|
+
contactBuf = grown;
|
|
36
|
+
}
|
|
37
|
+
contactBuf[contactCount * 3] = x;
|
|
38
|
+
contactBuf[contactCount * 3 + 1] = y;
|
|
39
|
+
contactBuf[contactCount * 3 + 2] = z;
|
|
40
|
+
contactCount++;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const appendManifoldContacts = (manifold: RAPIER.TempContactManifold): void => {
|
|
44
|
+
const count = manifold.numSolverContacts();
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
// Solver contacts are WORLD-space (unlike localContactPoint1/2).
|
|
47
|
+
const p = manifold.solverContactPoint(i);
|
|
48
|
+
if (p) pushContact(p.x, p.y, p.z);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const collectContactPoints = (): Float32Array => {
|
|
53
|
+
if (!physics) return contactBuf.subarray(0, 0);
|
|
54
|
+
const world = physics.rapierWorld;
|
|
55
|
+
contactCount = 0;
|
|
56
|
+
for (const [, refs] of registry.entries()) {
|
|
57
|
+
world.contactPairsWith(refs.collider, (other) => {
|
|
58
|
+
// Each touching pair is enumerated from BOTH sides — process it only
|
|
59
|
+
// from the lower-handle side (also skips self).
|
|
60
|
+
if (other.handle <= refs.collider.handle) return;
|
|
61
|
+
world.contactPair(refs.collider, other, appendManifoldContacts);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return contactBuf.subarray(0, contactCount * 3);
|
|
65
|
+
};
|
|
66
|
+
|
|
22
67
|
return {
|
|
23
68
|
ownerOf(o: THREE.Object3D): TransformOwner {
|
|
24
69
|
const body = registry.get(o)?.body;
|
|
@@ -51,6 +96,14 @@ export function createRapierPhysicsAdapter(
|
|
|
51
96
|
frozen.delete(body);
|
|
52
97
|
}
|
|
53
98
|
},
|
|
54
|
-
debugDraw: () => debugMesh ?? null,
|
|
99
|
+
debugDraw: () => physics?.debugMesh ?? null,
|
|
100
|
+
setDebugDrawEnabled(enabled: boolean): void {
|
|
101
|
+
if (!physics) return;
|
|
102
|
+
// Same two fields the in-game KeyP toggle flips — the adapter's render
|
|
103
|
+
// system reads `debugEnabled` and feeds `debugRender()` into the mesh.
|
|
104
|
+
physics.debugEnabled = enabled;
|
|
105
|
+
physics.debugMesh.visible = enabled;
|
|
106
|
+
},
|
|
107
|
+
contactPoints: collectContactPoints,
|
|
55
108
|
};
|
|
56
109
|
}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type * as THREE from 'three';
|
|
15
|
+
import type { RenderMemorySnapshot } from '../dev/render-memory';
|
|
16
|
+
import type { FrameCapture } from '../dev/webgl-frame-capture';
|
|
15
17
|
import type { ActionValueType, Vector2 } from '../input/input-types';
|
|
16
18
|
import type { Transform, TransformOwner } from './transform';
|
|
17
19
|
|
|
@@ -30,6 +32,20 @@ export interface PhysicsAdapter {
|
|
|
30
32
|
unfreeze(o: THREE.Object3D): void;
|
|
31
33
|
/** Optional debug-draw object (collider wireframes). */
|
|
32
34
|
debugDraw?(): THREE.Object3D | null;
|
|
35
|
+
/**
|
|
36
|
+
* Optional: turn the engine's per-frame physics debug rendering on/off —
|
|
37
|
+
* the first-party implementation drives Rapier's `debugRender()` into the
|
|
38
|
+
* `debugDraw()` object inside the game's own render system, so an editor
|
|
39
|
+
* overlay toggle costs the editor zero per-frame work (W2a §2.3c).
|
|
40
|
+
*/
|
|
41
|
+
setDebugDrawEnabled?(enabled: boolean): void;
|
|
42
|
+
/**
|
|
43
|
+
* Optional: live world-space contact points this frame, as xyz triples.
|
|
44
|
+
* Returns a view over a REUSED backing buffer — valid only until the next
|
|
45
|
+
* call (no per-frame allocation churn; callers copy into their own
|
|
46
|
+
* geometry).
|
|
47
|
+
*/
|
|
48
|
+
contactPoints?(): Float32Array;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
export interface NetPeer {
|
|
@@ -59,7 +75,61 @@ export interface ReplicationStats {
|
|
|
59
75
|
|
|
60
76
|
export type Unsubscribe = () => void;
|
|
61
77
|
|
|
62
|
-
/**
|
|
78
|
+
/** One captured network message, for the inspector's message log (W3b).
|
|
79
|
+
* `seq` is an adapter-lifetime MONOTONIC counter — the ring-buffer fence a
|
|
80
|
+
* consumer polls with (`messageEvents(sinceSeq)`), mirroring
|
|
81
|
+
* `DebugAdapter.events`' seq discipline. `time` is a `performance.now()`-
|
|
82
|
+
* domain ms timestamp at capture. */
|
|
83
|
+
export interface NetMessageEvent {
|
|
84
|
+
seq: number;
|
|
85
|
+
time: number;
|
|
86
|
+
direction: 'in' | 'out';
|
|
87
|
+
/** Message type/channel (e.g. `'input'`, `'players.add'`, `'state.phase'`). */
|
|
88
|
+
type: string;
|
|
89
|
+
/** Approximate payload size in bytes, when measurable. OMITTED — never
|
|
90
|
+
* fabricated — when the transport doesn't expose it (e.g. Colyseus schema
|
|
91
|
+
* patches observed via the Callbacks API carry no byte count). */
|
|
92
|
+
size?: number;
|
|
93
|
+
/** `true` when the link conditioner (`setConditioning`) dropped this
|
|
94
|
+
* outbound message instead of sending it — logged so the inspector can
|
|
95
|
+
* show the drop honestly rather than silently losing it. */
|
|
96
|
+
dropped?: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Send/receive rates for the inspector's sparklines (W3b). The byte fields
|
|
100
|
+
* are OPTIONAL capabilities-within-the-capability: an implementer omits a
|
|
101
|
+
* direction it cannot measure (never reports a fabricated 0). */
|
|
102
|
+
export interface NetRates {
|
|
103
|
+
msgsInPerSec: number;
|
|
104
|
+
msgsOutPerSec: number;
|
|
105
|
+
bytesInPerSec?: number;
|
|
106
|
+
bytesOutPerSec?: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Client-side link-conditioning parameters, applied AT THE ADAPTER SEAM
|
|
110
|
+
* (W3b M3) — the implementer wraps its own transport calls (e.g. delaying/
|
|
111
|
+
* dropping around `room.send`); nobody patches transport internals. */
|
|
112
|
+
export interface NetConditioning {
|
|
113
|
+
/** Fixed added delay per outbound message, ms. */
|
|
114
|
+
latencyMs: number;
|
|
115
|
+
/** Uniform random extra delay in `[0, jitterMs)`, ms. */
|
|
116
|
+
jitterMs: number;
|
|
117
|
+
/** Probability in `[0, 1]` that an outbound message is dropped. */
|
|
118
|
+
packetLoss: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* INTROSPECTION over replication state — NOT a networking transport.
|
|
123
|
+
*
|
|
124
|
+
* The optional members are CAPABILITIES in the `SystemAdapters` sense
|
|
125
|
+
* (absence means "not supported" and the editor's Network inspector marks
|
|
126
|
+
* that section absent — it never fabricates data): `getStateSnapshot` feeds
|
|
127
|
+
* the replicated-state tree, `messageEvents` the message log, `getRates` the
|
|
128
|
+
* rate sparklines, and `getConditioning`/`setConditioning` the latency/loss
|
|
129
|
+
* conditioner. The first-party Colyseus factory
|
|
130
|
+
* (`createColyseusNetworkingAdapter`) only attaches each one when its config
|
|
131
|
+
* supplies the accessor, so partial implementers degrade honestly.
|
|
132
|
+
*/
|
|
63
133
|
export interface NetworkingAdapter {
|
|
64
134
|
peers(): NetPeer[];
|
|
65
135
|
networkId(o: THREE.Object3D): string | null;
|
|
@@ -74,6 +144,22 @@ export interface NetworkingAdapter {
|
|
|
74
144
|
getReplicationStats(): ReplicationStats;
|
|
75
145
|
/** Subscribe to changes in connection state / room / replication stats. */
|
|
76
146
|
subscribe(cb: () => void): Unsubscribe;
|
|
147
|
+
/** Optional capability: one plain-data (JSON-safe) snapshot of the
|
|
148
|
+
* replicated room state, or `null` when not connected. Colyseus schema is
|
|
149
|
+
* self-describing (`state.toJSON()`), so the first-party implementation
|
|
150
|
+
* is one call — the inspector renders whatever tree comes back. */
|
|
151
|
+
getStateSnapshot?(): unknown;
|
|
152
|
+
/** Optional capability: captured message events with `seq > sinceSeq`
|
|
153
|
+
* (whole ring when omitted). Implementations ring-buffer (drop oldest);
|
|
154
|
+
* the inspector keeps its own consumer-side ring and fences on `seq`. */
|
|
155
|
+
messageEvents?(sinceSeq?: number): NetMessageEvent[];
|
|
156
|
+
/** Optional capability: msg + byte rates for the inspector's sparklines. */
|
|
157
|
+
getRates?(): NetRates;
|
|
158
|
+
/** Optional capability (paired with `setConditioning`): current
|
|
159
|
+
* conditioning parameters. */
|
|
160
|
+
getConditioning?(): NetConditioning;
|
|
161
|
+
/** Optional capability: apply link conditioning at the adapter seam. */
|
|
162
|
+
setConditioning?(c: NetConditioning): void;
|
|
77
163
|
}
|
|
78
164
|
|
|
79
165
|
/** One action's current value, tagged with its declared shape (F1 `ActionValueType`) so a
|
|
@@ -105,14 +191,125 @@ export interface NavPoint {
|
|
|
105
191
|
z: number;
|
|
106
192
|
}
|
|
107
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Navmesh bake parameters — the recast `rcConfig` vocabulary the scene schema
|
|
196
|
+
* (`SceneNavigationSchema`) also mirrors. All optional; an implementation
|
|
197
|
+
* applies its own defaults. `walkableHeight`/`walkableClimb`/`walkableRadius`
|
|
198
|
+
* are in VOXEL units (multiples of `cellHeight`/`cellSize`), per recast.
|
|
199
|
+
*/
|
|
200
|
+
export interface NavBakeParams {
|
|
201
|
+
cellSize?: number | undefined;
|
|
202
|
+
cellHeight?: number | undefined;
|
|
203
|
+
walkableSlopeAngle?: number | undefined;
|
|
204
|
+
walkableHeight?: number | undefined;
|
|
205
|
+
walkableClimb?: number | undefined;
|
|
206
|
+
walkableRadius?: number | undefined;
|
|
207
|
+
maxEdgeLen?: number | undefined;
|
|
208
|
+
maxSimplificationError?: number | undefined;
|
|
209
|
+
minRegionArea?: number | undefined;
|
|
210
|
+
mergeRegionArea?: number | undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** One crowd agent's live state — plain data (no engine/WASM handles), for the
|
|
214
|
+
* editor's play-mode crowd debug draw. */
|
|
215
|
+
export interface NavCrowdAgentState {
|
|
216
|
+
position: NavPoint;
|
|
217
|
+
velocity: NavPoint;
|
|
218
|
+
radius: number;
|
|
219
|
+
height: number;
|
|
220
|
+
}
|
|
221
|
+
|
|
108
222
|
/**
|
|
109
223
|
* Inspect a game's navigation mesh: whether one is built, query a path, and get a
|
|
110
224
|
* debug overlay. Coordination/introspection only — the editor doesn't own the build.
|
|
225
|
+
*
|
|
226
|
+
* The optional members are CAPABILITIES in the `SystemAdapters` sense (absence
|
|
227
|
+
* means "not supported", the editor degrades): `bake`/`exportData` let the
|
|
228
|
+
* editor's Navigation panel (re)build and persist a navmesh through the seam —
|
|
229
|
+
* the first-party recast implementation (`createNavigationAdapter`) is the
|
|
230
|
+
* blessed implementer — while an external game that owns its build simply
|
|
231
|
+
* omits them. `crowdAgents` feeds the play-mode crowd debug draw.
|
|
111
232
|
*/
|
|
112
233
|
export interface NavigationAdapter {
|
|
113
234
|
hasNavMesh(): boolean;
|
|
114
235
|
findPath(start: NavPoint, end: NavPoint): NavPoint[];
|
|
115
236
|
debugMesh(scene: THREE.Scene): THREE.Object3D | null;
|
|
237
|
+
/** (Re)build the navmesh from source meshes. Synchronous in the blessed
|
|
238
|
+
* recast/WASM implementation; returns `false` on a failed bake. */
|
|
239
|
+
bake?(meshes: THREE.Mesh[], params?: NavBakeParams): boolean;
|
|
240
|
+
/** Serialize the current navmesh to a binary blob (the editor persists it
|
|
241
|
+
* as the scene's `.navmesh` sidecar). Throws when nothing is built. */
|
|
242
|
+
exportData?(): Uint8Array;
|
|
243
|
+
/** Live crowd-agent snapshots for debug draw. Empty when no crowd. */
|
|
244
|
+
crowdAgents?(): NavCrowdAgentState[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** One node in an audio-graph snapshot (W3c, the Audio debugger tab). Edges
|
|
248
|
+
* point DOWNSTREAM: `outputs` holds the ids of nodes this node's output
|
|
249
|
+
* feeds (ultimately reaching the `destination` node). */
|
|
250
|
+
export interface AudioGraphNode {
|
|
251
|
+
id: string;
|
|
252
|
+
/** Node class/constructor name (`'GainNode'`, `'Gain'` for a Tone node,
|
|
253
|
+
* `'AudioDestinationNode'`, …) — whatever the implementation truthfully
|
|
254
|
+
* knows, never a prettified fabrication. */
|
|
255
|
+
type: string;
|
|
256
|
+
/** Human-facing label (`'Master'`, `'Music bus'`) when one exists. */
|
|
257
|
+
label?: string;
|
|
258
|
+
/** ids of downstream nodes this node's output connects to. */
|
|
259
|
+
outputs: string[];
|
|
260
|
+
/** Lifecycle/playback state when truthfully known (`'running'`,
|
|
261
|
+
* `'suspended'`, `'started'`…). OMITTED — never fabricated — otherwise. */
|
|
262
|
+
state?: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Musical-transport snapshot for the Audio debugger's transport strip
|
|
266
|
+
* (W3c). The first-party implementation reads Tone's real transport. */
|
|
267
|
+
export interface AudioTransportState {
|
|
268
|
+
state: 'started' | 'stopped' | 'paused';
|
|
269
|
+
/** Playback position along the transport's timeline, seconds. */
|
|
270
|
+
seconds: number;
|
|
271
|
+
bpm: number;
|
|
272
|
+
/** Musical position (`Bars:Beats:Sixteenths`) when a musical grid exists. */
|
|
273
|
+
position?: string;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** One bus level sample: `level` is linear RMS over the implementation's most
|
|
277
|
+
* recent analysis window (0 = silence; ~1 = full-scale). */
|
|
278
|
+
export interface AudioMeterFrame {
|
|
279
|
+
id: string;
|
|
280
|
+
label: string;
|
|
281
|
+
level: number;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** A live metering session. Metering costs real audio nodes (Tone.Meter /
|
|
285
|
+
* AnalyserNode taps), so it is ACQUIRED for exactly as long as a meter UI is
|
|
286
|
+
* visible and MUST be released via `dispose()` — the first-party handle
|
|
287
|
+
* disconnects its analyser taps there (no leaked nodes after Stop). */
|
|
288
|
+
export interface AudioMeterHandle {
|
|
289
|
+
/** Current level per bus. Cheap enough to poll at UI rate (~10 Hz). */
|
|
290
|
+
read(): AudioMeterFrame[];
|
|
291
|
+
/** Release the metering nodes. Idempotent. */
|
|
292
|
+
dispose(): void;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** One entry in the audio event ring (W3c). `seq` is an adapter-lifetime
|
|
296
|
+
* MONOTONIC counter — the same ring-buffer fence discipline as
|
|
297
|
+
* `NetMessageEvent.seq` / `DebugAdapter.events`. `time` is a
|
|
298
|
+
* `performance.now()`-domain ms timestamp at capture. `'error'` is reserved
|
|
299
|
+
* for implementations that can truthfully report one (the first-party
|
|
300
|
+
* adapter has no honest audio-error source today and never emits it). */
|
|
301
|
+
export interface AudioDebugEvent {
|
|
302
|
+
seq: number;
|
|
303
|
+
time: number;
|
|
304
|
+
kind:
|
|
305
|
+
| 'transport-start'
|
|
306
|
+
| 'transport-stop'
|
|
307
|
+
| 'transport-pause'
|
|
308
|
+
| 'context-statechange'
|
|
309
|
+
| 'mute'
|
|
310
|
+
| 'unmute'
|
|
311
|
+
| 'error';
|
|
312
|
+
detail?: string;
|
|
116
313
|
}
|
|
117
314
|
|
|
118
315
|
/**
|
|
@@ -123,14 +320,46 @@ export interface NavigationAdapter {
|
|
|
123
320
|
* cannot"). Absence on a world's `mounted.systems` means exactly that:
|
|
124
321
|
* `Game.play.pause()` reports it loudly once per world rather than silently
|
|
125
322
|
* leaving that world's audio playing under a "paused" game.
|
|
323
|
+
*
|
|
324
|
+
* The OPTIONAL methods below are the W3c read-only INTROSPECTION capabilities
|
|
325
|
+
* feeding the editor's Audio debugger tab (graph / transport / meters /
|
|
326
|
+
* events). They follow the W3a degradation ladder: an implementer omits what
|
|
327
|
+
* it cannot truthfully provide, and the editor marks that section absent —
|
|
328
|
+
* never fabricated. The full MIXER (sends/effects/gain editing) is SQ-4-gated
|
|
329
|
+
* and deliberately NOT part of this seam. The first-party implementation over
|
|
330
|
+
* the engine's own Tone/Web-Audio stack is
|
|
331
|
+
* `packages/engine/src/audio/audio-introspection.ts`; pre-existing
|
|
332
|
+
* mute-only registrants keep compiling untouched (every addition is
|
|
333
|
+
* optional).
|
|
126
334
|
*/
|
|
127
335
|
export interface AudioAdapter {
|
|
336
|
+
/** Resume/unlock this adapter's real audio context from a user gesture.
|
|
337
|
+
* Optional for external adapters that do not own a resumable Web Audio
|
|
338
|
+
* context; first-party edit and play adapters both provide it. */
|
|
339
|
+
resume?(): void;
|
|
128
340
|
/** `true` silences this world's audio; `false` restores it to whatever
|
|
129
341
|
* level it was at before silencing (an implementer's own concern — the
|
|
130
342
|
* first-party adapter restores its master-gain value, not a hardcoded 1). */
|
|
131
343
|
setMuted(muted: boolean): void;
|
|
132
344
|
/** Current muted state, for inspection (editor mute UI, proofs). */
|
|
133
345
|
isMuted(): boolean;
|
|
346
|
+
/** Optional: current audio-graph snapshot (active nodes + downstream
|
|
347
|
+
* edges). The first-party adapter reports the engine bus hierarchy plus
|
|
348
|
+
* every Tone node routed through `connectToneBusToMasterGain` — it cannot
|
|
349
|
+
* see a game's private Tone-internal wiring and does not pretend to. */
|
|
350
|
+
graphSnapshot?(): AudioGraphNode[];
|
|
351
|
+
/** Optional: musical-transport snapshot. Returns `null` — an honest
|
|
352
|
+
* "this world has no musical transport" — when none is active (e.g. the
|
|
353
|
+
* first-party adapter when the game never bridged Tone onto this world's
|
|
354
|
+
* context). Method ABSENT means the capability itself is unsupported. */
|
|
355
|
+
transportState?(): AudioTransportState | null;
|
|
356
|
+
/** Optional: begin a per-bus metering session. Returns `null` when the
|
|
357
|
+
* environment cannot meter (headless world with no real AudioContext) —
|
|
358
|
+
* the UI says so rather than showing frozen zeros. */
|
|
359
|
+
acquireMeters?(): AudioMeterHandle | null;
|
|
360
|
+
/** Optional: the audio event ring (bounded, drops oldest). `sinceSeq`
|
|
361
|
+
* filters to `seq > sinceSeq` — the consumer's fence. */
|
|
362
|
+
audioEvents?(sinceSeq?: number): AudioDebugEvent[];
|
|
134
363
|
}
|
|
135
364
|
|
|
136
365
|
/** One tick-stamped debug event (`ctx.debug.emit`, spec §3.3): `tick`/`simT`
|
|
@@ -170,7 +399,7 @@ export interface DebugCommandInfo {
|
|
|
170
399
|
* game-scoped introspection + actuation over whatever a game registers via
|
|
171
400
|
* `ctx.debug` (`registerStateProvider`/`registerCommand`/`emit`). NOT a
|
|
172
401
|
* gameplay API — this is the one seam the debug bridge, the editor's Debug
|
|
173
|
-
* Console/State Watch panels, and `@vgai/
|
|
402
|
+
* Console/State Watch panels, and `@vgai/e2e` all read/drive through.
|
|
174
403
|
*/
|
|
175
404
|
export interface DebugAdapter {
|
|
176
405
|
providers(): { name: string; tier: 'observable' | 'assisted' }[];
|
|
@@ -205,6 +434,23 @@ export interface DebugAdapter {
|
|
|
205
434
|
events(sinceTick?: number, sinceSeq?: number): TickStampedEvent[];
|
|
206
435
|
}
|
|
207
436
|
|
|
437
|
+
/**
|
|
438
|
+
* Read-only render-debugging introspection (W4b, F11 frame debugger +
|
|
439
|
+
* profiler) — NOT a renderer. `captureFrame` arms the first-party WebGL2
|
|
440
|
+
* capture (`dev/webgl-frame-capture.ts`) and resolves with the NEXT rendered
|
|
441
|
+
* frame's draw list, attributed by wrapping each renderable's `onBeforeRender`
|
|
442
|
+
* (three calls it immediately before that object's main-pass draw; shadow/
|
|
443
|
+
* composer-pass draws stay unattributed BY CONSTRUCTION — an honest gap, not a
|
|
444
|
+
* guess). It REJECTS — never fabricates — if no frame renders within a bounded
|
|
445
|
+
* timeout (a paused/stopped world). `memorySnapshot` is the W3a-style OPTIONAL
|
|
446
|
+
* capability: present only when the mount can supply `renderer.info` (a
|
|
447
|
+
* headless mount omits it, and the editor marks the section absent).
|
|
448
|
+
*/
|
|
449
|
+
export interface RenderDebugAdapter {
|
|
450
|
+
captureFrame(): Promise<FrameCapture>;
|
|
451
|
+
memorySnapshot?(): RenderMemorySnapshot;
|
|
452
|
+
}
|
|
453
|
+
|
|
208
454
|
/**
|
|
209
455
|
* The set of optional subsystem providers a mounted game may expose. Absence of
|
|
210
456
|
* a provider means "capability not supported" — the editor degrades gracefully.
|
|
@@ -217,4 +463,5 @@ export interface SystemAdapters {
|
|
|
217
463
|
navigation?: NavigationAdapter;
|
|
218
464
|
audio?: AudioAdapter;
|
|
219
465
|
debug?: DebugAdapter;
|
|
466
|
+
renderDebug?: RenderDebugAdapter;
|
|
220
467
|
}
|