@vgai/engine 0.5.2 → 0.5.4
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/scene/scene-query.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import type * as THREE from 'three';
|
|
2
|
-
import { getUserData } from './user-data';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Runtime queries over a loaded THREE scene graph (P1.6d).
|
|
6
|
-
*
|
|
7
|
-
* The scene's `tags` schema field is documented as "for runtime queries" but,
|
|
8
|
-
* until now, no query existed. These helpers walk a THREE.Object3D graph and
|
|
9
|
-
* read entity metadata off `userData`, so gameplay code can find objects by tag,
|
|
10
|
-
* name, or attached component without bookkeeping its own indexes.
|
|
11
|
-
*
|
|
12
|
-
* Canonical metadata locations (in priority order):
|
|
13
|
-
* - tags: `userData['tags']` (string[]) — falls back to `userData['entity'].tags`
|
|
14
|
-
* - name: `Object3D.name`
|
|
15
|
-
* - components: `userData['components']` (record or string[]) — falls back to
|
|
16
|
-
* `userData['entity'].components` (record keyed by component name)
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
function getTags(obj: THREE.Object3D): string[] {
|
|
20
|
-
const direct = getUserData(obj, 'tags');
|
|
21
|
-
if (Array.isArray(direct)) return direct;
|
|
22
|
-
const fromDef = getUserData(obj, 'entity')?.tags;
|
|
23
|
-
return Array.isArray(fromDef) ? fromDef : [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function getComponentNames(obj: THREE.Object3D): string[] {
|
|
27
|
-
const direct = getUserData(obj, 'components');
|
|
28
|
-
if (Array.isArray(direct)) return direct;
|
|
29
|
-
if (direct && typeof direct === 'object') return Object.keys(direct);
|
|
30
|
-
const fromDef = getUserData(obj, 'entity')?.components;
|
|
31
|
-
if (fromDef && typeof fromDef === 'object') return Object.keys(fromDef);
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** All objects in the graph carrying the given tag (depth-first, document order). */
|
|
36
|
-
export function queryByTag(root: THREE.Object3D, tag: string): THREE.Object3D[] {
|
|
37
|
-
const matches: THREE.Object3D[] = [];
|
|
38
|
-
root.traverse((obj) => {
|
|
39
|
-
if (getTags(obj).includes(tag)) matches.push(obj);
|
|
40
|
-
});
|
|
41
|
-
return matches;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** All objects in the graph with the given name (depth-first, document order). */
|
|
45
|
-
export function queryByName(root: THREE.Object3D, name: string): THREE.Object3D[] {
|
|
46
|
-
const matches: THREE.Object3D[] = [];
|
|
47
|
-
root.traverse((obj) => {
|
|
48
|
-
if (obj.name === name) matches.push(obj);
|
|
49
|
-
});
|
|
50
|
-
return matches;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* All objects in the graph that have the named component attached
|
|
55
|
-
* (depth-first, document order).
|
|
56
|
-
*/
|
|
57
|
-
export function queryByComponent(root: THREE.Object3D, componentName: string): THREE.Object3D[] {
|
|
58
|
-
const matches: THREE.Object3D[] = [];
|
|
59
|
-
root.traverse((obj) => {
|
|
60
|
-
if (getComponentNames(obj).includes(componentName)) matches.push(obj);
|
|
61
|
-
});
|
|
62
|
-
return matches;
|
|
63
|
-
}
|
package/src/scene/scene-types.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scene file types — derived from Zod schemas (single source of truth).
|
|
3
|
-
*
|
|
4
|
-
* Raw types for the .vscn.json file format. Both the editor and the runtime
|
|
5
|
-
* loader work with these directly, applying DEFAULTS (./defaults.ts) for omitted
|
|
6
|
-
* fields — DEFAULTS is the single source of truth for default values.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type {
|
|
10
|
-
InstancesFile,
|
|
11
|
-
MaterialFile,
|
|
12
|
-
PrefabFile,
|
|
13
|
-
SceneAnimation,
|
|
14
|
-
SceneAudio,
|
|
15
|
-
SceneCamera,
|
|
16
|
-
SceneCollider,
|
|
17
|
-
SceneEntity,
|
|
18
|
-
SceneEnvironment,
|
|
19
|
-
SceneFile,
|
|
20
|
-
SceneJoint,
|
|
21
|
-
SceneLight,
|
|
22
|
-
SceneMaterial,
|
|
23
|
-
SceneMesh,
|
|
24
|
-
SceneNavigation,
|
|
25
|
-
SceneParticles,
|
|
26
|
-
ScenePhysics,
|
|
27
|
-
ScenePhysicsSettings,
|
|
28
|
-
ScenePostProcessing,
|
|
29
|
-
SceneSpline,
|
|
30
|
-
SceneToneMapping,
|
|
31
|
-
} from './schema';
|
|
32
|
-
|
|
33
|
-
export { mergePrefabInstance } from './schema';
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The scene/prefab file format `version` range this engine checkout can
|
|
3
|
-
* load (T2.3/D3 §1.E, docs/ENGINE-UPGRADE-DESIGN.md). `[min, max]` — today
|
|
4
|
-
* `[1, 1]`: every shipped `.vscn.json`/`.prefab.json` is version 1, and no
|
|
5
|
-
* migration machinery exists yet (a future format bump widens `max`, and a
|
|
6
|
-
* future engine that drops support for `1` would raise `min`).
|
|
7
|
-
*
|
|
8
|
-
* Deliberately NOT in `./schema/` (that directory's byte contents are
|
|
9
|
-
* pinned by `packages/engine/schemas/engine-capabilities.json`'s
|
|
10
|
-
* `schemaDirHash`, refreshed only by `scripts/capabilities-hash.ts` — this
|
|
11
|
-
* slice does not run that script). This is also why the check below is a
|
|
12
|
-
* POST-Zod-parse function here in `scene/`, not a Zod refinement inside
|
|
13
|
-
* `SceneFileSchema`/`PrefabFileSchema`: the generated `vscn.schema.json`/
|
|
14
|
-
* `prefab.schema.json` stay byte-identical (the `version` field itself is
|
|
15
|
-
* still just `z.number()`).
|
|
16
|
-
*/
|
|
17
|
-
export const SUPPORTED_SCENE_VERSION_RANGE: readonly [number, number] = [1, 1];
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Throw if `version` falls outside `SUPPORTED_SCENE_VERSION_RANGE`; silently
|
|
21
|
-
* accept in-range versions. `fileKind` only changes the error message's noun
|
|
22
|
-
* (`.vscn.json` and `.prefab.json` share this single version axis — verified
|
|
23
|
-
* both schemas declare their own `version: z.number()` field, not a shared
|
|
24
|
-
* ref, but the same counter/semantics per docs/ENGINE-UPGRADE-DESIGN.md).
|
|
25
|
-
*/
|
|
26
|
-
export function checkSceneVersionInRange(
|
|
27
|
-
version: number,
|
|
28
|
-
fileKind: 'scene' | 'prefab' = 'scene',
|
|
29
|
-
): void {
|
|
30
|
-
const [min, max] = SUPPORTED_SCENE_VERSION_RANGE;
|
|
31
|
-
if (version >= min && version <= max) return;
|
|
32
|
-
const cause =
|
|
33
|
-
version > max
|
|
34
|
-
? `the engine checkout is older than this ${fileKind}`
|
|
35
|
-
: `this ${fileKind} predates the oldest supported format version`;
|
|
36
|
-
throw new Error(
|
|
37
|
-
`${fileKind} version ${version} is outside the supported range [${min}, ${max}] — ` +
|
|
38
|
-
`${cause}; see docs/ENGINE-UPGRADE-DESIGN.md`,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// D8 — declarative property tracks (formerly F5,
|
|
5
|
-
// docs/VSCN-STRUCTURAL-GAPS-DESIGN.md) were REMOVED: Theatre
|
|
6
|
-
// (`@theatre/core`) is the sole authored continuous-animation source (spec
|
|
7
|
-
// §3.2, §10 D2-D4/D8). `PropertyTrackSchema`/`PropertyTrackRunner` and the
|
|
8
|
-
// `tracks` field are deleted, not deprecated-in-place — see
|
|
9
|
-
// packages/engine/src/scene/parse.ts (`assertNoRemovedPropertyTracks`) for
|
|
10
|
-
// the loud migration-error guard that rejects any `.vscn`/`.prefab.json`
|
|
11
|
-
// still authoring `animation.tracks`, since Zod would otherwise silently
|
|
12
|
-
// strip an unrecognized field rather than failing loudly (Global AC §6).
|
|
13
|
-
//
|
|
14
|
-
// E5 — the AnimGraph state-machine runtime, `.animgraph.json` format, and
|
|
15
|
-
// every consumer were removed (spec §3.3, §11 E5; XState + native Three
|
|
16
|
-
// `AnimationMixer` replace it — see `xstate-animation-binding.ts`). The
|
|
17
|
-
// `animGraph` field is deleted outright, not deprecated-in-place, for the
|
|
18
|
-
// same reason as `tracks` above: Zod's default object mode would otherwise
|
|
19
|
-
// silently STRIP an authored `animation.animGraph` rather than reporting it,
|
|
20
|
-
// violating Global AC §6 ("Removed formats fail with a concise migration/
|
|
21
|
-
// removal error rather than being partially read"). The raw-JSON guard
|
|
22
|
-
// (`assertNoRemovedAnimGraph` in packages/engine/src/scene/parse.ts) rejects
|
|
23
|
-
// any `.vscn`/`.prefab.json` still authoring it before Zod ever parses the
|
|
24
|
-
// data — see packages/engine/test/anim-graph-removed.test.ts.
|
|
25
|
-
//
|
|
26
|
-
// `parameters` is deleted alongside `animGraph`, though it isn't itself named
|
|
27
|
-
// in the spec/removal-inventory: its ONLY documented purpose was seeding an
|
|
28
|
-
// `AnimGraph`'s initial parameter values (its only readers were
|
|
29
|
-
// `scene-loader.ts`'s former animGraph branch, the editor's entity-factory.ts,
|
|
30
|
-
// and AnimationSection.tsx's parameter-row UI — all deleted with the runtime).
|
|
31
|
-
// Leaving it declared with zero remaining readers would violate this repo's
|
|
32
|
-
// "no schema field without a runtime reader" policy (CLAUDE.md).
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
export const SceneAnimationSchema = z
|
|
36
|
-
.object({
|
|
37
|
-
autoplay: z.string().optional().describe('Clip name to play automatically on load'),
|
|
38
|
-
loop: z.boolean().optional().describe('Whether the autoplay clip should loop'),
|
|
39
|
-
clipAliases: z
|
|
40
|
-
.record(z.string(), z.string())
|
|
41
|
-
.optional()
|
|
42
|
-
.describe('Map raw GLTF clip names to clean names'),
|
|
43
|
-
})
|
|
44
|
-
.describe('Animation playback configuration');
|
|
45
|
-
|
|
46
|
-
export type SceneAnimation = z.infer<typeof SceneAnimationSchema>;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
export const SceneAudioSchema = z
|
|
4
|
-
.object({
|
|
5
|
-
src: z.string().optional().describe('Path to audio file (WAV, MP3, OGG)'),
|
|
6
|
-
spatial: z.boolean().optional().describe('Enable 3D spatial audio (positional sound)'),
|
|
7
|
-
volume: z.number().optional().describe('Playback volume 0–1'),
|
|
8
|
-
refDistance: z
|
|
9
|
-
.number()
|
|
10
|
-
.optional()
|
|
11
|
-
.describe('Distance at which volume starts to attenuate (spatial only)'),
|
|
12
|
-
rolloffFactor: z
|
|
13
|
-
.number()
|
|
14
|
-
.optional()
|
|
15
|
-
.describe('How quickly volume attenuates with distance (spatial only)'),
|
|
16
|
-
maxDistance: z
|
|
17
|
-
.number()
|
|
18
|
-
.optional()
|
|
19
|
-
.describe('Maximum distance at which sound is audible (spatial only)'),
|
|
20
|
-
loop: z.boolean().optional().describe('Whether the audio should loop'),
|
|
21
|
-
autoplay: z.boolean().optional().describe('Start playing automatically when the scene loads'),
|
|
22
|
-
})
|
|
23
|
-
.describe('Audio source configuration');
|
|
24
|
-
|
|
25
|
-
export type SceneAudio = z.infer<typeof SceneAudioSchema>;
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* F6 (`docs/VSCN-STRUCTURAL-GAPS-DESIGN.md`) — a first-class, reusable
|
|
5
|
-
* reference from one entity to another, for a `GameComponent`'s own static
|
|
6
|
-
* Zod schema (e.g. `target: EntityRefSchema` — "this button opens THAT
|
|
7
|
-
* door"). A branded string: on disk (and through generic component-data
|
|
8
|
-
* validation) it is a plain string id, but a component field typed
|
|
9
|
-
* `EntityRef` is compile-time distinct from an arbitrary `string` field, so a
|
|
10
|
-
* bare label can't be assigned where a ref is expected.
|
|
11
|
-
*
|
|
12
|
-
* Resolution is a runtime concern, not a schema one: `scene-loader.ts`'s
|
|
13
|
-
* `resolveEntityRefs` (the post-spawn pass, modeled on `joint.target`'s
|
|
14
|
-
* existing resolution) rewrites the field from the authored string id to the
|
|
15
|
-
* live `THREE.Object3D` — through the SAME `idMap` `createSceneJoints`
|
|
16
|
-
* already uses, no new registry (G3) — before the referencing component's
|
|
17
|
-
* `init()` runs.
|
|
18
|
-
*/
|
|
19
|
-
export const EntityRefSchema = z
|
|
20
|
-
.string()
|
|
21
|
-
.brand<'EntityRef'>()
|
|
22
|
-
.describe(
|
|
23
|
-
'A reference to another entity by its authored id. On disk it is a plain string id; the ' +
|
|
24
|
-
'scene loader resolves it to the live THREE.Object3D (WorldNode-as-truth) before the ' +
|
|
25
|
-
"referencing component's init() runs. A ref to a missing id is a loud load error.",
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
export type EntityRef = z.infer<typeof EntityRefSchema>;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Identity registry of the `EntityRefSchema` object (there is exactly one —
|
|
32
|
-
* this module's export), used by {@link isEntityRefSchema}.
|
|
33
|
-
*
|
|
34
|
-
* Why identity rather than a Zod "type name" check: zod 4's `.brand()` is a
|
|
35
|
-
* type-only cast (see zod's own source — `inst.brand = () => inst`), not a
|
|
36
|
-
* distinct wrapper class runtime code could switch on. A branded schema is,
|
|
37
|
-
* at runtime, just the underlying `ZodString` (here, after `.describe()`
|
|
38
|
-
* clones it once more — see zod's `check()`/`clone()`). So "is this an
|
|
39
|
-
* EntityRef field" can only be answered by reference identity against the
|
|
40
|
-
* one schema object every ref field is declared with — which is exactly
|
|
41
|
-
* what we want anyway: a look-alike `z.string()` field must NOT count.
|
|
42
|
-
*/
|
|
43
|
-
const ENTITY_REF_SCHEMAS = new WeakSet<object>();
|
|
44
|
-
ENTITY_REF_SCHEMAS.add(EntityRefSchema);
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Just enough of zod 4's internal `_def` shape to unwrap wrapper schemas.
|
|
48
|
-
* Typed against `z.core.$ZodType` (the base/"core" schema type, NOT the
|
|
49
|
-
* fuller "classic" `z.ZodType`) because that's what a `z.ZodObject`'s
|
|
50
|
-
* `.shape` values are typed as (`ZodRawShape = core.$ZodShape`) — the type
|
|
51
|
-
* every field schema `resolveEntityRefs` (`scene-loader.ts`) actually walks
|
|
52
|
-
* arrives as.
|
|
53
|
-
*/
|
|
54
|
-
interface UnwrappableDef {
|
|
55
|
-
innerType?: z.core.$ZodType;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Is `schema` an {@link EntityRefSchema} field — including when wrapped by
|
|
60
|
-
* `.optional()`, `.default(...)`, or `.nullable()` (the common ways a
|
|
61
|
-
* component author would declare an optional ref)? Unwraps those via zod 4's
|
|
62
|
-
* internal `_def.innerType` (there is no public reflection API for this —
|
|
63
|
-
* `packages/editor/src/components/inspectors/zod-fields.ts` uses the same
|
|
64
|
-
* mechanism to introspect component schemas for the inspector) down to the
|
|
65
|
-
* core schema, then checks it against the registered `EntityRefSchema`
|
|
66
|
-
* instance by reference.
|
|
67
|
-
*/
|
|
68
|
-
export function isEntityRefSchema(schema: z.core.$ZodType): boolean {
|
|
69
|
-
let cur: z.core.$ZodType | undefined = schema;
|
|
70
|
-
while (cur) {
|
|
71
|
-
if (ENTITY_REF_SCHEMAS.has(cur)) return true;
|
|
72
|
-
const inner: z.core.$ZodType | undefined = (cur as unknown as { _def?: UnwrappableDef })._def
|
|
73
|
-
?.innerType;
|
|
74
|
-
if (!inner) return false;
|
|
75
|
-
cur = inner;
|
|
76
|
-
}
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { buildRenderingZod } from '../../render/render-features';
|
|
3
|
-
import { SceneAnimationSchema } from './animation';
|
|
4
|
-
import { SceneAudioSchema } from './audio';
|
|
5
|
-
import { SceneCameraOverrideSchema, SceneCameraSchema } from './camera';
|
|
6
|
-
import { SceneJointSchema } from './joint';
|
|
7
|
-
import { SceneLightOverrideSchema, SceneLightSchema } from './light';
|
|
8
|
-
import { SceneMaterialOverrideSchema, SceneMaterialSchema } from './material';
|
|
9
|
-
import { SceneMeshOverrideSchema, SceneMeshSchema } from './mesh';
|
|
10
|
-
import { SceneParticlesOverrideSchema, SceneParticlesSchema } from './particles';
|
|
11
|
-
import { ScenePhysicsOverrideSchema, ScenePhysicsSchema } from './physics';
|
|
12
|
-
import { SceneShadowOverrideSchema, SceneShadowSchema } from './shadow';
|
|
13
|
-
import { SceneSplineOverrideSchema, SceneSplineSchema } from './spline';
|
|
14
|
-
import { TransformSchema, Vec3Schema } from './tuples';
|
|
15
|
-
|
|
16
|
-
const BoneAttachmentSchema = z
|
|
17
|
-
.object({
|
|
18
|
-
target: z.string().describe('Entity ID whose animated skeleton owns the target bone'),
|
|
19
|
-
bone: z.string().describe('Exact name of the target bone or socket Object3D'),
|
|
20
|
-
})
|
|
21
|
-
.describe(
|
|
22
|
-
'Attach this entity to a named bone/socket after the complete scene has loaded. The entity transform becomes its local attachment offset',
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* User-defined ECS component data. Validated at runtime against ComponentRegistry.
|
|
27
|
-
*
|
|
28
|
-
* Scalars (number/string/boolean) plus homogeneous arrays of those scalars so
|
|
29
|
-
* that vec3 (number[3]) and other array fields produced by the schema-driven
|
|
30
|
-
* inspector round-trip through parse/validate. The component's own static Zod
|
|
31
|
-
* schema (GameComponent.schema) does the precise per-field validation at load.
|
|
32
|
-
*
|
|
33
|
-
* F6 (`docs/VSCN-STRUCTURAL-GAPS-DESIGN.md`) entity refs (`EntityRefSchema`,
|
|
34
|
-
* `./entity-ref.ts`) are branded strings — structurally still a `string` on
|
|
35
|
-
* disk and through this coarse, per-component-agnostic schema, so they
|
|
36
|
-
* already parse via the `z.string()` branch below without a dedicated union
|
|
37
|
-
* member. (Deliberately not added as an explicit branch: this record's VALUE
|
|
38
|
-
* schema is the one place in the file with no `.describe()` calls anywhere in
|
|
39
|
-
* it — see `schema-consumption-map.ts`'s `entity.components` entry — and
|
|
40
|
-
* `EntityRefSchema` carries one, which would flip `entity.components` from a
|
|
41
|
-
* plain leaf into a `subtree: true` entry for no behavioral gain: the
|
|
42
|
-
* precise, per-field validation of a ref — and the brand — comes from the
|
|
43
|
-
* referencing component's OWN static schema in `applyComponents`, not from
|
|
44
|
-
* this generic envelope.)
|
|
45
|
-
*/
|
|
46
|
-
const ComponentScalarSchema = z.union([z.number(), z.string(), z.boolean()]);
|
|
47
|
-
const ComponentValueSchema = z.union([
|
|
48
|
-
ComponentScalarSchema,
|
|
49
|
-
z.array(z.number()),
|
|
50
|
-
z.array(z.string()),
|
|
51
|
-
z.array(z.boolean()),
|
|
52
|
-
]);
|
|
53
|
-
const ComponentDataSchema = z.record(z.string(), ComponentValueSchema);
|
|
54
|
-
const ComponentsSchema = z.record(z.string(), ComponentDataSchema);
|
|
55
|
-
|
|
56
|
-
const NavigationRoleSchema = z
|
|
57
|
-
.object({
|
|
58
|
-
role: z
|
|
59
|
-
.enum(['walkable', 'obstacle'])
|
|
60
|
-
.describe('Whether this entity is walkable surface or an obstacle'),
|
|
61
|
-
})
|
|
62
|
-
.describe('Navigation mesh role');
|
|
63
|
-
|
|
64
|
-
// Fields shared by both standalone and prefab instance entities
|
|
65
|
-
const SharedEntityFields = {
|
|
66
|
-
id: z.string().optional().describe('Unique entity ID. Auto-generated if omitted'),
|
|
67
|
-
name: z.string().describe('Human-readable entity name (shown in hierarchy panel)'),
|
|
68
|
-
visible: z.boolean().optional().describe('Whether the entity and its children are rendered'),
|
|
69
|
-
locked: z.boolean().optional().describe('Prevent selection and editing in the scene editor'),
|
|
70
|
-
tags: z.array(z.string()).optional().describe('Arbitrary string tags for runtime queries'),
|
|
71
|
-
prefab: z.string().optional().describe('Path to .prefab.json file to instantiate'),
|
|
72
|
-
materialRef: z
|
|
73
|
-
.string()
|
|
74
|
-
.optional()
|
|
75
|
-
.describe('Path to shared .mat.json material asset. Mutually exclusive with inline material'),
|
|
76
|
-
transform: TransformSchema.optional().describe('Local transform relative to parent'),
|
|
77
|
-
boneAttachment: BoneAttachmentSchema.optional().describe(
|
|
78
|
-
'Animated bone/socket attachment. Use transform for the local position, rotation, and scale offset',
|
|
79
|
-
),
|
|
80
|
-
pivot: Vec3Schema.optional().describe(
|
|
81
|
-
'Local-space pivot point for rotation/scale. [0,0,0] = geometry center',
|
|
82
|
-
),
|
|
83
|
-
navigation: NavigationRoleSchema.optional().describe('Navigation mesh role for this entity'),
|
|
84
|
-
render: buildRenderingZod({ perEntityOnly: true })
|
|
85
|
-
.optional()
|
|
86
|
-
.describe(
|
|
87
|
-
'Per-entity render overrides (e.g. autoBatch:false to keep a custom-shader entity out of batching). Omit to inherit from the scene/engine default.',
|
|
88
|
-
),
|
|
89
|
-
components: ComponentsSchema.optional().describe(
|
|
90
|
-
'User-defined ECS component data (validated against component registry)',
|
|
91
|
-
),
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// Standalone entities: full section validation (type fields required)
|
|
95
|
-
const StandaloneEntityBase = z.object({
|
|
96
|
-
...SharedEntityFields,
|
|
97
|
-
mesh: SceneMeshSchema.optional().describe('Mesh geometry'),
|
|
98
|
-
material: SceneMaterialSchema.optional().describe('Material appearance'),
|
|
99
|
-
light: SceneLightSchema.optional().describe('Light source'),
|
|
100
|
-
shadow: SceneShadowSchema.optional().describe('Shadow configuration'),
|
|
101
|
-
physics: ScenePhysicsSchema.optional().describe('Physics rigid body'),
|
|
102
|
-
camera: SceneCameraSchema.optional().describe('Camera'),
|
|
103
|
-
audio: SceneAudioSchema.optional().describe('Audio source'),
|
|
104
|
-
animation: SceneAnimationSchema.optional().describe('Animation configuration'),
|
|
105
|
-
particles: SceneParticlesSchema.optional().describe('Particle system'),
|
|
106
|
-
spline: SceneSplineSchema.optional().describe('Spline path'),
|
|
107
|
-
joints: z
|
|
108
|
-
.array(SceneJointSchema)
|
|
109
|
-
.optional()
|
|
110
|
-
.describe('Physics joints connecting to other bodies'),
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Prefab instance entities: section overrides are partial (merged with prefab root at load time)
|
|
114
|
-
const PrefabInstanceBase = z.object({
|
|
115
|
-
...SharedEntityFields,
|
|
116
|
-
prefab: z.string().describe('Path to .prefab.json file to instantiate'),
|
|
117
|
-
mesh: SceneMeshOverrideSchema.optional().describe('Mesh overrides merged with prefab'),
|
|
118
|
-
material: SceneMaterialOverrideSchema.optional().describe(
|
|
119
|
-
'Material overrides merged with prefab',
|
|
120
|
-
),
|
|
121
|
-
light: SceneLightOverrideSchema.optional().describe('Light overrides merged with prefab'),
|
|
122
|
-
shadow: SceneShadowOverrideSchema.optional().describe('Shadow overrides merged with prefab'),
|
|
123
|
-
physics: ScenePhysicsOverrideSchema.optional().describe('Physics overrides merged with prefab'),
|
|
124
|
-
camera: SceneCameraOverrideSchema.optional().describe('Camera overrides merged with prefab'),
|
|
125
|
-
audio: SceneAudioSchema.optional().describe('Audio source'),
|
|
126
|
-
animation: SceneAnimationSchema.optional().describe('Animation configuration'),
|
|
127
|
-
particles: SceneParticlesOverrideSchema.optional().describe(
|
|
128
|
-
'Particle overrides merged with prefab',
|
|
129
|
-
),
|
|
130
|
-
spline: SceneSplineOverrideSchema.optional().describe('Spline overrides merged with prefab'),
|
|
131
|
-
joints: z
|
|
132
|
-
.array(SceneJointSchema)
|
|
133
|
-
.optional()
|
|
134
|
-
.describe('Physics joints connecting to other bodies'),
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* SceneEntity type — the strict form used by all consumer code.
|
|
139
|
-
* Section types have required discriminant fields (type, bodyType, etc.).
|
|
140
|
-
*
|
|
141
|
-
* Prefab instances in scene files may have partial sections, but they are
|
|
142
|
-
* always completed by mergePrefabInstance() before being consumed.
|
|
143
|
-
*/
|
|
144
|
-
export type SceneEntity = z.infer<typeof StandaloneEntityBase> & {
|
|
145
|
-
children?: SceneEntity[] | undefined;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Validation schema for scene entity JSON.
|
|
150
|
-
*
|
|
151
|
-
* Standalone entities are validated strictly (section discriminants required).
|
|
152
|
-
* Prefab instances accept partial section overrides — these are merged with
|
|
153
|
-
* the prefab root at load time via mergePrefabInstance().
|
|
154
|
-
*
|
|
155
|
-
* Typed as SceneEntity (strict) because prefab instances always go through
|
|
156
|
-
* mergePrefabInstance() before any code reads their section data.
|
|
157
|
-
*/
|
|
158
|
-
export const SceneEntitySchema: z.ZodType<SceneEntity> = z.lazy(() =>
|
|
159
|
-
z
|
|
160
|
-
.union([
|
|
161
|
-
StandaloneEntityBase.extend({
|
|
162
|
-
children: z.array(SceneEntitySchema).optional().describe('Child entities'),
|
|
163
|
-
}),
|
|
164
|
-
PrefabInstanceBase.extend({
|
|
165
|
-
children: z.array(SceneEntitySchema).optional().describe('Child entities'),
|
|
166
|
-
}),
|
|
167
|
-
])
|
|
168
|
-
// P1.6a: `material` (inline) and `materialRef` (shared .mat.json asset) are
|
|
169
|
-
// mutually exclusive — allowing both lets one silently win at load time.
|
|
170
|
-
// Reject up front instead of trapping the author.
|
|
171
|
-
.superRefine((entity, ctx) => {
|
|
172
|
-
if (entity.material !== undefined && entity.materialRef !== undefined) {
|
|
173
|
-
ctx.addIssue({
|
|
174
|
-
code: z.ZodIssueCode.custom,
|
|
175
|
-
message:
|
|
176
|
-
'An entity cannot set both `material` (inline) and `materialRef` (shared .mat.json asset). Choose one.',
|
|
177
|
-
path: ['material'],
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
if (entity.boneAttachment !== undefined && entity.physics !== undefined) {
|
|
181
|
-
ctx.addIssue({
|
|
182
|
-
code: z.ZodIssueCode.custom,
|
|
183
|
-
message:
|
|
184
|
-
'A bone-attached entity cannot declare physics. Attachments follow an animated transform; add gameplay collision to a separate entity.',
|
|
185
|
-
path: ['boneAttachment'],
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
}),
|
|
189
|
-
) as z.ZodType<SceneEntity>;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// Zod schemas
|
|
2
|
-
|
|
3
|
-
export type { SceneAnimation } from './animation';
|
|
4
|
-
export { SceneAnimationSchema } from './animation';
|
|
5
|
-
export type { SceneAudio } from './audio';
|
|
6
|
-
export { SceneAudioSchema } from './audio';
|
|
7
|
-
export type { SceneCamera } from './camera';
|
|
8
|
-
export { SceneCameraSchema } from './camera';
|
|
9
|
-
export type { SceneCollider } from './collider';
|
|
10
|
-
export { SceneColliderSchema } from './collider';
|
|
11
|
-
export type { SceneEntity } from './entity';
|
|
12
|
-
export { SceneEntitySchema } from './entity';
|
|
13
|
-
export type { EntityRef } from './entity-ref';
|
|
14
|
-
export { EntityRefSchema, isEntityRefSchema } from './entity-ref';
|
|
15
|
-
export type {
|
|
16
|
-
SceneEnvironment,
|
|
17
|
-
SceneNavigation,
|
|
18
|
-
ScenePhysicsSettings,
|
|
19
|
-
ScenePostProcessing,
|
|
20
|
-
SceneToneMapping,
|
|
21
|
-
} from './environment';
|
|
22
|
-
export {
|
|
23
|
-
SceneEnvironmentSchema,
|
|
24
|
-
SceneNavigationSchema,
|
|
25
|
-
ScenePhysicsSettingsSchema,
|
|
26
|
-
ScenePostProcessingSchema,
|
|
27
|
-
SceneToneMappingSchema,
|
|
28
|
-
} from './environment';
|
|
29
|
-
export type { InstancesFile } from './instances';
|
|
30
|
-
export { InstancesFileSchema } from './instances';
|
|
31
|
-
export type { SceneJoint } from './joint';
|
|
32
|
-
export { SceneJointSchema } from './joint';
|
|
33
|
-
export type { SceneLight } from './light';
|
|
34
|
-
export { SceneLightSchema } from './light';
|
|
35
|
-
export type { MaterialFile, SceneMaterial } from './material';
|
|
36
|
-
export { MaterialFileSchema, SceneMaterialSchema } from './material';
|
|
37
|
-
export type { SceneMesh } from './mesh';
|
|
38
|
-
export { SceneMeshSchema } from './mesh';
|
|
39
|
-
export type { SceneParticles } from './particles';
|
|
40
|
-
export { SceneParticlesOverrideSchema, SceneParticlesSchema } from './particles';
|
|
41
|
-
export type { ScenePhysics } from './physics';
|
|
42
|
-
export { ScenePhysicsSchema } from './physics';
|
|
43
|
-
export type { PrefabFile, SceneFile } from './scene-file';
|
|
44
|
-
export { mergePrefabInstance, PrefabFileSchema, SceneFileSchema } from './scene-file';
|
|
45
|
-
export type { SceneShadow } from './shadow';
|
|
46
|
-
export { SceneShadowSchema } from './shadow';
|
|
47
|
-
export type { SceneSpline } from './spline';
|
|
48
|
-
export { SceneSplineOverrideSchema, SceneSplineSchema } from './spline';
|
|
49
|
-
// Inferred types
|
|
50
|
-
export type { Quat, SceneTransform, Vec3 } from './tuples';
|
|
51
|
-
export { QuatSchema, TransformSchema, Vec3Schema } from './tuples';
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { Vec3Schema } from './tuples';
|
|
3
|
-
|
|
4
|
-
export const SceneJointSchema = z
|
|
5
|
-
.object({
|
|
6
|
-
type: z
|
|
7
|
-
.enum(['fixed', 'revolute', 'prismatic', 'spherical', 'spring'])
|
|
8
|
-
.describe('Joint constraint type'),
|
|
9
|
-
target: z.string().describe('Entity ID of the body this joint connects to'),
|
|
10
|
-
anchor: Vec3Schema.optional().describe('Local anchor point on this body'),
|
|
11
|
-
targetAnchor: Vec3Schema.optional().describe('Local anchor point on the target body'),
|
|
12
|
-
axis: Vec3Schema.optional().describe('Rotation/slide axis for revolute and prismatic joints'),
|
|
13
|
-
limits: z
|
|
14
|
-
.object({
|
|
15
|
-
min: z.number().describe('Minimum angle (degrees) or distance'),
|
|
16
|
-
max: z.number().describe('Maximum angle (degrees) or distance'),
|
|
17
|
-
})
|
|
18
|
-
.optional()
|
|
19
|
-
.describe('Angle or distance limits for revolute/prismatic joints'),
|
|
20
|
-
stiffness: z.number().optional().describe('Spring stiffness coefficient'),
|
|
21
|
-
damping: z.number().optional().describe('Spring damping coefficient'),
|
|
22
|
-
restLength: z.number().optional().describe('Spring rest length'),
|
|
23
|
-
})
|
|
24
|
-
.describe('Physics joint constraint between two bodies');
|
|
25
|
-
|
|
26
|
-
export type SceneJoint = z.infer<typeof SceneJointSchema>;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { SceneColliderOverrideSchema, SceneColliderSchema } from './collider';
|
|
3
|
-
|
|
4
|
-
export const ScenePhysicsSchema = z
|
|
5
|
-
.object({
|
|
6
|
-
bodyType: z
|
|
7
|
-
.enum(['dynamic', 'fixed', 'kinematic'])
|
|
8
|
-
.describe(
|
|
9
|
-
'Physics body type: dynamic (simulated), fixed (immovable), kinematic (script-driven). ' +
|
|
10
|
-
'T1.1/R1c: ANY physics body (dynamic, fixed, OR kinematic) nested under an ancestor ' +
|
|
11
|
-
'entity that itself has a "dynamic" or "kinematic" body is unsupported (loud load ' +
|
|
12
|
-
'error) — Rapier has no parent/child body relationship, so a moving ancestor cannot ' +
|
|
13
|
-
"carry a nested body of ANY type with it; the child's Object3D would stay pinned to " +
|
|
14
|
-
"its own (non-moving) body's pose while the ancestor moves out from under it. " +
|
|
15
|
-
'Nesting under a "fixed"/static or physics-less transformed ancestor is fine.',
|
|
16
|
-
),
|
|
17
|
-
mass: z
|
|
18
|
-
.number()
|
|
19
|
-
.optional()
|
|
20
|
-
.describe('Explicit mass override. If omitted, computed from collider density and volume'),
|
|
21
|
-
collider: SceneColliderSchema.describe(
|
|
22
|
-
'Collider shape attached to this body. T1.1/T1.2: the Rapier body is created at this ' +
|
|
23
|
-
"entity's COMPOSED WORLD transform (parent chain × own local transform) — Rapier has " +
|
|
24
|
-
"no concept of a parent Object3D, so nested entities' transforms are baked in at spawn " +
|
|
25
|
-
'time, not read live thereafter.',
|
|
26
|
-
),
|
|
27
|
-
})
|
|
28
|
-
.describe('Physics rigid body configuration');
|
|
29
|
-
|
|
30
|
-
/** Partial override — uses SceneColliderOverrideSchema for deep partial on collider. */
|
|
31
|
-
export const ScenePhysicsOverrideSchema = z.object({
|
|
32
|
-
bodyType: z
|
|
33
|
-
.enum(['dynamic', 'fixed', 'kinematic'])
|
|
34
|
-
.optional()
|
|
35
|
-
.describe(
|
|
36
|
-
'Physics body type: dynamic (simulated), fixed (immovable), kinematic (script-driven). ' +
|
|
37
|
-
'A body of ANY type nested under a "dynamic"/"kinematic"-bodied ancestor throws a loud ' +
|
|
38
|
-
'load error (T1.1/R1c) — see ScenePhysicsSchema.bodyType.',
|
|
39
|
-
),
|
|
40
|
-
mass: z
|
|
41
|
-
.number()
|
|
42
|
-
.optional()
|
|
43
|
-
.describe('Explicit mass override. If omitted, computed from collider density and volume'),
|
|
44
|
-
collider: SceneColliderOverrideSchema.optional().describe(
|
|
45
|
-
'Partial collider overrides merged with prefab',
|
|
46
|
-
),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
export type ScenePhysics = z.infer<typeof ScenePhysicsSchema>;
|