@vgai/engine 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -11
- package/package.json +10 -6
- package/schemas/engine-api.json +1 -68
- package/schemas/engine-api.md +1 -32
- package/schemas/engine-capabilities.json +30 -42
- package/schemas/{vgai-game.schema.json → vgai-project.schema.json} +52 -34
- package/src/adapter/adapter-surface.ts +5 -5
- package/src/adapter/authoring.ts +168 -226
- package/src/adapter/colyseus-networking-adapter.ts +44 -5
- package/src/adapter/first-party-systems.ts +156 -42
- package/src/adapter/host-context.ts +177 -46
- package/src/adapter/index.ts +47 -51
- package/src/adapter/ingest/game-contract.ts +2 -2
- package/src/adapter/ingest/scene-capture.ts +18 -19
- package/src/adapter/ingest/structural-ids.ts +127 -0
- package/src/adapter/ingest/upstream-pin.ts +9 -12
- package/src/adapter/loop-gate-report.ts +11 -11
- package/src/adapter/rapier-physics-adapter.ts +27 -9
- package/src/adapter/root-adapter.ts +217 -0
- package/src/adapter/{vgai-scene-game-adapter.ts → setup-three-root-adapter.ts} +173 -351
- package/src/adapter/system-adapter.ts +80 -63
- package/src/ai/navigation.ts +1 -1
- package/src/animation/animation-clock.ts +1 -1
- package/src/animation/camera-ownership.ts +1 -2
- package/src/animation/cubic-spline-interpolant.ts +132 -0
- package/src/animation/theatre-clock-binding.ts +2 -2
- package/src/animation/theatre-object-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +75 -5
- package/src/{scene/schema → asset-formats}/camera.ts +2 -5
- package/src/{scene/schema → asset-formats}/collider.ts +2 -5
- package/src/asset-formats/index.ts +54 -0
- package/src/{scene/schema → asset-formats}/instances.ts +10 -6
- package/src/{scene/schema → asset-formats}/light.ts +3 -6
- package/src/{scene/schema → asset-formats}/material.ts +4 -7
- package/src/{scene/schema → asset-formats}/mesh.ts +4 -7
- package/src/asset-formats/parse.ts +39 -0
- package/src/{scene/schema → asset-formats}/particles.ts +3 -6
- package/src/{scene/schema/environment.ts → asset-formats/render-env.ts} +23 -86
- package/src/{scene/schema → asset-formats}/tuples.ts +1 -1
- package/src/{scene/asset-loaders.ts → asset-loaders.ts} +8 -9
- package/src/asset-parse-error.ts +33 -0
- package/src/{scene/asset-registry.ts → asset-registry.ts} +1 -1
- package/src/assets.ts +1 -1
- package/src/audio/wav-encode.ts +9 -9
- package/src/canvas-react/engine-bridge.ts +59 -0
- package/src/canvas-react/index.ts +50 -0
- package/src/canvas-react/pixi-primitive.tsx +202 -0
- package/src/canvas-react/pixi-react-adapter.tsx +290 -0
- package/src/canvas-react/pixi-react-root-factory.tsx +88 -0
- package/src/canvas-react/world-context.ts +328 -0
- package/src/core/frame-pacing.ts +100 -0
- package/src/core/game-loop.ts +50 -28
- package/src/core/seeded-random.ts +7 -7
- package/src/core/sim-clock.ts +388 -0
- package/src/core/system-runner.ts +17 -63
- package/src/core/types.ts +34 -15
- package/src/data/data-asset.ts +3 -3
- package/src/data/data-check-core.ts +6 -7
- package/src/data/data-ref.ts +11 -11
- package/src/data/vite-plugin-data.ts +10 -10
- package/src/{scene/defaults.ts → defaults.ts} +18 -40
- package/src/dev/render-debug-adapter.ts +1 -1
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/ecs/scene-index.ts +439 -0
- package/src/ecs/scene-query.ts +43 -0
- package/src/{scene → ecs}/user-data.ts +17 -36
- package/src/index.ts +7 -9
- package/src/input/input-manager.ts +29 -32
- package/src/input/input-types.ts +2 -2
- package/src/input/schema.ts +5 -5
- package/src/loader.ts +57 -0
- package/src/manifest/editor-port.ts +69 -0
- package/src/manifest/filename.ts +49 -0
- package/src/manifest/index.ts +8 -2
- package/src/manifest/load-file.ts +11 -0
- package/src/manifest/load.ts +65 -77
- package/src/manifest/locate.ts +55 -0
- package/src/manifest/schema.ts +400 -233
- package/src/{scene → physics}/collider-dimensions.ts +3 -3
- package/src/physics/physics-registry.ts +1 -1
- package/src/{world2d/authoring-2d.ts → pixi/authoring.ts} +24 -11
- package/src/pixi/index.ts +43 -0
- package/src/{world2d/ingest-iframe-2d.ts → pixi/ingest-iframe.ts} +9 -9
- package/src/{world2d/ingest2d.ts → pixi/ingest.ts} +28 -28
- package/src/{world2d/physics2d-registry.ts → pixi/physics-registry.ts} +1 -1
- package/src/{world2d/scene-capture-2d.ts → pixi/scene-capture.ts} +3 -3
- package/src/{world2d/system-adapters-2d.ts → pixi/system-adapters.ts} +2 -2
- package/src/react/unmanaged-root-detector.ts +26 -1
- package/src/react/use-data.ts +5 -5
- package/src/react/use-selection.tsx +15 -42
- package/src/react/{game-state.tsx → world-state.tsx} +44 -46
- package/src/render/auto-batcher.ts +1 -2
- package/src/{scene → render}/instance-mesh.ts +1 -1
- package/src/{scene → render}/light-camera-factory.ts +14 -13
- package/src/render/lod.ts +17 -0
- package/src/{scene → render}/material-factory.ts +8 -7
- package/src/{scene → render}/particles-factory.ts +62 -12
- package/src/render/render-batch-system.ts +14 -41
- package/src/render/render-features.ts +1 -1
- package/src/render/render-settings.ts +1 -2
- package/src/render/spark-renderer-lifecycle.ts +1 -1
- package/src/runtime/create-runtime.ts +352 -463
- package/src/runtime/debug-bridge.ts +148 -93
- package/src/runtime/debug-registry.ts +79 -54
- package/src/runtime/dev-layers.ts +40 -0
- package/src/runtime/frame-selector-cache.ts +4 -4
- package/src/runtime/game.ts +618 -369
- package/src/runtime/gameplay-rng-trap.ts +6 -7
- package/src/runtime/input-router.ts +11 -11
- package/src/runtime/mount-game.ts +54 -55
- package/src/runtime/mount-manifest.ts +154 -150
- package/src/runtime/presentation.ts +141 -0
- package/src/runtime/render-audio-control.ts +64 -53
- package/src/runtime/render-control.ts +45 -51
- package/src/runtime/render-seed.ts +3 -4
- package/src/runtime/state-bridge.ts +17 -18
- package/src/runtime/types.ts +94 -61
- package/src/setup/setup-renderer.ts +11 -6
- package/src/world3d-react/engine-bridge.ts +46 -33
- package/src/world3d-react/index.ts +31 -26
- package/src/world3d-react/r3f-adapter.tsx +211 -96
- package/src/world3d-react/r3f-root-factory.tsx +91 -0
- package/src/world3d-react/renderer-config.ts +137 -0
- package/src/world3d-react/world-context.ts +141 -111
- package/schemas/entity2d.schema.json +0 -468
- package/schemas/prefab.schema.json +0 -9992
- package/schemas/scn2d.schema.json +0 -494
- package/schemas/vscn.schema.json +0 -10851
- package/src/adapter/game-adapter.ts +0 -164
- package/src/adapter/ingest/overlay-applier.ts +0 -207
- package/src/adapter/ingest/overlay-apply.ts +0 -168
- package/src/adapter/ingest/overlay-file.ts +0 -126
- package/src/adapter/ingest/overlay-report.ts +0 -176
- package/src/animation/gsap-registration.ts +0 -184
- package/src/audio/audio-introspection.ts +0 -290
- package/src/audio/index.ts +0 -39
- package/src/audio/tone-clock-binding.ts +0 -98
- package/src/audio/tone-context.ts +0 -175
- package/src/audio/tone-offline-render.ts +0 -167
- package/src/ecs/component-manager.ts +0 -814
- package/src/ecs/game-component.ts +0 -260
- package/src/ecs/hmr-swap-report.ts +0 -65
- package/src/physics/trigger-dispatch.ts +0 -97
- package/src/react/root-adapter.tsx +0 -49
- package/src/scene/asset-paths.ts +0 -121
- package/src/scene/asset-ref-check.ts +0 -248
- package/src/scene/component-registry.ts +0 -51
- package/src/scene/parse.ts +0 -204
- package/src/scene/scene-apply.ts +0 -407
- package/src/scene/scene-diff-schema.ts +0 -115
- package/src/scene/scene-diff-types.ts +0 -29
- package/src/scene/scene-loader.ts +0 -1526
- package/src/scene/scene-query.ts +0 -63
- package/src/scene/scene-types.ts +0 -33
- package/src/scene/scene-version.ts +0 -40
- package/src/scene/schema/animation.ts +0 -46
- package/src/scene/schema/audio.ts +0 -25
- package/src/scene/schema/entity-ref.ts +0 -78
- package/src/scene/schema/entity.ts +0 -189
- package/src/scene/schema/index.ts +0 -51
- package/src/scene/schema/joint.ts +0 -26
- package/src/scene/schema/physics.ts +0 -49
- package/src/scene/schema/scene-file.ts +0 -292
- package/src/scene/schema/shadow.ts +0 -24
- package/src/scene/schema/spline.ts +0 -21
- package/src/world2d/asset-paths2d.ts +0 -44
- package/src/world2d/capture-to-scene2d.ts +0 -52
- package/src/world2d/collision-2d.ts +0 -99
- package/src/world2d/entity2d-asset.ts +0 -22
- package/src/world2d/index.ts +0 -91
- package/src/world2d/physics2d-transform.ts +0 -173
- package/src/world2d/physics2d-units.ts +0 -10
- package/src/world2d/pixi-game-adapter.ts +0 -439
- package/src/world2d/pixi-surface.ts +0 -78
- package/src/world2d/scene2d-identity.ts +0 -49
- package/src/world2d/scene2d-loader.ts +0 -433
- package/src/world2d/schema/entity2d.ts +0 -163
- package/src/world2d/schema/physics2d.ts +0 -64
- package/src/world2d/schema/sprite.ts +0 -99
- package/src/world2d/schema/tilemap.ts +0 -39
- package/src/world2d/schema/tuples2d.ts +0 -25
- package/src/world2d/transform-writer-2d.ts +0 -42
- package/src/world2d/types.ts +0 -74
- package/src/world3d-react/behavior.tsx +0 -146
- /package/src/{scene → render}/mesh-shadow.ts +0 -0
package/src/runtime/types.ts
CHANGED
|
@@ -5,21 +5,20 @@ import type { z } from 'zod';
|
|
|
5
5
|
import type { SystemAdapters } from '../adapter/system-adapter';
|
|
6
6
|
import type { AssetCache } from '../assets';
|
|
7
7
|
import type { SeededRandom } from '../core/seeded-random';
|
|
8
|
+
import type { SimClock } from '../core/sim-clock';
|
|
8
9
|
import type { createSystemRunner } from '../core/system-runner';
|
|
9
10
|
import type { createDebugDraw } from '../dev/debug-draw';
|
|
10
|
-
import type {
|
|
11
|
+
import type { SceneIndex } from '../ecs/scene-index';
|
|
11
12
|
import type { InputManager } from '../input/input-manager';
|
|
12
13
|
import type { CollisionSystem } from '../physics/collision-system';
|
|
13
14
|
import type { PhysicsRegistry } from '../physics/physics-registry';
|
|
14
|
-
import type { SceneFile } from '../scene/scene-types';
|
|
15
15
|
import type { AudioContext as GameAudio } from '../setup/setup-audio';
|
|
16
16
|
import type { ParticlesContext } from '../setup/setup-particles';
|
|
17
|
-
import type { Game,
|
|
17
|
+
import type { Game, RootInstance } from './game';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* The minimal structural shape `ctx.debug.attachRoom` needs from a joined
|
|
21
|
-
* Colyseus room
|
|
22
|
-
* locus" note): just enough to send the reserved `__vgai:debugCommand`
|
|
21
|
+
* Colyseus room: just enough to send the reserved `__vgai:debugCommand`
|
|
23
22
|
* message and listen for its `__vgai:debugCommandResult` reply. Structural,
|
|
24
23
|
* not a Colyseus type import — the same seam-boundary style every other
|
|
25
24
|
* `DebugCtxSurface` member uses (no wrapper around Colyseus itself, D2).
|
|
@@ -36,11 +35,10 @@ export interface DebugRoomHandle {
|
|
|
36
35
|
/**
|
|
37
36
|
* Infers a `registerCommand`/`registerReactCommand`/`useDebugCommand`
|
|
38
37
|
* handler's parameter tuple from its declared Zod `args` tuple (dry-run
|
|
39
|
-
* finding, `
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `undefined` default) resolves to the original permissive
|
|
38
|
+
* finding, ledger — "`registerCommand` fn typing forces `unknown[]` casts"):
|
|
39
|
+
* a real `z.ZodTuple` infers its element types (`z.tuple([z.number(),
|
|
40
|
+
* z.string()])` → `(n: number, s: string) => ...`), while omitting `args`
|
|
41
|
+
* (the generic's `undefined` default) resolves to the original permissive
|
|
44
42
|
* `(...args: unknown[]) => ...` shape every command had before this generic
|
|
45
43
|
* existed. That permissive fallback is deliberate, not just a placeholder —
|
|
46
44
|
* it keeps every existing no-schema registration (including ones whose
|
|
@@ -53,13 +51,11 @@ export type DebugCommandArgs<T extends z.ZodTuple | undefined = undefined> = T e
|
|
|
53
51
|
: unknown[];
|
|
54
52
|
|
|
55
53
|
/**
|
|
56
|
-
* The authoring half of the debug/synthetic-player seam
|
|
57
|
-
* (`
|
|
58
|
-
* `
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* feeds the ONE game-scoped registry (`runtime/debug-registry.ts`) — never a
|
|
62
|
-
* per-world accumulator.
|
|
54
|
+
* The authoring half of the debug/synthetic-player seam — the read/actuate half is
|
|
55
|
+
* `SystemAdapters.DebugAdapter` (`adapter/system-adapter.ts`). Callable from any
|
|
56
|
+
* `init` (one file per new provider/command), or from a react world's
|
|
57
|
+
* `useDebugProvider`/`useDebugCommand`/`useDebugEmit` hooks. Every registration feeds the ONE
|
|
58
|
+
* game-scoped registry (`runtime/debug-registry.ts`) — never a per-world accumulator.
|
|
63
59
|
*/
|
|
64
60
|
export interface DebugCtxSurface {
|
|
65
61
|
/** Register (or replace) a named, JSON-serializable state read. Duplicate
|
|
@@ -99,7 +95,7 @@ export interface DebugCtxSurface {
|
|
|
99
95
|
|
|
100
96
|
/**
|
|
101
97
|
* All engine subsystems, passed to every game `setup(ctx)` and to each
|
|
102
|
-
*
|
|
98
|
+
* game lifecycle callback.
|
|
103
99
|
*
|
|
104
100
|
* The surface is intentionally split in two:
|
|
105
101
|
*
|
|
@@ -109,6 +105,17 @@ export interface DebugCtxSurface {
|
|
|
109
105
|
* into the phase-ordered game loop. Reach for these when you need behavior the
|
|
110
106
|
* raw library doesn't provide (physics↔Object3D mapping, system phases, etc.).
|
|
111
107
|
*/
|
|
108
|
+
/** Host-supplied identity for one private play run or coordinated team test.
|
|
109
|
+
* Games may pass `roomKey` as a Colyseus filter/join option; the engine never
|
|
110
|
+
* joins a room or couples this authoring identity to a networking library. */
|
|
111
|
+
export interface PlaytestContext {
|
|
112
|
+
readonly mode: 'private' | 'team';
|
|
113
|
+
readonly id: string;
|
|
114
|
+
readonly roomKey: string;
|
|
115
|
+
readonly revision: number | null;
|
|
116
|
+
readonly participantId: string | null;
|
|
117
|
+
}
|
|
118
|
+
|
|
112
119
|
export interface GameContext {
|
|
113
120
|
// ─── Raw libraries (the real library objects — call them directly) ───
|
|
114
121
|
|
|
@@ -116,7 +123,7 @@ export interface GameContext {
|
|
|
116
123
|
* groups) here. This is THE scene — there is no separate engine scene. */
|
|
117
124
|
scene: THREE.Scene;
|
|
118
125
|
/** The single render camera. Move/aim it directly, or (preferred) drive it
|
|
119
|
-
* from
|
|
126
|
+
* from game code — see the camera-precedence note below. */
|
|
120
127
|
camera: THREE.PerspectiveCamera;
|
|
121
128
|
/** The Rapier physics `World`. Step/query it, create bodies & colliders via
|
|
122
129
|
* `ctx.rapier`. One shared world for the whole scene. */
|
|
@@ -136,7 +143,7 @@ export interface GameContext {
|
|
|
136
143
|
input: InputManager;
|
|
137
144
|
/** Rapier collision-event dispatcher. Register handlers with
|
|
138
145
|
* `collisions.onCollision(...)`; it is drained each `postPhysics`. Prefer a
|
|
139
|
-
*
|
|
146
|
+
* own handler for per-entity sensor logic. */
|
|
140
147
|
collisions: CollisionSystem;
|
|
141
148
|
/** `Object3D ↔ Rapier body/collider` registry (+ collider→Object3D reverse
|
|
142
149
|
* index). Use to find an entity's body/collider, or the entity behind a
|
|
@@ -161,11 +168,36 @@ export interface GameContext {
|
|
|
161
168
|
assets: AssetCache;
|
|
162
169
|
/** The phase-ordered system runner. `systems.add(phase, fn)` registers a
|
|
163
170
|
* per-frame callback. Reserve this for non-gameplay infra (audio mixers,
|
|
164
|
-
* network send); per-entity GAMEPLAY belongs in a
|
|
171
|
+
* network send); per-entity GAMEPLAY belongs in a `useFrame` hook. */
|
|
165
172
|
systems: ReturnType<typeof createSystemRunner>;
|
|
166
|
-
/**
|
|
167
|
-
*
|
|
168
|
-
|
|
173
|
+
/**
|
|
174
|
+
* P3 — the SIM-TIME scheduler (`core/sim-clock.ts`). `await ctx.clock.delay(n)`
|
|
175
|
+
* waits `n` seconds of the fixed loop's own time, so it pauses when the game
|
|
176
|
+
* pauses, steps when `Game.play.step()` steps, and reproduces exactly in an
|
|
177
|
+
* offline export; `ctx.clock.after(n, fn)` is the frame-exact synchronous
|
|
178
|
+
* form, and `ctx.clock.disposeAfter(obj, n)` is debris removal. Cancellation
|
|
179
|
+
* is `AbortSignal`.
|
|
180
|
+
*
|
|
181
|
+
* REQUIRED, unlike `debug`/`random` above: every world has a sim clock. When
|
|
182
|
+
* a Game hosts the mount it is the ONE game-scoped clock the fixed loop
|
|
183
|
+
* flushes; a bare harness mount with no Game shell gets a private,
|
|
184
|
+
* mount-local clock that nothing advances (so `now()` stays 0) rather than an
|
|
185
|
+
* absent field.
|
|
186
|
+
*
|
|
187
|
+
* NOT `AnimationClock` (`animation/animation-clock.ts`) — that one is the
|
|
188
|
+
* seekable cinematic playhead and runs backwards. This one is monotonic and
|
|
189
|
+
* never rewinds, which is exactly why a Promise can be bound to it. See
|
|
190
|
+
* `core/sim-clock.ts`'s header.
|
|
191
|
+
*/
|
|
192
|
+
clock: SimClock;
|
|
193
|
+
/** The live scene index for this root (P2 `observe`): tag/entity-id lookup
|
|
194
|
+
* that never traverses, plus the change signals — `entityadded`,
|
|
195
|
+
* `entityremoved`, `tagadded`, `tagremoved`, `attributechanged` — on a plain
|
|
196
|
+
* `EventTarget`, unsubscribed with an `AbortSignal`. Maintained from three's
|
|
197
|
+
* own `childadded`/`childremoved` parent events, so it costs nothing on a
|
|
198
|
+
* frame where nothing changes. See `ecs/scene-index.ts`; the one-shot
|
|
199
|
+
* `ecs/scene-query.ts` walks remain for a single ad-hoc question. */
|
|
200
|
+
sceneIndex: SceneIndex;
|
|
169
201
|
/** Register a game-owned `SystemAdapters` capability (networking,
|
|
170
202
|
* navigation, …) on the mounted game's adapter surface, so the editor's
|
|
171
203
|
* introspection panels (via `getActiveSystems()`) can see it. The engine
|
|
@@ -186,33 +218,32 @@ export interface GameContext {
|
|
|
186
218
|
* mounted surface. */
|
|
187
219
|
registerSystemAdapter?<K extends keyof SystemAdapters>(kind: K, adapter: SystemAdapters[K]): void;
|
|
188
220
|
|
|
189
|
-
/** The debug/synthetic-player authoring surface
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
221
|
+
/** The debug/synthetic-player authoring surface — see {@link DebugCtxSurface}.
|
|
222
|
+
* Optional, same absence precedent as `registerSystemAdapter`: present on every
|
|
223
|
+
* first-party mount, absent only in bare test harnesses that build a
|
|
224
|
+
* hand-rolled `ctx`. */
|
|
193
225
|
debug?: DebugCtxSurface;
|
|
194
226
|
|
|
195
227
|
/**
|
|
196
|
-
* D15 (T-D15.1
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* only) is `play.seed.set`'s eventual target (T-D15.6, not yet wired).
|
|
228
|
+
* D15 (T-D15.1) — the game-scoped, named-stream seeded PRNG. `ctx.random()`
|
|
229
|
+
* draws from the `'gameplay'` stream; `ctx.random.stream('vfx')` (or any other
|
|
230
|
+
* name) draws from an INDEPENDENT stream, so a cosmetic/VFX draw can never
|
|
231
|
+
* shift the gameplay draw sequence. Use this instead of raw `Math.random()` for
|
|
232
|
+
* anything a project wants to keep reproducible; `ctx.random` itself always
|
|
233
|
+
* exists and is always internally consistent (same seed ⇒ same sequence)
|
|
234
|
+
* whether or not the project's manifest declares `determinism.seededRandom` —
|
|
235
|
+
* that flag only gates the BOOT-TIME seeding from
|
|
236
|
+
* `manifest.determinism.defaultSeed`/`?vgai-seed=`, the burn-down lint scan,
|
|
237
|
+
* and the dev-mode `Math.random` phase trap (three separate enforcers,
|
|
238
|
+
* `test/gameplay-rng-ban.test.ts` + `runtime/gameplay-rng-trap.ts`), never
|
|
239
|
+
* `ctx.random`'s mere presence. Same absence precedent as `debug` above:
|
|
240
|
+
* present on every first-party mount, optional only for bare test harnesses
|
|
241
|
+
* that build a hand-rolled `ctx`. `reseed(seed)` (future draws only) is
|
|
242
|
+
* `play.seed.set`'s eventual target (T-D15.6, not yet wired).
|
|
212
243
|
*/
|
|
213
244
|
random?: SeededRandom;
|
|
214
245
|
|
|
215
|
-
// ─── Game root (T7.1 slice 1 —
|
|
246
|
+
// ─── Game root (T7.1 slice 1 — D6) ───
|
|
216
247
|
|
|
217
248
|
/** The Game root — the loop, the world registry, and game-scoped handles.
|
|
218
249
|
* Absent only in bare test harnesses that construct a partial context (the
|
|
@@ -222,7 +253,10 @@ export interface GameContext {
|
|
|
222
253
|
game?: Game | undefined;
|
|
223
254
|
/** Alias for `game.roots` — the live, declaration-ordered world registry.
|
|
224
255
|
* Same optionality/absence rule as `game` above. */
|
|
225
|
-
roots?: ReadonlyArray<
|
|
256
|
+
roots?: ReadonlyArray<RootInstance> | undefined;
|
|
257
|
+
/** The host's current private play or Team Test identity. Optional for
|
|
258
|
+
* standalone/custom hosts; networking remains game-owned and direct. */
|
|
259
|
+
playtest?: PlaytestContext | null | undefined;
|
|
226
260
|
}
|
|
227
261
|
|
|
228
262
|
/** Return value from a game setup function. */
|
|
@@ -232,10 +266,6 @@ export interface GameCleanup {
|
|
|
232
266
|
|
|
233
267
|
/** Context passed to setup() when launched from the editor's play mode. */
|
|
234
268
|
export interface EditorPreview {
|
|
235
|
-
/** Path of the .vscn.json file the user has open (null if unsaved). */
|
|
236
|
-
scenePath: string | null;
|
|
237
|
-
/** The editor's in-memory scene document, including unsaved edits. */
|
|
238
|
-
sceneData: SceneFile;
|
|
239
269
|
/** Editor viewport camera transform — use this as the initial game camera viewpoint. */
|
|
240
270
|
viewportCamera?: {
|
|
241
271
|
position: [number, number, number];
|
|
@@ -243,12 +273,16 @@ export interface EditorPreview {
|
|
|
243
273
|
};
|
|
244
274
|
/**
|
|
245
275
|
* Which bundled example the static/browser build's project picker wants to
|
|
246
|
-
* run (
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
276
|
+
* run (Adjudication H).
|
|
277
|
+
*
|
|
278
|
+
* Its one former reader — `browser-play-entry.ts`'s whole-bundle `setup()`,
|
|
279
|
+
* which dispatched over the generated `SETUP_BY_EXAMPLE_ID` table — is
|
|
280
|
+
* DELETED. Hosted dispatch is per-root (`ADAPTER_BY_EXAMPLE_ROOT` /
|
|
281
|
+
* `REACT_BY_EXAMPLE_ROOT`, resolved in `adapter-resolver.ts`) and never
|
|
282
|
+
* consults this field; `exampleDispatchFor` still sets it as the marker for
|
|
283
|
+
* "play the project's own shipped game" rather than a `?scene=` override
|
|
284
|
+
* (which sets `assetPrefix` instead — that one IS read, by
|
|
285
|
+
* `SetupThreeRootAdapter`). Every `GameSetupFn` consumer ignores it.
|
|
252
286
|
*/
|
|
253
287
|
exampleId?: string | undefined;
|
|
254
288
|
/**
|
|
@@ -265,9 +299,8 @@ export interface EditorPreview {
|
|
|
265
299
|
/** A game setup function: receives engine context, returns cleanup.
|
|
266
300
|
*
|
|
267
301
|
* This is the MOUNT body of the FIRST-PARTY implementer. The host no longer
|
|
268
|
-
* depends on it — the host depends on `
|
|
269
|
-
* `GameSetupFn` is wrapped into a `
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* `@engine/adapter`.) */
|
|
302
|
+
* depends on it — the host depends on `RootAdapter` (`@engine/adapter`), and
|
|
303
|
+
* `GameSetupFn` is wrapped into a `SetupThreeRootAdapter` via `fromSetup`.. (The
|
|
304
|
+
* former `RootAdapter` `{ setup, components }` interface lived here; it is
|
|
305
|
+
* superseded by the real adapter interfaces in `@engine/adapter`.) */
|
|
273
306
|
export type GameSetupFn = (ctx: GameContext, editor?: EditorPreview) => Promise<GameCleanup>;
|
|
@@ -35,9 +35,10 @@ import {
|
|
|
35
35
|
SSREffect,
|
|
36
36
|
VelocityDepthNormalPass,
|
|
37
37
|
} from '../../vendor/realism-effects/dist/index.js';
|
|
38
|
+
import type { RenderEnvironment } from '../asset-formats/render-env';
|
|
39
|
+
import { DEFAULTS } from '../defaults';
|
|
40
|
+
import { detectKtx2Support } from '../loader';
|
|
38
41
|
import { type RenderScope, resolveRenderSettings } from '../render/render-settings';
|
|
39
|
-
import { DEFAULTS } from '../scene/defaults';
|
|
40
|
-
import type { SceneEnvironment } from '../scene/scene-types';
|
|
41
42
|
|
|
42
43
|
export interface RendererContext {
|
|
43
44
|
renderer: THREE.WebGLRenderer;
|
|
@@ -69,14 +70,14 @@ export function applyRendererSettings(
|
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
72
|
* Create ONLY the WebGL renderer. The HOST owns the renderer (see
|
|
72
|
-
* `
|
|
73
|
+
* `ThreeHostContext.renderer`); the game/adapter owns the scene/camera/composer it
|
|
73
74
|
* renders into. Split out of `setupRenderer` for the adapter inversion.
|
|
74
75
|
*
|
|
75
76
|
* `rendering` is the scene-level render scope (`env.rendering`); it seeds the
|
|
76
77
|
* reload-only `antialias` option at construction and the live shadow/resolution
|
|
77
78
|
* settings (via {@link applyRendererSettings}). Absent → registry defaults.
|
|
78
79
|
*
|
|
79
|
-
* `opts` is additive (T6.1 slice 1,
|
|
80
|
+
* `opts` is additive (T6.1 slice 1, D5 §1/§4): omitted
|
|
80
81
|
* entirely, construction is byte-identical to before (no `alpha`/
|
|
81
82
|
* `preserveDrawingBuffer` keys at all) — the legacy single-canvas host never
|
|
82
83
|
* passes it. The roots path passes `alpha:true` for every stacked canvas
|
|
@@ -109,6 +110,10 @@ export function createHostRenderer(
|
|
|
109
110
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
110
111
|
renderer.toneMapping = toneMappingModes[DEFAULTS.toneMapping.mode] ?? THREE.ACESFilmicToneMapping;
|
|
111
112
|
renderer.toneMappingExposure = DEFAULTS.toneMapping.exposure;
|
|
113
|
+
// The shared KTX2Loader cannot transcode a KHR_texture_basisu texture until
|
|
114
|
+
// some renderer has told it which compressed formats this GPU accepts.
|
|
115
|
+
// Idempotent — see `detectKtx2Support`.
|
|
116
|
+
detectKtx2Support(renderer);
|
|
112
117
|
return renderer;
|
|
113
118
|
}
|
|
114
119
|
|
|
@@ -193,7 +198,7 @@ export function applyScenePostProcessing(
|
|
|
193
198
|
renderer: THREE.WebGLRenderer,
|
|
194
199
|
scene: THREE.Scene,
|
|
195
200
|
camera: THREE.Camera,
|
|
196
|
-
env?:
|
|
201
|
+
env?: RenderEnvironment,
|
|
197
202
|
objectMap?: Map<string, THREE.Object3D>,
|
|
198
203
|
entityTags?: Map<string, string[]>,
|
|
199
204
|
): void {
|
|
@@ -553,7 +558,7 @@ export function applySceneRenderPipeline(
|
|
|
553
558
|
renderer: THREE.WebGLRenderer,
|
|
554
559
|
scene: THREE.Scene,
|
|
555
560
|
camera: THREE.Camera,
|
|
556
|
-
env?:
|
|
561
|
+
env?: RenderEnvironment,
|
|
557
562
|
options?: {
|
|
558
563
|
basePixelRatio?: number;
|
|
559
564
|
objectMap?: Map<string, THREE.Object3D>;
|
|
@@ -1,48 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@engine/world3d-react` — the React context that carries
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* `@engine/world3d-react` — the React context that carries this R3F world's
|
|
3
|
+
* engine runtime into its fiber tree: the world's `R3FGameContext`, read
|
|
4
|
+
* with {@link useGameContext}.
|
|
5
5
|
*
|
|
6
|
-
* Upstreamed from `examples/r3f-first-party/src/engine-bridge.ts` (R4
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* GameComponents tick in engine phase order, pause included (no update while
|
|
13
|
-
* the world is frozen), exactly like scene-authored components.
|
|
6
|
+
* Upstreamed from `examples/r3f-first-party/src/engine-bridge.ts` (R4).
|
|
7
|
+
* The bridge value
|
|
8
|
+
* is built by `createR3FAdapter`'s `mount()` (./r3f-adapter.tsx): a real
|
|
9
|
+
* `createSystemRunner` whose phases the adapter's `update(dt)` runs under the
|
|
10
|
+
* HOST loop — so registered systems tick in engine phase order, pause included
|
|
11
|
+
* (no update while the world is frozen).
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* imports project code; the project hands its registry to
|
|
18
|
-
* `createR3FAdapter({ components })`, which places it here.
|
|
13
|
+
* Behavior in an R3F world is components and hooks: `useGameContext` is how a
|
|
14
|
+
* component reaches engine services.
|
|
19
15
|
*/
|
|
20
16
|
|
|
21
|
-
import { createContext } from 'react';
|
|
22
|
-
import type {
|
|
23
|
-
import type { GameComponentClass } from '../ecs/game-component';
|
|
24
|
-
|
|
25
|
-
/** A project's behavior registry: `<Behavior type>` keys → component classes. */
|
|
26
|
-
export type BehaviorRegistry = Readonly<Record<string, GameComponentClass>>;
|
|
17
|
+
import { createContext, useContext } from 'react';
|
|
18
|
+
import type { R3FGameContext } from './world-context';
|
|
27
19
|
|
|
28
20
|
export interface EngineBridgeValue {
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
readonly
|
|
21
|
+
/** This root's context — `input`, `debug`, `random`, `game`, `roots`, … .
|
|
22
|
+
* Present so ordinary components can reach engine services with a HOOK
|
|
23
|
+
* (see {@link useGameContext}). */
|
|
24
|
+
readonly ctx?: R3FGameContext | undefined;
|
|
33
25
|
}
|
|
34
26
|
|
|
35
27
|
export const EngineBridge = createContext<EngineBridgeValue | null>(null);
|
|
36
28
|
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
/**
|
|
30
|
+
* This R3F root's context, for use inside any component in the tree — an
|
|
31
|
+
* {@link R3FGameContext}, which is exactly the subsystems an R3F world builds.
|
|
32
|
+
* The ones it does not build are absent from the type, so reaching for one is
|
|
33
|
+
* a compile error at the call site; that type's doc comment says what to reach
|
|
34
|
+
* for instead.
|
|
35
|
+
*
|
|
36
|
+
* An ordinary component does what a model would write:
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* function Player({ speed = 4.5 }) {
|
|
40
|
+
* const { input } = useGameContext();
|
|
41
|
+
* const ref = useRef<THREE.Group>(null);
|
|
42
|
+
* useFrame((_, dt) => { if (input.isPressed('move_forward')) … });
|
|
43
|
+
* return <group ref={ref}><PlayerCharacter /></group>;
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* Throws when called outside an R3F root mounted by `createR3FAdapter` —
|
|
48
|
+
* absent context is a wiring bug, and a silent `null` would surface as a
|
|
49
|
+
* mystery `undefined` deep in a frame callback.
|
|
50
|
+
*/
|
|
51
|
+
export function useGameContext(): R3FGameContext {
|
|
52
|
+
const bridge = useContext(EngineBridge);
|
|
53
|
+
if (!bridge?.ctx) {
|
|
43
54
|
throw new Error(
|
|
44
|
-
|
|
55
|
+
'useGameContext(): no engine root context. This hook only works inside an R3F ' +
|
|
56
|
+
'root mounted by createR3FAdapter (@engine/world3d-react) — check that the ' +
|
|
57
|
+
'component is rendered inside your world entry, not in a standalone <Canvas>.',
|
|
45
58
|
);
|
|
46
59
|
}
|
|
47
|
-
return
|
|
60
|
+
return bridge.ctx;
|
|
48
61
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* world3d-react — the react-three-fiber bridge for vgai's
|
|
2
|
+
* world3d-react — the react-three-fiber bridge for vgai's three surface
|
|
3
3
|
* (`@engine/world3d-react`).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* from the live-proven `examples/r3f-first-party` project copies (R4
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `packages/engine/test/react-core-import-ban.test.ts` — this directory is
|
|
11
|
-
*
|
|
12
|
-
*
|
|
5
|
+
* An OPT-IN module, upstreamed
|
|
6
|
+
* from the live-proven `examples/r3f-first-party` project copies (R4;
|
|
7
|
+
* decision D24 — R3F TSX is the blessed three.js authoring path). Mirrors how
|
|
8
|
+
* `pixi/` is the PixiJS surface's opt-in home: a peer surface module the engine
|
|
9
|
+
* CORE never imports (enforced by
|
|
10
|
+
* `packages/engine/test/react-core-import-ban.test.ts` — this directory is an
|
|
11
|
+
* allowed react-importing entry alongside `react/`, and core files may not
|
|
12
|
+
* import it).
|
|
13
13
|
*
|
|
14
14
|
* Peer contract: an importing PROJECT already depends on `react`,
|
|
15
15
|
* `@react-three/fiber`, and `three` (fiber's three must be deduped to the
|
|
@@ -19,26 +19,31 @@
|
|
|
19
19
|
* that already mount through fiber ever import this module.
|
|
20
20
|
*
|
|
21
21
|
* Surface:
|
|
22
|
-
* - `createR3FAdapter({ id, content
|
|
23
|
-
*
|
|
24
|
-
* -
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
22
|
+
* - `createR3FAdapter({ id, content })` — mount an R3F tree as a first-party
|
|
23
|
+
* `kind: "three"` world under the host's gated loop.
|
|
24
|
+
* - `useGameContext()` — this root's `R3FGameContext` (`input`, `debug`,
|
|
25
|
+
* `random`, `game`, `roots`, …) from inside any component in the tree. This
|
|
26
|
+
* is how an R3F world reaches engine services: behavior is components +
|
|
27
|
+
* hooks (D26b), not a class under a string key. The subsystems an R3F world
|
|
28
|
+
* does not build are absent from that type — see its doc comment for what
|
|
29
|
+
* to use instead.
|
|
30
|
+
* - `EngineBridge` / `EngineBridgeValue` — the context contract, exported
|
|
31
|
+
* for advanced adopters building their own bridge.
|
|
29
32
|
*/
|
|
30
33
|
|
|
31
|
-
export {
|
|
32
|
-
export {
|
|
33
|
-
type BehaviorRegistry,
|
|
34
|
-
EngineBridge,
|
|
35
|
-
type EngineBridgeValue,
|
|
36
|
-
resolveBehaviorClass,
|
|
37
|
-
} from './engine-bridge';
|
|
34
|
+
export { EngineBridge, type EngineBridgeValue, useGameContext } from './engine-bridge';
|
|
38
35
|
export { type CreateR3FAdapterOptions, createR3FAdapter } from './r3f-adapter';
|
|
36
|
+
export { r3fRootFactory, resolveR3FEntryAdapter } from './r3f-root-factory';
|
|
37
|
+
export {
|
|
38
|
+
applyWorldRendererConfig,
|
|
39
|
+
type WorldOutputColorSpace,
|
|
40
|
+
type WorldRendererConfig,
|
|
41
|
+
type WorldToneMapping,
|
|
42
|
+
} from './renderer-config';
|
|
39
43
|
export {
|
|
40
|
-
|
|
44
|
+
createR3FRootContext,
|
|
41
45
|
DEFAULT_INPUT_MAP_PATH,
|
|
42
|
-
type
|
|
43
|
-
type
|
|
46
|
+
type R3FGameContext,
|
|
47
|
+
type R3FRootContextOptions,
|
|
48
|
+
type R3FRootRuntime,
|
|
44
49
|
} from './world-context';
|