@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.
Files changed (152) hide show
  1. package/README.md +6 -5
  2. package/dist/adapter/authoring.d.ts +51 -28
  3. package/dist/adapter/authoring.d.ts.map +1 -1
  4. package/dist/adapter/body-marks.d.ts +59 -0
  5. package/dist/adapter/body-marks.d.ts.map +1 -0
  6. package/dist/adapter/body-marks.js +62 -0
  7. package/dist/adapter/constraint.d.ts +41 -0
  8. package/dist/adapter/constraint.d.ts.map +1 -0
  9. package/dist/adapter/constraint.js +27 -0
  10. package/dist/adapter/hierarchy-marks.d.ts +138 -0
  11. package/dist/adapter/hierarchy-marks.d.ts.map +1 -0
  12. package/dist/adapter/hierarchy-marks.js +148 -0
  13. package/dist/adapter/index.d.ts +1 -1
  14. package/dist/adapter/index.d.ts.map +1 -1
  15. package/dist/adapter/ingest/contract-system-adapters.d.ts +64 -0
  16. package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -0
  17. package/dist/adapter/ingest/contract-system-adapters.js +134 -0
  18. package/dist/adapter/ingest/game-contract.d.ts +87 -9
  19. package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
  20. package/dist/adapter/ingest/game-contract.js +7 -6
  21. package/dist/adapter/ingest/scene-capture.d.ts +31 -4
  22. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  23. package/dist/adapter/ingest/scene-capture.js +29 -7
  24. package/dist/adapter/ingest/structural-ids.d.ts +1 -1
  25. package/dist/adapter/ingest/structural-ids.js +2 -2
  26. package/dist/adapter/reflection-probe.d.ts +53 -0
  27. package/dist/adapter/reflection-probe.d.ts.map +1 -0
  28. package/dist/adapter/reflection-probe.js +18 -0
  29. package/dist/asset-formats/index.d.ts +4 -10
  30. package/dist/asset-formats/index.d.ts.map +1 -1
  31. package/dist/asset-formats/index.js +3 -8
  32. package/dist/asset-formats/material.d.ts +0 -66
  33. package/dist/asset-formats/material.d.ts.map +1 -1
  34. package/dist/asset-formats/material.js +0 -8
  35. package/dist/asset-formats/mesh.d.ts +1 -2
  36. package/dist/asset-formats/mesh.d.ts.map +1 -1
  37. package/dist/asset-formats/mesh.js +3 -13
  38. package/dist/asset-formats/render-env.d.ts +2 -2
  39. package/dist/asset-parse-error.d.ts +4 -5
  40. package/dist/asset-parse-error.d.ts.map +1 -1
  41. package/dist/asset-parse-error.js +4 -5
  42. package/dist/config.d.ts +7 -0
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +7 -0
  45. package/dist/data/curve.d.ts +116 -0
  46. package/dist/data/curve.d.ts.map +1 -0
  47. package/dist/data/curve.js +230 -0
  48. package/dist/data/vite-plugin-data.d.ts +2 -2
  49. package/dist/data/vite-plugin-data.d.ts.map +1 -1
  50. package/dist/data/vite-plugin-data.js +24 -15
  51. package/dist/dev/performance-profiler.d.ts +1 -0
  52. package/dist/dev/performance-profiler.d.ts.map +1 -1
  53. package/dist/dev/performance-profiler.js +25 -2
  54. package/dist/ecs/user-data.d.ts +49 -1
  55. package/dist/ecs/user-data.d.ts.map +1 -1
  56. package/dist/ecs/user-data.js +34 -0
  57. package/dist/index.d.ts +2 -3
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +2 -3
  60. package/dist/manifest/index.d.ts +3 -3
  61. package/dist/manifest/index.d.ts.map +1 -1
  62. package/dist/manifest/index.js +2 -2
  63. package/dist/manifest/load.d.ts +15 -67
  64. package/dist/manifest/load.d.ts.map +1 -1
  65. package/dist/manifest/load.js +11 -103
  66. package/dist/manifest/schema.d.ts +18 -103
  67. package/dist/manifest/schema.d.ts.map +1 -1
  68. package/dist/manifest/schema.js +59 -179
  69. package/dist/pixi/index.d.ts +2 -3
  70. package/dist/pixi/index.d.ts.map +1 -1
  71. package/dist/pixi/index.js +2 -3
  72. package/dist/pixi/ingest.d.ts +7 -75
  73. package/dist/pixi/ingest.d.ts.map +1 -1
  74. package/dist/pixi/ingest.js +6 -14
  75. package/dist/pixi/scene-capture.d.ts +2 -2
  76. package/dist/pixi/scene-capture.js +2 -2
  77. package/dist/react/use-data.d.ts +10 -5
  78. package/dist/react/use-data.d.ts.map +1 -1
  79. package/dist/react/use-data.js +15 -15
  80. package/dist/render/viewport-shading.d.ts +1 -1
  81. package/dist/render/viewport-shading.d.ts.map +1 -1
  82. package/dist/render/viewport-shading.js +14 -0
  83. package/dist/runtime/create-runtime.d.ts +12 -0
  84. package/dist/runtime/create-runtime.d.ts.map +1 -1
  85. package/dist/runtime/create-runtime.js +7 -2
  86. package/dist/runtime/mount-game.d.ts +1 -1
  87. package/dist/runtime/mount-game.d.ts.map +1 -1
  88. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  89. package/dist/runtime/mount-manifest.js +5 -1
  90. package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
  91. package/dist/world3d-react/r3f-adapter.js +69 -39
  92. package/dist/world3d-react/renderer-config.d.ts +15 -0
  93. package/dist/world3d-react/renderer-config.d.ts.map +1 -1
  94. package/dist/world3d-react/renderer-config.js +15 -0
  95. package/dist/world3d-react/world-context.d.ts +2 -1
  96. package/dist/world3d-react/world-context.d.ts.map +1 -1
  97. package/dist-config/config.js +140 -20
  98. package/dist-config/config.js.map +4 -4
  99. package/package.json +1 -1
  100. package/schemas/engine-capabilities.json +10 -10
  101. package/schemas/vgai-project.schema.json +21 -76
  102. package/src/adapter/authoring.ts +54 -31
  103. package/src/adapter/body-marks.ts +69 -0
  104. package/src/adapter/constraint.ts +71 -0
  105. package/src/adapter/hierarchy-marks.ts +155 -0
  106. package/src/adapter/index.ts +2 -3
  107. package/src/adapter/ingest/contract-system-adapters.ts +174 -0
  108. package/src/adapter/ingest/game-contract.ts +98 -9
  109. package/src/adapter/ingest/scene-capture.ts +61 -11
  110. package/src/adapter/ingest/structural-ids.ts +2 -2
  111. package/src/adapter/reflection-probe.ts +75 -0
  112. package/src/asset-formats/index.ts +4 -10
  113. package/src/asset-formats/material.ts +0 -11
  114. package/src/asset-formats/mesh.ts +3 -16
  115. package/src/asset-parse-error.ts +4 -5
  116. package/src/config.ts +13 -0
  117. package/src/data/curve.ts +263 -0
  118. package/src/data/vite-plugin-data.ts +48 -14
  119. package/src/dev/performance-profiler.ts +21 -2
  120. package/src/ecs/user-data.ts +50 -0
  121. package/src/index.ts +2 -3
  122. package/src/manifest/index.ts +3 -15
  123. package/src/manifest/load.ts +27 -143
  124. package/src/manifest/schema.ts +65 -223
  125. package/src/pixi/index.ts +2 -15
  126. package/src/pixi/ingest.ts +13 -87
  127. package/src/pixi/scene-capture.ts +2 -2
  128. package/src/react/use-data.ts +15 -20
  129. package/src/render/viewport-shading.ts +16 -1
  130. package/src/runtime/create-runtime.ts +30 -5
  131. package/src/runtime/mount-game.ts +1 -1
  132. package/src/runtime/mount-manifest.ts +5 -1
  133. package/src/world3d-react/r3f-adapter.tsx +73 -39
  134. package/src/world3d-react/renderer-config.ts +31 -0
  135. package/src/world3d-react/world-context.ts +2 -1
  136. package/dist/asset-formats/instances.d.ts +0 -17
  137. package/dist/asset-formats/instances.d.ts.map +0 -1
  138. package/dist/asset-formats/instances.js +0 -31
  139. package/dist/asset-formats/parse.d.ts +0 -20
  140. package/dist/asset-formats/parse.d.ts.map +0 -1
  141. package/dist/asset-formats/parse.js +0 -34
  142. package/dist/pixi/ingest-iframe.d.ts +0 -82
  143. package/dist/pixi/ingest-iframe.d.ts.map +0 -1
  144. package/dist/pixi/ingest-iframe.js +0 -166
  145. package/dist/render/instance-mesh.d.ts +0 -5
  146. package/dist/render/instance-mesh.d.ts.map +0 -1
  147. package/dist/render/instance-mesh.js +0 -19
  148. package/schemas/mat.schema.json +0 -255
  149. package/src/asset-formats/instances.ts +0 -38
  150. package/src/asset-formats/parse.ts +0 -39
  151. package/src/pixi/ingest-iframe.ts +0 -255
  152. 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
