@vgai/engine 0.5.11 → 0.5.13
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 +6 -5
- package/dist/adapter/authoring.d.ts +51 -28
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/body-marks.d.ts +59 -0
- package/dist/adapter/body-marks.d.ts.map +1 -0
- package/dist/adapter/body-marks.js +62 -0
- package/dist/adapter/constraint.d.ts +41 -0
- package/dist/adapter/constraint.d.ts.map +1 -0
- package/dist/adapter/constraint.js +27 -0
- package/dist/adapter/hierarchy-marks.d.ts +138 -0
- package/dist/adapter/hierarchy-marks.d.ts.map +1 -0
- package/dist/adapter/hierarchy-marks.js +148 -0
- package/dist/adapter/index.d.ts +1 -1
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/ingest/contract-system-adapters.d.ts +64 -0
- package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -0
- package/dist/adapter/ingest/contract-system-adapters.js +134 -0
- package/dist/adapter/ingest/game-contract.d.ts +87 -9
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +7 -6
- package/dist/adapter/ingest/scene-capture.d.ts +31 -4
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +29 -7
- package/dist/adapter/ingest/structural-ids.d.ts +1 -1
- package/dist/adapter/ingest/structural-ids.js +2 -2
- package/dist/adapter/reflection-probe.d.ts +53 -0
- package/dist/adapter/reflection-probe.d.ts.map +1 -0
- package/dist/adapter/reflection-probe.js +18 -0
- package/dist/asset-formats/index.d.ts +4 -10
- package/dist/asset-formats/index.d.ts.map +1 -1
- package/dist/asset-formats/index.js +3 -8
- package/dist/asset-formats/material.d.ts +0 -66
- package/dist/asset-formats/material.d.ts.map +1 -1
- package/dist/asset-formats/material.js +0 -8
- package/dist/asset-formats/mesh.d.ts +1 -2
- package/dist/asset-formats/mesh.d.ts.map +1 -1
- package/dist/asset-formats/mesh.js +3 -13
- package/dist/asset-formats/render-env.d.ts +2 -2
- package/dist/asset-parse-error.d.ts +4 -5
- package/dist/asset-parse-error.d.ts.map +1 -1
- package/dist/asset-parse-error.js +4 -5
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -0
- package/dist/data/curve.d.ts +116 -0
- package/dist/data/curve.d.ts.map +1 -0
- package/dist/data/curve.js +230 -0
- package/dist/data/vite-plugin-data.d.ts +2 -2
- package/dist/data/vite-plugin-data.d.ts.map +1 -1
- package/dist/data/vite-plugin-data.js +24 -15
- package/dist/dev/performance-profiler.d.ts +1 -0
- package/dist/dev/performance-profiler.d.ts.map +1 -1
- package/dist/dev/performance-profiler.js +25 -2
- package/dist/ecs/user-data.d.ts +49 -1
- package/dist/ecs/user-data.d.ts.map +1 -1
- package/dist/ecs/user-data.js +34 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/manifest/index.d.ts +3 -3
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +2 -2
- package/dist/manifest/load.d.ts +15 -67
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +11 -103
- package/dist/manifest/schema.d.ts +18 -103
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +59 -179
- package/dist/pixi/index.d.ts +2 -3
- package/dist/pixi/index.d.ts.map +1 -1
- package/dist/pixi/index.js +2 -3
- package/dist/pixi/ingest.d.ts +7 -75
- package/dist/pixi/ingest.d.ts.map +1 -1
- package/dist/pixi/ingest.js +6 -14
- package/dist/pixi/scene-capture.d.ts +2 -2
- package/dist/pixi/scene-capture.js +2 -2
- package/dist/react/use-data.d.ts +10 -5
- package/dist/react/use-data.d.ts.map +1 -1
- package/dist/react/use-data.js +15 -15
- package/dist/render/viewport-shading.d.ts +1 -1
- package/dist/render/viewport-shading.d.ts.map +1 -1
- package/dist/render/viewport-shading.js +14 -0
- package/dist/runtime/create-runtime.d.ts +12 -0
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +7 -2
- package/dist/runtime/mount-game.d.ts +1 -1
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +5 -1
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +69 -39
- package/dist/world3d-react/renderer-config.d.ts +15 -0
- package/dist/world3d-react/renderer-config.d.ts.map +1 -1
- package/dist/world3d-react/renderer-config.js +15 -0
- package/dist/world3d-react/world-context.d.ts +2 -1
- package/dist/world3d-react/world-context.d.ts.map +1 -1
- package/dist-config/config.js +140 -20
- package/dist-config/config.js.map +4 -4
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +10 -10
- package/schemas/vgai-project.schema.json +21 -76
- package/src/adapter/authoring.ts +54 -31
- package/src/adapter/body-marks.ts +69 -0
- package/src/adapter/constraint.ts +71 -0
- package/src/adapter/hierarchy-marks.ts +155 -0
- package/src/adapter/index.ts +2 -3
- package/src/adapter/ingest/contract-system-adapters.ts +174 -0
- package/src/adapter/ingest/game-contract.ts +98 -9
- package/src/adapter/ingest/scene-capture.ts +61 -11
- package/src/adapter/ingest/structural-ids.ts +2 -2
- package/src/adapter/reflection-probe.ts +75 -0
- package/src/asset-formats/index.ts +4 -10
- package/src/asset-formats/material.ts +0 -11
- package/src/asset-formats/mesh.ts +3 -16
- package/src/asset-parse-error.ts +4 -5
- package/src/config.ts +13 -0
- package/src/data/curve.ts +263 -0
- package/src/data/vite-plugin-data.ts +48 -14
- package/src/dev/performance-profiler.ts +21 -2
- package/src/ecs/user-data.ts +50 -0
- package/src/index.ts +2 -3
- package/src/manifest/index.ts +3 -15
- package/src/manifest/load.ts +27 -143
- package/src/manifest/schema.ts +65 -223
- package/src/pixi/index.ts +2 -15
- package/src/pixi/ingest.ts +13 -87
- package/src/pixi/scene-capture.ts +2 -2
- package/src/react/use-data.ts +15 -20
- package/src/render/viewport-shading.ts +16 -1
- package/src/runtime/create-runtime.ts +30 -5
- package/src/runtime/mount-game.ts +1 -1
- package/src/runtime/mount-manifest.ts +5 -1
- package/src/world3d-react/r3f-adapter.tsx +73 -39
- package/src/world3d-react/renderer-config.ts +31 -0
- package/src/world3d-react/world-context.ts +2 -1
- package/dist/asset-formats/instances.d.ts +0 -17
- package/dist/asset-formats/instances.d.ts.map +0 -1
- package/dist/asset-formats/instances.js +0 -31
- package/dist/asset-formats/parse.d.ts +0 -20
- package/dist/asset-formats/parse.d.ts.map +0 -1
- package/dist/asset-formats/parse.js +0 -34
- package/dist/pixi/ingest-iframe.d.ts +0 -82
- package/dist/pixi/ingest-iframe.d.ts.map +0 -1
- package/dist/pixi/ingest-iframe.js +0 -166
- package/dist/render/instance-mesh.d.ts +0 -5
- package/dist/render/instance-mesh.d.ts.map +0 -1
- package/dist/render/instance-mesh.js +0 -19
- package/schemas/mat.schema.json +0 -255
- package/src/asset-formats/instances.ts +0 -38
- package/src/asset-formats/parse.ts +0 -39
- package/src/pixi/ingest-iframe.ts +0 -255
- package/src/render/instance-mesh.ts +0 -25
|
@@ -15,9 +15,18 @@
|
|
|
15
15
|
* endpoint by endpoint: capture infers what it can (the scene), the game
|
|
16
16
|
* declares what inference can't reach (its DOM root, its session lifecycle).
|
|
17
17
|
*
|
|
18
|
-
* Origin (root), F17+F21 (lifecycle), WO-SYS1 (systems)
|
|
18
|
+
* Origin (root), F17+F21 (lifecycle), WO-SYS1 (systems), WO-SYS2
|
|
19
|
+
* (`systems.systemAdapters`).
|
|
19
20
|
*/
|
|
20
21
|
|
|
22
|
+
import type {
|
|
23
|
+
AudioAdapter,
|
|
24
|
+
NavigationAdapter,
|
|
25
|
+
NetworkingAdapter,
|
|
26
|
+
PhysicsAdapter,
|
|
27
|
+
RenderDebugAdapter,
|
|
28
|
+
} from '../system-adapter';
|
|
29
|
+
|
|
21
30
|
/**
|
|
22
31
|
* Session lifecycle endpoints. Declaring `start` means "I support cold
|
|
23
32
|
* mount": when the host sets `window.__vgaiMountCold` before the game's entry
|
|
@@ -66,6 +75,29 @@ export interface VgaiGameStateProvider {
|
|
|
66
75
|
read(): unknown;
|
|
67
76
|
}
|
|
68
77
|
|
|
78
|
+
/**
|
|
79
|
+
* One top-level semantic hierarchy group from the game's OWN model. `count`
|
|
80
|
+
* is recomputed by the game when the host reads the hierarchy; it is not
|
|
81
|
+
* inferred from render objects. The editor presents these rows beside the
|
|
82
|
+
* captured render tree, so a system-driven game can say what its content is
|
|
83
|
+
* without pretending its scene graph is authored truth.
|
|
84
|
+
*/
|
|
85
|
+
export interface VgaiGameHierarchyGroup {
|
|
86
|
+
id: string;
|
|
87
|
+
label: string;
|
|
88
|
+
count: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Synchronous read plus an optional game-owned invalidation signal. The
|
|
93
|
+
* editor never polls or guesses when a game's model changed; a dynamic game
|
|
94
|
+
* subscribes its ordinary authoring hierarchy to the same truth it reads.
|
|
95
|
+
*/
|
|
96
|
+
export interface VgaiGameHierarchyProvider {
|
|
97
|
+
(): VgaiGameHierarchyGroup[];
|
|
98
|
+
subscribe?(listener: () => void): () => void;
|
|
99
|
+
}
|
|
100
|
+
|
|
69
101
|
/**
|
|
70
102
|
* The game's SYSTEM surface: what its content actually IS, as opposed to how
|
|
71
103
|
* it renders. An ingested game's content is not always its scene graph — in a
|
|
@@ -73,9 +105,10 @@ export interface VgaiGameStateProvider {
|
|
|
73
105
|
* is reached through the game's own verbs, which `root`/`lifecycle` could not
|
|
74
106
|
* express.
|
|
75
107
|
*
|
|
76
|
-
*
|
|
77
|
-
* `adapter/ingest/contract-debug-adapter.ts
|
|
78
|
-
*
|
|
108
|
+
* Every member is read by a concrete consumer: commands/state are projected
|
|
109
|
+
* by `adapter/ingest/contract-debug-adapter.ts` onto the host's ordinary
|
|
110
|
+
* `DebugAdapter` (`adapter/system-adapter.ts`), while `hierarchy` is projected
|
|
111
|
+
* by the editor onto the ordinary `AuthoringAdapter.hierarchy` surface. Thus
|
|
79
112
|
* `game.commands()`/`game.state()` reach an ingested game through the same
|
|
80
113
|
* bridge first-party content uses, with no second vocabulary for agents to
|
|
81
114
|
* learn. That is deliberate scope: this
|
|
@@ -88,6 +121,62 @@ export interface VgaiGameSystems {
|
|
|
88
121
|
commands?: VgaiGameCommand[];
|
|
89
122
|
/** State reads, enumerated by `game.providers()` and read by `game.state(...)`. */
|
|
90
123
|
state?: VgaiGameStateProvider[];
|
|
124
|
+
/** Semantic hierarchy groups from the game's own model, recomputed on read. */
|
|
125
|
+
hierarchy?: VgaiGameHierarchyProvider;
|
|
126
|
+
/**
|
|
127
|
+
* The game's own {@link SystemAdapters} slots — the ingest-realm equivalent
|
|
128
|
+
* of a first-party world's `ctx.registerSystemAdapter`. Read and projected by
|
|
129
|
+
* `contract-system-adapters.ts` onto the mounted root's `systems` bag, so an
|
|
130
|
+
* ingested game's physics/audio/navigation/networking reach the editor's
|
|
131
|
+
* ordinary panels through the SAME registry a native world publishes to.
|
|
132
|
+
*/
|
|
133
|
+
systemAdapters?: VgaiGameSystemAdapters;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A slot's IMPLEMENTED-EMPTY answer: "this game genuinely has no X", carried
|
|
138
|
+
* with the source-level evidence that establishes it.
|
|
139
|
+
*
|
|
140
|
+
* This exists because `SystemAdapters` is capability-by-PRESENCE, and presence
|
|
141
|
+
* has only two values while the ingestion bar (docs/BRINGING-AN-EXISTING-GAME.md
|
|
142
|
+
* §The ingestion checklist) has three inputs: bound, positively-absent, and
|
|
143
|
+
* not-yet-answered. Omitting a slot cannot tell the last two apart — an editor
|
|
144
|
+
* reading a bare absence cannot know whether anyone ever looked. Declaring this
|
|
145
|
+
* record says someone looked, says what they found, and turns a work-order row
|
|
146
|
+
* into a terminal one.
|
|
147
|
+
*
|
|
148
|
+
* It is deliberately NOT an adapter whose methods return zeros: a
|
|
149
|
+
* `getConnectionState()` of `'disconnected'` on a game with no transport at all
|
|
150
|
+
* is a fabrication (it implies a connection that could exist), which the
|
|
151
|
+
* anti-shim rule forbids. The absence is stated, never simulated.
|
|
152
|
+
*/
|
|
153
|
+
export interface VgaiGameSystemEmpty {
|
|
154
|
+
/** Always `false` — the discriminant that separates this from an adapter. */
|
|
155
|
+
present: false;
|
|
156
|
+
/**
|
|
157
|
+
* Why the slot is empty, in SOURCE terms: what was searched and what was
|
|
158
|
+
* found. "no netcode anywhere in `src/`" is a finished answer; "not
|
|
159
|
+
* implemented yet" is not one, and a reviewer reading this field is meant to
|
|
160
|
+
* be able to re-run the search.
|
|
161
|
+
*/
|
|
162
|
+
evidence: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The declarable `SystemAdapters` slots, each either a real implementation or a
|
|
167
|
+
* positively-answered {@link VgaiGameSystemEmpty}.
|
|
168
|
+
*
|
|
169
|
+
* `debug` is deliberately NOT here: it is already the projection of
|
|
170
|
+
* `commands`/`state` above (`contract-debug-adapter.ts`), and a second door onto
|
|
171
|
+
* the same slot would let a game declare two different debug planes with no rule
|
|
172
|
+
* for which wins.
|
|
173
|
+
*/
|
|
174
|
+
export interface VgaiGameSystemAdapters {
|
|
175
|
+
physics?: PhysicsAdapter | VgaiGameSystemEmpty;
|
|
176
|
+
networking?: NetworkingAdapter | VgaiGameSystemEmpty;
|
|
177
|
+
navigation?: NavigationAdapter | VgaiGameSystemEmpty;
|
|
178
|
+
audio?: AudioAdapter | VgaiGameSystemEmpty;
|
|
179
|
+
renderDebug?: RenderDebugAdapter | VgaiGameSystemEmpty;
|
|
91
180
|
}
|
|
92
181
|
|
|
93
182
|
export interface VgaiGameContract {
|
|
@@ -114,11 +203,11 @@ export interface VgaiGameContract {
|
|
|
114
203
|
* forward-compatibility hinge: a future v2 game on a v1 host is ignored
|
|
115
204
|
* (pre-contract fallbacks apply) rather than half-interpreted.
|
|
116
205
|
*
|
|
117
|
-
* `scope` is the REALM to read from, and defaults to this module's own
|
|
118
|
-
* —
|
|
119
|
-
* one global.
|
|
120
|
-
*
|
|
121
|
-
*
|
|
206
|
+
* `scope` is the REALM to read from, and defaults to this module's own
|
|
207
|
+
* `window` — the realm every ingest mount runs the game's modules in, where the
|
|
208
|
+
* game and the host share one global. It is a parameter at all because the
|
|
209
|
+
* contract is a property of the realm the game runs in, never of the realm that
|
|
210
|
+
* happens to be asking.
|
|
122
211
|
*/
|
|
123
212
|
export function readGameContract(
|
|
124
213
|
scope: Window | null | undefined = typeof window === 'undefined' ? null : window,
|
|
@@ -70,11 +70,11 @@ export interface SceneCaptureOptions {
|
|
|
70
70
|
* each with its own offscreen renderer and its own little light rig) is a
|
|
71
71
|
* candidate to be captured AS THE GAME.
|
|
72
72
|
*
|
|
73
|
-
* That is what made
|
|
74
|
-
*
|
|
73
|
+
* That is what made the "a game bundling its own three is DETECTED, never
|
|
74
|
+
* silently mistaken for a capture" spec
|
|
75
75
|
* pass or fail on timing alone: whether a thumbnail happened to bake inside
|
|
76
|
-
* the game's
|
|
77
|
-
* preview scene as the game and
|
|
76
|
+
* the game's capture window. When it did, the editor adopted its own
|
|
77
|
+
* preview scene as the game and the mount reported success.
|
|
78
78
|
*
|
|
79
79
|
* Identity, not shape, is the discriminator: a host renderer and a game
|
|
80
80
|
* renderer are the same class, both built after install, both drawing real
|
|
@@ -84,10 +84,38 @@ export interface SceneCaptureOptions {
|
|
|
84
84
|
isHostRenderer?: (renderer: unknown) => boolean;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Brackets ONE `render()` call the captured renderer makes.
|
|
89
|
+
*
|
|
90
|
+
* The trap already stands between an ingested game and its own
|
|
91
|
+
* `WebGLRenderer.render`, which is the only place a host can see the game's
|
|
92
|
+
* render pass begin and end — the game owns its loop, so nothing else in the
|
|
93
|
+
* editor gets a `finally` around it. That is exactly the bracket
|
|
94
|
+
* `dev/render-debug-adapter.ts`'s `beforeRender`/`afterRender` need, so the
|
|
95
|
+
* first-party `RenderDebugAdapter` works for an ingested game with no per-game
|
|
96
|
+
* shimming and no second capture mechanism.
|
|
97
|
+
*
|
|
98
|
+
* Neither hook may throw: they run inside the game's own render call, so an
|
|
99
|
+
* exception here would break the game's frame. The trap calls them in a
|
|
100
|
+
* `try`/`finally` for `after`, but a throwing `before` is the hook author's bug.
|
|
101
|
+
*/
|
|
102
|
+
export interface RenderPassHooks {
|
|
103
|
+
before(): void;
|
|
104
|
+
after(): void;
|
|
105
|
+
}
|
|
106
|
+
|
|
87
107
|
/** Handle returned by {@link installSceneCapture}. */
|
|
88
108
|
export interface SceneCaptureHandle {
|
|
89
109
|
/** The captured runtime, or null until the game renders its first frame. */
|
|
90
110
|
readonly captured: CapturedRuntime | null;
|
|
111
|
+
/**
|
|
112
|
+
* Install (or clear, with `null`) hooks bracketing every `render()` the
|
|
113
|
+
* CAPTURED renderer makes. Set AFTER capture, because the consumer
|
|
114
|
+
* (`RenderDebugAdapter`) is built from the captured scene and context. Only
|
|
115
|
+
* the captured renderer's renders are bracketed — the editor's own viewport
|
|
116
|
+
* renders come through the same trap and are not the game's frame.
|
|
117
|
+
*/
|
|
118
|
+
setRenderPassHooks(hooks: RenderPassHooks | null): void;
|
|
91
119
|
/** Resolve once a scene+camera is captured (rejects on timeout). */
|
|
92
120
|
waitForCapture(timeoutMs?: number): Promise<CapturedRuntime>;
|
|
93
121
|
/** Total `render()` calls observed through the trap (a liveness signal). */
|
|
@@ -156,6 +184,8 @@ export function installSceneCapture(
|
|
|
156
184
|
|
|
157
185
|
let captured: CapturedRuntime | null = null;
|
|
158
186
|
let drawCount = 0;
|
|
187
|
+
/** Set post-capture by the host; see {@link SceneCaptureHandle.setRenderPassHooks}. */
|
|
188
|
+
let renderPassHooks: RenderPassHooks | null = null;
|
|
159
189
|
const waiters: Array<(rt: CapturedRuntime) => void> = [];
|
|
160
190
|
|
|
161
191
|
// Preserve any descriptor already on the prototype so uninstall can restore it.
|
|
@@ -190,6 +220,26 @@ export function installSceneCapture(
|
|
|
190
220
|
},
|
|
191
221
|
});
|
|
192
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Call the renderer's own real `render`, bracketed by the render-pass hooks
|
|
225
|
+
* when this IS the captured game's renderer (see `setRenderPassHooks`).
|
|
226
|
+
* `after` runs in a `finally`, so a throwing game frame still restores
|
|
227
|
+
* whatever the hook armed.
|
|
228
|
+
*/
|
|
229
|
+
function forwardRender(self: Record<symbol, unknown>, args: unknown[]): unknown {
|
|
230
|
+
const real = self[REAL] as (...a: unknown[]) => unknown;
|
|
231
|
+
const hooks = renderPassHooks;
|
|
232
|
+
if (hooks === null || !captured || (self as unknown) !== captured.renderer) {
|
|
233
|
+
return real.apply(self, args);
|
|
234
|
+
}
|
|
235
|
+
hooks.before();
|
|
236
|
+
try {
|
|
237
|
+
return real.apply(self, args);
|
|
238
|
+
} finally {
|
|
239
|
+
hooks.after();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
193
243
|
Object.defineProperty(proto, 'render', {
|
|
194
244
|
configurable: true,
|
|
195
245
|
set(this: Record<symbol, unknown>, fn: unknown) {
|
|
@@ -211,13 +261,11 @@ export function installSceneCapture(
|
|
|
211
261
|
if (!captured || (self as unknown) === captured.renderer) drawCount++;
|
|
212
262
|
// Never capture a scene the HOST owns. The trap sits on the shared
|
|
213
263
|
// `WebGLRenderer.prototype`, so the editor's own viewport renders
|
|
214
|
-
// arrive here too — and when a game bundles its own MISMATCHED three
|
|
215
|
-
//
|
|
216
|
-
// editor's scene is then the only thing that ever reaches the trap.
|
|
264
|
+
// arrive here too — and when a game bundles its own MISMATCHED three,
|
|
265
|
+
// the editor's scene is then the only thing that ever reaches the trap.
|
|
217
266
|
// It was duly "captured" as the game: the editor got a hierarchy of
|
|
218
267
|
// its own GridHelper, BatchedRenderer and viewport lights presented as
|
|
219
|
-
// the ingested game's content, and
|
|
220
|
-
// whole graceful-degradation contract is built on — never fired.
|
|
268
|
+
// the ingested game's content, and the mount reported success.
|
|
221
269
|
// Whether that happened at all came down to whether the editor
|
|
222
270
|
// rendered a frame inside the game's capture window, so the same
|
|
223
271
|
// session could pass or fail on timing alone.
|
|
@@ -230,8 +278,7 @@ export function installSceneCapture(
|
|
|
230
278
|
};
|
|
231
279
|
for (const resolve of waiters.splice(0)) resolve(captured);
|
|
232
280
|
}
|
|
233
|
-
|
|
234
|
-
return (self[REAL] as (...a: unknown[]) => unknown).apply(self, args);
|
|
281
|
+
return forwardRender(self, args);
|
|
235
282
|
};
|
|
236
283
|
},
|
|
237
284
|
});
|
|
@@ -276,6 +323,9 @@ export function installSceneCapture(
|
|
|
276
323
|
getDrawCount() {
|
|
277
324
|
return drawCount;
|
|
278
325
|
},
|
|
326
|
+
setRenderPassHooks(hooks: RenderPassHooks | null) {
|
|
327
|
+
renderPassHooks = hooks;
|
|
328
|
+
},
|
|
279
329
|
getAnimationLoop(renderer: THREE.WebGLRenderer) {
|
|
280
330
|
return loopCallbacks.get(renderer as unknown as object) ?? null;
|
|
281
331
|
},
|
|
@@ -29,7 +29,7 @@ export const CAMERA_ID = 'ingest:camera';
|
|
|
29
29
|
* The editor parks its OWN objects — grid, its two lights, the particle
|
|
30
30
|
* BatchedRenderer, every TransformControls/gizmo helper — on layer 31
|
|
31
31
|
* (`packages/editor/src/editor-layers.ts`'s `EDITOR_LAYER`) so the game camera
|
|
32
|
-
* never sees them.
|
|
32
|
+
* never sees them. On an ingest root those objects are added to the GAME'S OWN
|
|
33
33
|
* scene, which is the same tree this walk indexes.
|
|
34
34
|
*/
|
|
35
35
|
const EDITOR_ONLY_LAYER_MASK = 1 << 31;
|
|
@@ -74,7 +74,7 @@ export interface StructuralIdWalk {
|
|
|
74
74
|
* tree re-binds the same ids to the same objects.
|
|
75
75
|
*
|
|
76
76
|
* EDITOR FURNITURE IS EXCLUDED, and that exclusion is load-bearing rather than
|
|
77
|
-
* cosmetic.
|
|
77
|
+
* cosmetic. On an ingest root the editor adds its grid, lights, particle
|
|
78
78
|
* BatchedRenderer and TransformControls gizmo to the GAME'S OWN scene, and
|
|
79
79
|
* they arrive asynchronously — some before the walk, some after. Indexing them
|
|
80
80
|
* made a game object's path depend on how much editor furniture happened to be
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format-neutral live reflection-probe mark.
|
|
3
|
+
*
|
|
4
|
+
* A project-owned scene component writes this mark onto the Object3D it
|
|
5
|
+
* returns. The renderer capability owns capture and material integration; the
|
|
6
|
+
* editor only reads the mark to draw ordinary probe gizmos and expose the
|
|
7
|
+
* capture command/status. Authored truth remains the component's JSX props —
|
|
8
|
+
* this is a live adapter seam, not another document format.
|
|
9
|
+
*/
|
|
10
|
+
import type * as THREE from 'three';
|
|
11
|
+
import { deleteUserData, getUserData, setUserData } from '../ecs/user-data';
|
|
12
|
+
|
|
13
|
+
export type ReflectionProbeShape = 'box' | 'sphere';
|
|
14
|
+
export type ReflectionProbeCaptureMode = 'on-change' | 'manual' | 'realtime';
|
|
15
|
+
export type ReflectionProbeCaptureStatus = 'idle' | 'queued' | 'capturing' | 'ready' | 'error';
|
|
16
|
+
|
|
17
|
+
export interface ReflectionProbeConfig {
|
|
18
|
+
readonly shape: ReflectionProbeShape;
|
|
19
|
+
/** Full local-space box dimensions. */
|
|
20
|
+
readonly size: readonly [number, number, number];
|
|
21
|
+
/** Local-space sphere radius when `shape === 'sphere'`. */
|
|
22
|
+
readonly radius: number;
|
|
23
|
+
/** Local-space distance over which this probe fades at its boundary. */
|
|
24
|
+
readonly blendDistance: number;
|
|
25
|
+
/** Larger values win before equal-priority probes blend. */
|
|
26
|
+
readonly priority: number;
|
|
27
|
+
readonly intensity: number;
|
|
28
|
+
readonly parallaxProjection: boolean;
|
|
29
|
+
/** Full local-space projection-box dimensions. */
|
|
30
|
+
readonly parallaxSize: readonly [number, number, number];
|
|
31
|
+
readonly parallaxOffset: readonly [number, number, number];
|
|
32
|
+
readonly captureOffset: readonly [number, number, number];
|
|
33
|
+
readonly captureMode: ReflectionProbeCaptureMode;
|
|
34
|
+
readonly resolution: number;
|
|
35
|
+
readonly near: number;
|
|
36
|
+
readonly far: number;
|
|
37
|
+
readonly cullMask: number;
|
|
38
|
+
readonly captureShadows: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ReflectionProbeSnapshot {
|
|
42
|
+
readonly status: ReflectionProbeCaptureStatus;
|
|
43
|
+
/** Monotonic browser time from `performance.now()`, or null before capture. */
|
|
44
|
+
readonly lastCapturedAt: number | null;
|
|
45
|
+
readonly message?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ReflectionProbeMark {
|
|
49
|
+
readonly config: ReflectionProbeConfig;
|
|
50
|
+
readonly revision: number;
|
|
51
|
+
recapture(): void;
|
|
52
|
+
getSnapshot(): ReflectionProbeSnapshot;
|
|
53
|
+
subscribe(listener: () => void): () => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function reflectionProbeOf(
|
|
57
|
+
object: THREE.Object3D | null | undefined,
|
|
58
|
+
): ReflectionProbeMark | null {
|
|
59
|
+
const candidate = getUserData(object, 'reflectionProbe');
|
|
60
|
+
if (!candidate || typeof candidate !== 'object') return null;
|
|
61
|
+
const mark = candidate as Partial<ReflectionProbeMark>;
|
|
62
|
+
return mark.config &&
|
|
63
|
+
typeof mark.recapture === 'function' &&
|
|
64
|
+
typeof mark.getSnapshot === 'function'
|
|
65
|
+
? (candidate as ReflectionProbeMark)
|
|
66
|
+
: null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function setReflectionProbeMark(object: THREE.Object3D, mark: ReflectionProbeMark): void {
|
|
70
|
+
setUserData(object, 'reflectionProbe', mark);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function clearReflectionProbeMark(object: THREE.Object3D): void {
|
|
74
|
+
deleteUserData(object, 'reflectionProbe');
|
|
75
|
+
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The engine's
|
|
2
|
+
* The engine's runtime descriptor schemas.
|
|
3
3
|
*
|
|
4
4
|
* WO-8 deleted the `.vscn.json` scene format and everything under
|
|
5
5
|
* `engine/src/scene/`. What survived is here, and each module is here because a
|
|
6
6
|
* real runtime reader still consumes it:
|
|
7
7
|
*
|
|
8
|
-
* - `material.ts` — the
|
|
9
|
-
* plus the inline material descriptor `render/material-factory.ts` reads
|
|
10
|
-
* - `instances.ts` — the `.instances.json` transform format (`parseInstancesFile`),
|
|
11
|
-
* read by `render/instance-mesh.ts`
|
|
8
|
+
* - `material.ts` — the inline material descriptor `render/material-factory.ts` reads
|
|
12
9
|
* - `mesh.ts` — the geometry descriptor `render/material-factory.ts`'s `createGeometry` reads
|
|
13
10
|
* - `light.ts` / `camera.ts` — the descriptors `render/light-camera-factory.ts` reads
|
|
14
11
|
* - `collider.ts` — the descriptor `physics/collider-dimensions.ts` and the editor's
|
|
@@ -29,15 +26,12 @@ export type { CameraDescriptor } from './camera';
|
|
|
29
26
|
export { CameraDescriptorSchema } from './camera';
|
|
30
27
|
export type { ColliderDescriptor } from './collider';
|
|
31
28
|
export { ColliderDescriptorSchema } from './collider';
|
|
32
|
-
export type { InstancesFile } from './instances';
|
|
33
|
-
export { InstancesFileSchema } from './instances';
|
|
34
29
|
export type { LightDescriptor } from './light';
|
|
35
30
|
export { LightDescriptorSchema } from './light';
|
|
36
|
-
export type { MaterialDescriptor
|
|
37
|
-
export { MaterialDescriptorSchema
|
|
31
|
+
export type { MaterialDescriptor } from './material';
|
|
32
|
+
export { MaterialDescriptorSchema } from './material';
|
|
38
33
|
export type { MeshDescriptor } from './mesh';
|
|
39
34
|
export { MeshDescriptorSchema } from './mesh';
|
|
40
|
-
export { parseInstancesFile, parseMaterialFile } from './parse';
|
|
41
35
|
export type { ParticlesDescriptor } from './particles';
|
|
42
36
|
export { ParticlesDescriptorSchema } from './particles';
|
|
43
37
|
export type {
|
|
@@ -91,14 +91,3 @@ export const MaterialDescriptorSchema = MaterialDescriptorObjectSchema.describe(
|
|
|
91
91
|
);
|
|
92
92
|
|
|
93
93
|
export type MaterialDescriptor = z.infer<typeof MaterialDescriptorSchema>;
|
|
94
|
-
|
|
95
|
-
/** .mat.json file format for shared/reusable material assets. */
|
|
96
|
-
export const MaterialFileSchema = z
|
|
97
|
-
.object({
|
|
98
|
-
version: z.number().describe('File format version'),
|
|
99
|
-
name: z.string().describe('Human-readable material name'),
|
|
100
|
-
material: MaterialDescriptorSchema.describe('Material definition'),
|
|
101
|
-
})
|
|
102
|
-
.describe('Shared material asset file (.mat.json)');
|
|
103
|
-
|
|
104
|
-
export type MaterialFile = z.infer<typeof MaterialFileSchema>;
|
|
@@ -30,15 +30,6 @@ const MeshDescriptorBase = z
|
|
|
30
30
|
'lifted out of its armature — if the named node is a SkinnedMesh (or contains one), ' +
|
|
31
31
|
'resolving it throws (degrade loudly, not silently).',
|
|
32
32
|
),
|
|
33
|
-
instances: z
|
|
34
|
-
.string()
|
|
35
|
-
.optional()
|
|
36
|
-
.describe(
|
|
37
|
-
'Path to an instance-transform asset (.instances.json: an array of ' +
|
|
38
|
-
'[posX,posY,posZ, quatX,quatY,quatZ,quatW, scaleX,scaleY,scaleZ] tuples). Renders one ' +
|
|
39
|
-
'THREE.InstancedMesh (one draw call). Bulk transform data is referenced as an asset, ' +
|
|
40
|
-
'never inlined — keeps the document thin.',
|
|
41
|
-
),
|
|
42
33
|
lod: z
|
|
43
34
|
.array(
|
|
44
35
|
z.object({
|
|
@@ -61,7 +52,7 @@ const MeshDescriptorBase = z
|
|
|
61
52
|
.describe(
|
|
62
53
|
'Per-entity Level-of-Detail: distance-keyed glTF sub-nodes swapped by camera distance ' +
|
|
63
54
|
'(THREE.LOD). gltf-only. Gated by the environment.rendering.lod master toggle — when ' +
|
|
64
|
-
'off, only the closest level renders. Cannot combine with node
|
|
55
|
+
'off, only the closest level renders. Cannot combine with node.',
|
|
65
56
|
),
|
|
66
57
|
})
|
|
67
58
|
.strict();
|
|
@@ -79,12 +70,8 @@ export const MeshDescriptorSchema = MeshDescriptorBase.refine(
|
|
|
79
70
|
.refine((m) => m.lod == null || m.type === 'gltf', {
|
|
80
71
|
message: "Mesh 'lod' is only meaningful with type: 'gltf'",
|
|
81
72
|
})
|
|
82
|
-
.refine((m) => m.
|
|
83
|
-
message: "Mesh '
|
|
84
|
-
})
|
|
85
|
-
.refine((m) => !(m.lod && (m.node || m.instances)), {
|
|
86
|
-
message:
|
|
87
|
-
"Mesh 'lod' cannot combine with 'node'/'instances' — LOD is the multi-level form of a node ref",
|
|
73
|
+
.refine((m) => !(m.lod && m.node), {
|
|
74
|
+
message: "Mesh 'lod' cannot combine with 'node' — LOD is the multi-level form of a node ref",
|
|
88
75
|
})
|
|
89
76
|
.describe('Mesh geometry definition');
|
|
90
77
|
|
package/src/asset-parse-error.ts
CHANGED
|
@@ -4,14 +4,13 @@
|
|
|
4
4
|
* Every authored asset format the engine validates at fetch time throws this
|
|
5
5
|
* when Zod rejects the payload, naming the offending file (T4.6). It lives
|
|
6
6
|
* here — beside `loader.ts`/`assets.ts` — rather than inside a format module so
|
|
7
|
-
* every caller (
|
|
8
|
-
*
|
|
9
|
-
* it.
|
|
7
|
+
* every caller (currently the input-map loader in `input/input-manager.ts`)
|
|
8
|
+
* does not have to reach into an unrelated subsystem for it.
|
|
10
9
|
*
|
|
11
10
|
* It was called `SceneParseError` until WO-9 task #6. The extraction that gave
|
|
12
11
|
* this file its name deliberately deferred the class rename ("extraction, not
|
|
13
|
-
* redesign"); with `.vscn` gone the old name described nothing —
|
|
14
|
-
*
|
|
12
|
+
* redesign"); with `.vscn` gone the old name described nothing — an
|
|
13
|
+
* `.inputmap.json` failure is not a scene failure — so the class and its
|
|
15
14
|
* message header now say `asset`.
|
|
16
15
|
*/
|
|
17
16
|
|
package/src/config.ts
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
* evaluated. The package build emits this entry as ordinary JavaScript, so
|
|
4
4
|
* project config remains package-native without asking Node to execute raw
|
|
5
5
|
* TypeScript or reach into an adjacent checkout.
|
|
6
|
+
*
|
|
7
|
+
* A project's `*.schema.ts` is reached from `vite.config.ts` itself (through
|
|
8
|
+
* `./src/data/assets`, so the dangling-ref gate can run the real Zod), which
|
|
9
|
+
* is why every schema-time field factory belongs here: `dataRef` and `curve`
|
|
10
|
+
* are both authored in a schema, and a schema module must stay loadable by
|
|
11
|
+
* plain Node.
|
|
6
12
|
*/
|
|
13
|
+
export {
|
|
14
|
+
type CurveAxes,
|
|
15
|
+
type CurvePoint,
|
|
16
|
+
type CurveValue,
|
|
17
|
+
curve,
|
|
18
|
+
sampleCurve,
|
|
19
|
+
} from './data/curve';
|
|
7
20
|
export { type DataRef, dataRef, getRef } from './data/data-ref';
|
|
8
21
|
export { type VgaiDataCheckOptions, vgaiDataCheck } from './data/vite-plugin-data';
|