- * Both members are read by exactly one consumer,
77
- * `adapter/ingest/contract-debug-adapter.ts`, which projects them onto the
78
- * host's ordinary `DebugAdapter` (`adapter/system-adapter.ts`) so
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 `window`
118
- * — correct for a same-realm mount, where the game's modules and the host share
119
- * one global. An iframe-hosted game declares on ITS realm's window, so the host
120
- * passes `iframe.contentWindow`: the contract is a property of the realm the
121
- * game runs in, never of the realm that happens to be asking.
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 `cov-mismatched-three` — the spec for "a game bundling
74
- * its own three is DETECTED and degrades to embed-only, never silently"
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 2.5s capture window. When it did, the editor adopted its own
77
- * preview scene as the game and `captureFailed` never fired.
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
- // (the case this whole degrade-to-embed-only path exists for), the
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 `captureFailed` the signal the
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
- // Forward to the renderer's real render with the original args.
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. In ingest mode those objects are added to the GAME'S OWN
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. In ingest mode the editor adds its grid, lights, particle
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 remaining authored asset formats and descriptor schemas.
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 `.mat.json` shared-material format (`parseMaterialFile`)
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, MaterialFile } from './material';
37
- export { MaterialDescriptorSchema, MaterialFileSchema } from './material';
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/instances.',
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.instances == null || m.type !== 'splat', {
83
- message: "Mesh 'instances' is not supported with type: 'splat'",
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
 
@@ -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 (the `.mat.json` parser, the input-map loader in
8
- * `input/input-manager.ts`) does not have to reach into the format half for
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 — a `.mat.json`
14
- * or `.inputmap.json` failure is not a scene failure — so the class and its
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';