@vgai/engine 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/adapter/authoring.d.ts +404 -0
- package/dist/adapter/authoring.d.ts.map +1 -0
- package/dist/adapter/authoring.js +22 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts +43 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -0
- package/dist/adapter/colyseus-networking-adapter.js +38 -0
- package/dist/adapter/first-party-systems.d.ts +38 -0
- package/dist/adapter/first-party-systems.d.ts.map +1 -0
- package/dist/adapter/first-party-systems.js +77 -0
- package/dist/adapter/game-adapter.d.ts +133 -0
- package/dist/adapter/game-adapter.d.ts.map +1 -0
- package/dist/adapter/game-adapter.js +10 -0
- package/dist/adapter/host-context.d.ts +73 -0
- package/dist/adapter/host-context.d.ts.map +1 -0
- package/dist/adapter/host-context.js +16 -0
- package/dist/adapter/index.d.ts +22 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +15 -0
- package/dist/adapter/ingest/game-contract.d.ts +53 -0
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -0
- package/dist/adapter/ingest/game-contract.js +30 -0
- package/dist/adapter/ingest/overlay-applier.d.ts +118 -0
- package/dist/adapter/ingest/overlay-applier.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-applier.js +132 -0
- package/dist/adapter/ingest/overlay-apply.d.ts +60 -0
- package/dist/adapter/ingest/overlay-apply.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-apply.js +101 -0
- package/dist/adapter/ingest/overlay-file.d.ts +79 -0
- package/dist/adapter/ingest/overlay-file.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-file.js +66 -0
- package/dist/adapter/ingest/overlay-report.d.ts +113 -0
- package/dist/adapter/ingest/overlay-report.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-report.js +103 -0
- package/dist/adapter/ingest/scene-capture.d.ts +97 -0
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -0
- package/dist/adapter/ingest/scene-capture.js +248 -0
- package/dist/adapter/ingest/upstream-pin.d.ts +47 -0
- package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -0
- package/dist/adapter/ingest/upstream-pin.js +50 -0
- package/dist/adapter/loop-gate-report.d.ts +34 -0
- package/dist/adapter/loop-gate-report.d.ts.map +1 -0
- package/dist/adapter/loop-gate-report.js +37 -0
- package/dist/adapter/rapier-physics-adapter.d.ts +12 -0
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -0
- package/dist/adapter/rapier-physics-adapter.js +46 -0
- package/dist/adapter/system-adapter.d.ts +133 -0
- package/dist/adapter/system-adapter.d.ts.map +1 -0
- package/dist/adapter/system-adapter.js +13 -0
- package/dist/adapter/transform.d.ts +18 -0
- package/dist/adapter/transform.d.ts.map +1 -0
- package/dist/adapter/transform.js +1 -0
- package/dist/adapter/vgai-scene-game-adapter.d.ts +116 -0
- package/dist/adapter/vgai-scene-game-adapter.d.ts.map +1 -0
- package/dist/adapter/vgai-scene-game-adapter.js +714 -0
- package/dist/adapter/world-kind.d.ts +30 -0
- package/dist/adapter/world-kind.d.ts.map +1 -0
- package/dist/adapter/world-kind.js +18 -0
- package/dist/ai/navigation.d.ts +64 -0
- package/dist/ai/navigation.d.ts.map +1 -0
- package/dist/ai/navigation.js +133 -0
- package/dist/animation/anim-graph-types.d.ts +25 -0
- package/dist/animation/anim-graph-types.d.ts.map +1 -0
- package/dist/animation/anim-graph-types.js +1 -0
- package/dist/animation/animation-clock.d.ts +213 -0
- package/dist/animation/animation-clock.d.ts.map +1 -0
- package/dist/animation/animation-clock.js +301 -0
- package/dist/animation/blend-node.d.ts +22 -0
- package/dist/animation/blend-node.d.ts.map +1 -0
- package/dist/animation/blend-node.js +97 -0
- package/dist/animation/camera-ownership.d.ts +250 -0
- package/dist/animation/camera-ownership.d.ts.map +1 -0
- package/dist/animation/camera-ownership.js +169 -0
- package/dist/animation/cinematic-cues.d.ts +200 -0
- package/dist/animation/cinematic-cues.d.ts.map +1 -0
- package/dist/animation/cinematic-cues.js +213 -0
- package/dist/animation/clip-map.d.ts +12 -0
- package/dist/animation/clip-map.d.ts.map +1 -0
- package/dist/animation/clip-map.js +37 -0
- package/dist/animation/gsap-registration.d.ts +119 -0
- package/dist/animation/gsap-registration.d.ts.map +1 -0
- package/dist/animation/gsap-registration.js +92 -0
- package/dist/animation/theatre-clock-binding.d.ts +89 -0
- package/dist/animation/theatre-clock-binding.d.ts.map +1 -0
- package/dist/animation/theatre-clock-binding.js +29 -0
- package/dist/animation/theatre-director.d.ts +242 -0
- package/dist/animation/theatre-director.d.ts.map +1 -0
- package/dist/animation/theatre-director.js +235 -0
- package/dist/animation/theatre-object-binding.d.ts +305 -0
- package/dist/animation/theatre-object-binding.d.ts.map +1 -0
- package/dist/animation/theatre-object-binding.js +351 -0
- package/dist/animation/xstate-animation-binding.d.ts +111 -0
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -0
- package/dist/animation/xstate-animation-binding.js +307 -0
- package/dist/animation/xstate-animation-meta.d.ts +228 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -0
- package/dist/animation/xstate-animation-meta.js +221 -0
- package/dist/assets.d.ts +26 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +55 -0
- package/dist/audio/index.d.ts +4 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/audio/index.js +24 -0
- package/dist/audio/tone-clock-binding.d.ts +75 -0
- package/dist/audio/tone-clock-binding.d.ts.map +1 -0
- package/dist/audio/tone-clock-binding.js +29 -0
- package/dist/audio/tone-context.d.ts +114 -0
- package/dist/audio/tone-context.d.ts.map +1 -0
- package/dist/audio/tone-context.js +39 -0
- package/dist/audio/tone-offline-render.d.ts +90 -0
- package/dist/audio/tone-offline-render.d.ts.map +1 -0
- package/dist/audio/tone-offline-render.js +56 -0
- package/dist/audio/wav-encode.d.ts +38 -0
- package/dist/audio/wav-encode.d.ts.map +1 -0
- package/dist/audio/wav-encode.js +97 -0
- package/dist/core/game-loop.d.ts +21 -0
- package/dist/core/game-loop.d.ts.map +1 -0
- package/dist/core/game-loop.js +130 -0
- package/dist/core/system-runner.d.ts +120 -0
- package/dist/core/system-runner.d.ts.map +1 -0
- package/dist/core/system-runner.js +286 -0
- package/dist/core/types.d.ts +58 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +25 -0
- package/dist/data/data-asset.d.ts +101 -0
- package/dist/data/data-asset.d.ts.map +1 -0
- package/dist/data/data-asset.js +122 -0
- package/dist/data/data-check-core.d.ts +110 -0
- package/dist/data/data-check-core.d.ts.map +1 -0
- package/dist/data/data-check-core.js +178 -0
- package/dist/data/data-ref.d.ts +79 -0
- package/dist/data/data-ref.d.ts.map +1 -0
- package/dist/data/data-ref.js +130 -0
- package/dist/data/vite-plugin-data.d.ts +79 -0
- package/dist/data/vite-plugin-data.d.ts.map +1 -0
- package/dist/data/vite-plugin-data.js +217 -0
- package/dist/dev/console-bridge.d.ts +22 -0
- package/dist/dev/console-bridge.d.ts.map +1 -0
- package/dist/dev/console-bridge.js +71 -0
- package/dist/dev/debug-draw.d.ts +24 -0
- package/dist/dev/debug-draw.d.ts.map +1 -0
- package/dist/dev/debug-draw.js +73 -0
- package/dist/dev/logger.d.ts +30 -0
- package/dist/dev/logger.d.ts.map +1 -0
- package/dist/dev/logger.js +90 -0
- package/dist/ecs/component-manager.d.ts +189 -0
- package/dist/ecs/component-manager.d.ts.map +1 -0
- package/dist/ecs/component-manager.js +676 -0
- package/dist/ecs/game-component.d.ts +97 -0
- package/dist/ecs/game-component.d.ts.map +1 -0
- package/dist/ecs/game-component.js +80 -0
- package/dist/ecs/hmr-swap-report.d.ts +53 -0
- package/dist/ecs/hmr-swap-report.d.ts.map +1 -0
- package/dist/ecs/hmr-swap-report.js +55 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/input/input-manager.d.ts +596 -0
- package/dist/input/input-manager.d.ts.map +1 -0
- package/dist/input/input-manager.js +1564 -0
- package/dist/input/input-types.d.ts +171 -0
- package/dist/input/input-types.d.ts.map +1 -0
- package/dist/input/input-types.js +1 -0
- package/dist/input/prompt-labels.d.ts +21 -0
- package/dist/input/prompt-labels.d.ts.map +1 -0
- package/dist/input/prompt-labels.js +121 -0
- package/dist/input/rebind-controller.d.ts +59 -0
- package/dist/input/rebind-controller.d.ts.map +1 -0
- package/dist/input/rebind-controller.js +85 -0
- package/dist/input/schema.d.ts +16 -0
- package/dist/input/schema.d.ts.map +1 -0
- package/dist/input/schema.js +228 -0
- package/dist/loader.d.ts +43 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +59 -0
- package/dist/manifest/index.d.ts +5 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +8 -0
- package/dist/manifest/load-file.d.ts +4 -0
- package/dist/manifest/load-file.d.ts.map +1 -0
- package/dist/manifest/load-file.js +14 -0
- package/dist/manifest/load.d.ts +117 -0
- package/dist/manifest/load.d.ts.map +1 -0
- package/dist/manifest/load.js +255 -0
- package/dist/manifest/schema.d.ts +173 -0
- package/dist/manifest/schema.d.ts.map +1 -0
- package/dist/manifest/schema.js +302 -0
- package/dist/physics/collision-system.d.ts +41 -0
- package/dist/physics/collision-system.d.ts.map +1 -0
- package/dist/physics/collision-system.js +64 -0
- package/dist/physics/physics-registry.d.ts +45 -0
- package/dist/physics/physics-registry.d.ts.map +1 -0
- package/dist/physics/physics-registry.js +65 -0
- package/dist/physics/transform-writer.d.ts +16 -0
- package/dist/physics/transform-writer.d.ts.map +1 -0
- package/dist/physics/transform-writer.js +38 -0
- package/dist/physics/trigger-dispatch.d.ts +24 -0
- package/dist/physics/trigger-dispatch.d.ts.map +1 -0
- package/dist/physics/trigger-dispatch.js +80 -0
- package/dist/react/game-state.d.ts +105 -0
- package/dist/react/game-state.d.ts.map +1 -0
- package/dist/react/game-state.js +144 -0
- package/dist/react/use-data.d.ts +60 -0
- package/dist/react/use-data.d.ts.map +1 -0
- package/dist/react/use-data.js +96 -0
- package/dist/react/use-selection.d.ts +75 -0
- package/dist/react/use-selection.d.ts.map +1 -0
- package/dist/react/use-selection.js +90 -0
- package/dist/render/auto-batcher.d.ts +34 -0
- package/dist/render/auto-batcher.d.ts.map +1 -0
- package/dist/render/auto-batcher.js +140 -0
- package/dist/render/render-batch-system.d.ts +33 -0
- package/dist/render/render-batch-system.d.ts.map +1 -0
- package/dist/render/render-batch-system.js +230 -0
- package/dist/render/render-features.d.ts +47 -0
- package/dist/render/render-features.d.ts.map +1 -0
- package/dist/render/render-features.js +105 -0
- package/dist/render/render-settings.d.ts +36 -0
- package/dist/render/render-settings.d.ts.map +1 -0
- package/dist/render/render-settings.js +51 -0
- package/dist/runtime/create-runtime.d.ts +280 -0
- package/dist/runtime/create-runtime.d.ts.map +1 -0
- package/dist/runtime/create-runtime.js +795 -0
- package/dist/runtime/frame-selector-cache.d.ts +41 -0
- package/dist/runtime/frame-selector-cache.d.ts.map +1 -0
- package/dist/runtime/frame-selector-cache.js +65 -0
- package/dist/runtime/game.d.ts +433 -0
- package/dist/runtime/game.d.ts.map +1 -0
- package/dist/runtime/game.js +569 -0
- package/dist/runtime/input-router.d.ts +78 -0
- package/dist/runtime/input-router.d.ts.map +1 -0
- package/dist/runtime/input-router.js +171 -0
- package/dist/runtime/mount-game.d.ts +129 -0
- package/dist/runtime/mount-game.d.ts.map +1 -0
- package/dist/runtime/mount-game.js +152 -0
- package/dist/runtime/mount-manifest.d.ts +119 -0
- package/dist/runtime/mount-manifest.d.ts.map +1 -0
- package/dist/runtime/mount-manifest.js +204 -0
- package/dist/runtime/render-audio-control.d.ts +110 -0
- package/dist/runtime/render-audio-control.d.ts.map +1 -0
- package/dist/runtime/render-audio-control.js +111 -0
- package/dist/runtime/render-control.d.ts +291 -0
- package/dist/runtime/render-control.d.ts.map +1 -0
- package/dist/runtime/render-control.js +264 -0
- package/dist/runtime/render-seed.d.ts +57 -0
- package/dist/runtime/render-seed.d.ts.map +1 -0
- package/dist/runtime/render-seed.js +74 -0
- package/dist/runtime/scene-ui-bridge.d.ts +69 -0
- package/dist/runtime/scene-ui-bridge.d.ts.map +1 -0
- package/dist/runtime/scene-ui-bridge.js +23 -0
- package/dist/runtime/scene-ui-data.d.ts +13 -0
- package/dist/runtime/scene-ui-data.d.ts.map +1 -0
- package/dist/runtime/scene-ui-data.js +92 -0
- package/dist/runtime/state-bridge.d.ts +47 -0
- package/dist/runtime/state-bridge.d.ts.map +1 -0
- package/dist/runtime/state-bridge.js +53 -0
- package/dist/runtime/types.d.ts +179 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +1 -0
- package/dist/scene/asset-loaders.d.ts +79 -0
- package/dist/scene/asset-loaders.d.ts.map +1 -0
- package/dist/scene/asset-loaders.js +141 -0
- package/dist/scene/asset-paths.d.ts +11 -0
- package/dist/scene/asset-paths.d.ts.map +1 -0
- package/dist/scene/asset-paths.js +108 -0
- package/dist/scene/asset-ref-check.d.ts +88 -0
- package/dist/scene/asset-ref-check.d.ts.map +1 -0
- package/dist/scene/asset-ref-check.js +214 -0
- package/dist/scene/asset-registry.d.ts +30 -0
- package/dist/scene/asset-registry.d.ts.map +1 -0
- package/dist/scene/asset-registry.js +50 -0
- package/dist/scene/collider-dimensions.d.ts +63 -0
- package/dist/scene/collider-dimensions.d.ts.map +1 -0
- package/dist/scene/collider-dimensions.js +98 -0
- package/dist/scene/component-registry.d.ts +17 -0
- package/dist/scene/component-registry.d.ts.map +1 -0
- package/dist/scene/component-registry.js +28 -0
- package/dist/scene/defaults.d.ts +252 -0
- package/dist/scene/defaults.d.ts.map +1 -0
- package/dist/scene/defaults.js +151 -0
- package/dist/scene/geometries/index.d.ts +8 -0
- package/dist/scene/geometries/index.d.ts.map +1 -0
- package/dist/scene/geometries/index.js +7 -0
- package/dist/scene/geometries/terrain.d.ts +2 -0
- package/dist/scene/geometries/terrain.d.ts.map +1 -0
- package/dist/scene/geometries/terrain.js +37 -0
- package/dist/scene/geometry-registry.d.ts +24 -0
- package/dist/scene/geometry-registry.d.ts.map +1 -0
- package/dist/scene/geometry-registry.js +20 -0
- package/dist/scene/instance-registry.d.ts +36 -0
- package/dist/scene/instance-registry.d.ts.map +1 -0
- package/dist/scene/instance-registry.js +55 -0
- package/dist/scene/instancers/grid.d.ts +2 -0
- package/dist/scene/instancers/grid.d.ts.map +1 -0
- package/dist/scene/instancers/grid.js +36 -0
- package/dist/scene/instancers/index.d.ts +8 -0
- package/dist/scene/instancers/index.d.ts.map +1 -0
- package/dist/scene/instancers/index.js +7 -0
- package/dist/scene/light-camera-factory.d.ts +23 -0
- package/dist/scene/light-camera-factory.d.ts.map +1 -0
- package/dist/scene/light-camera-factory.js +68 -0
- package/dist/scene/material-factory.d.ts +17 -0
- package/dist/scene/material-factory.d.ts.map +1 -0
- package/dist/scene/material-factory.js +197 -0
- package/dist/scene/material-registry.d.ts +40 -0
- package/dist/scene/material-registry.d.ts.map +1 -0
- package/dist/scene/material-registry.js +44 -0
- package/dist/scene/materials/index.d.ts +8 -0
- package/dist/scene/materials/index.d.ts.map +1 -0
- package/dist/scene/materials/index.js +7 -0
- package/dist/scene/materials/water.d.ts +2 -0
- package/dist/scene/materials/water.d.ts.map +1 -0
- package/dist/scene/materials/water.js +54 -0
- package/dist/scene/parse.d.ts +23 -0
- package/dist/scene/parse.d.ts.map +1 -0
- package/dist/scene/parse.js +179 -0
- package/dist/scene/particles-factory.d.ts +33 -0
- package/dist/scene/particles-factory.d.ts.map +1 -0
- package/dist/scene/particles-factory.js +282 -0
- package/dist/scene/scene-apply.d.ts +109 -0
- package/dist/scene/scene-apply.d.ts.map +1 -0
- package/dist/scene/scene-apply.js +326 -0
- package/dist/scene/scene-diff-schema.d.ts +285 -0
- package/dist/scene/scene-diff-schema.d.ts.map +1 -0
- package/dist/scene/scene-diff-schema.js +92 -0
- package/dist/scene/scene-diff-types.d.ts +40 -0
- package/dist/scene/scene-diff-types.d.ts.map +1 -0
- package/dist/scene/scene-diff-types.js +14 -0
- package/dist/scene/scene-loader.d.ts +177 -0
- package/dist/scene/scene-loader.d.ts.map +1 -0
- package/dist/scene/scene-loader.js +1213 -0
- package/dist/scene/scene-query.d.ts +11 -0
- package/dist/scene/scene-query.d.ts.map +1 -0
- package/dist/scene/scene-query.js +63 -0
- package/dist/scene/scene-types.d.ts +10 -0
- package/dist/scene/scene-types.d.ts.map +1 -0
- package/dist/scene/scene-types.js +8 -0
- package/dist/scene/scene-version.d.ts +26 -0
- package/dist/scene/scene-version.d.ts.map +1 -0
- package/dist/scene/scene-version.js +34 -0
- package/dist/scene/schema/animation.d.ts +8 -0
- package/dist/scene/schema/animation.d.ts.map +1 -0
- package/dist/scene/schema/animation.js +42 -0
- package/dist/scene/schema/audio.d.ts +13 -0
- package/dist/scene/schema/audio.d.ts.map +1 -0
- package/dist/scene/schema/audio.js +22 -0
- package/dist/scene/schema/camera.d.ts +28 -0
- package/dist/scene/schema/camera.d.ts.map +1 -0
- package/dist/scene/schema/camera.js +17 -0
- package/dist/scene/schema/collider.d.ts +40 -0
- package/dist/scene/schema/collider.d.ts.map +1 -0
- package/dist/scene/schema/collider.js +55 -0
- package/dist/scene/schema/entity-ref.d.ts +31 -0
- package/dist/scene/schema/entity-ref.d.ts.map +1 -0
- package/dist/scene/schema/entity-ref.js +61 -0
- package/dist/scene/schema/entity.d.ts +1036 -0
- package/dist/scene/schema/entity.d.ts.map +1 -0
- package/dist/scene/schema/entity.js +153 -0
- package/dist/scene/schema/environment.d.ts +425 -0
- package/dist/scene/schema/environment.d.ts.map +1 -0
- package/dist/scene/schema/environment.js +357 -0
- package/dist/scene/schema/index.d.ts +39 -0
- package/dist/scene/schema/index.d.ts.map +1 -0
- package/dist/scene/schema/index.js +20 -0
- package/dist/scene/schema/instances.d.ts +14 -0
- package/dist/scene/schema/instances.d.ts.map +1 -0
- package/dist/scene/schema/instances.js +28 -0
- package/dist/scene/schema/joint.d.ts +23 -0
- package/dist/scene/schema/joint.d.ts.map +1 -0
- package/dist/scene/schema/joint.js +23 -0
- package/dist/scene/schema/light.d.ts +38 -0
- package/dist/scene/schema/light.d.ts.map +1 -0
- package/dist/scene/schema/light.js +26 -0
- package/dist/scene/schema/material.d.ts +199 -0
- package/dist/scene/schema/material.d.ts.map +1 -0
- package/dist/scene/schema/material.js +99 -0
- package/dist/scene/schema/mesh.d.ts +48 -0
- package/dist/scene/schema/mesh.d.ts.map +1 -0
- package/dist/scene/schema/mesh.js +82 -0
- package/dist/scene/schema/particles.d.ts +2178 -0
- package/dist/scene/schema/particles.d.ts.map +1 -0
- package/dist/scene/schema/particles.js +321 -0
- package/dist/scene/schema/physics.d.ts +56 -0
- package/dist/scene/schema/physics.d.ts.map +1 -0
- package/dist/scene/schema/physics.js +37 -0
- package/dist/scene/schema/scene-file.d.ts +304 -0
- package/dist/scene/schema/scene-file.d.ts.map +1 -0
- package/dist/scene/schema/scene-file.js +262 -0
- package/dist/scene/schema/shadow.d.ts +28 -0
- package/dist/scene/schema/shadow.d.ts.map +1 -0
- package/dist/scene/schema/shadow.js +20 -0
- package/dist/scene/schema/spline.d.ts +25 -0
- package/dist/scene/schema/spline.d.ts.map +1 -0
- package/dist/scene/schema/spline.js +15 -0
- package/dist/scene/schema/tuples.d.ts +12 -0
- package/dist/scene/schema/tuples.d.ts.map +1 -0
- package/dist/scene/schema/tuples.js +14 -0
- package/dist/scene/schema/ui.d.ts +880 -0
- package/dist/scene/schema/ui.d.ts.map +1 -0
- package/dist/scene/schema/ui.js +506 -0
- package/dist/scene/user-data.d.ts +171 -0
- package/dist/scene/user-data.d.ts.map +1 -0
- package/dist/scene/user-data.js +137 -0
- package/dist/setup/setup-audio.d.ts +20 -0
- package/dist/setup/setup-audio.d.ts.map +1 -0
- package/dist/setup/setup-audio.js +41 -0
- package/dist/setup/setup-particles.d.ts +17 -0
- package/dist/setup/setup-particles.d.ts.map +1 -0
- package/dist/setup/setup-particles.js +16 -0
- package/dist/setup/setup-physics.d.ts +25 -0
- package/dist/setup/setup-physics.d.ts.map +1 -0
- package/dist/setup/setup-physics.js +51 -0
- package/dist/setup/setup-renderer.d.ts +69 -0
- package/dist/setup/setup-renderer.d.ts.map +1 -0
- package/dist/setup/setup-renderer.js +363 -0
- package/dist/tools/define-tool.d.ts +105 -0
- package/dist/tools/define-tool.d.ts.map +1 -0
- package/dist/tools/define-tool.js +82 -0
- package/dist/world2d/authoring-2d.d.ts +76 -0
- package/dist/world2d/authoring-2d.d.ts.map +1 -0
- package/dist/world2d/authoring-2d.js +176 -0
- package/dist/world2d/capture-to-scene2d.d.ts +5 -0
- package/dist/world2d/capture-to-scene2d.d.ts.map +1 -0
- package/dist/world2d/capture-to-scene2d.js +49 -0
- package/dist/world2d/collision-2d.d.ts +52 -0
- package/dist/world2d/collision-2d.d.ts.map +1 -0
- package/dist/world2d/collision-2d.js +70 -0
- package/dist/world2d/components-2d.d.ts +47 -0
- package/dist/world2d/components-2d.d.ts.map +1 -0
- package/dist/world2d/components-2d.js +77 -0
- package/dist/world2d/index.d.ts +34 -0
- package/dist/world2d/index.d.ts.map +1 -0
- package/dist/world2d/index.js +32 -0
- package/dist/world2d/ingest-iframe-2d.d.ts +82 -0
- package/dist/world2d/ingest-iframe-2d.d.ts.map +1 -0
- package/dist/world2d/ingest-iframe-2d.js +166 -0
- package/dist/world2d/ingest2d.d.ts +97 -0
- package/dist/world2d/ingest2d.d.ts.map +1 -0
- package/dist/world2d/ingest2d.js +40 -0
- package/dist/world2d/physics2d-registry.d.ts +24 -0
- package/dist/world2d/physics2d-registry.d.ts.map +1 -0
- package/dist/world2d/physics2d-registry.js +38 -0
- package/dist/world2d/pixi-game-adapter.d.ts +107 -0
- package/dist/world2d/pixi-game-adapter.d.ts.map +1 -0
- package/dist/world2d/pixi-game-adapter.js +211 -0
- package/dist/world2d/pixi-surface.d.ts +39 -0
- package/dist/world2d/pixi-surface.d.ts.map +1 -0
- package/dist/world2d/pixi-surface.js +47 -0
- package/dist/world2d/scene-capture-2d.d.ts +43 -0
- package/dist/world2d/scene-capture-2d.d.ts.map +1 -0
- package/dist/world2d/scene-capture-2d.js +92 -0
- package/dist/world2d/scene2d-loader.d.ts +27 -0
- package/dist/world2d/scene2d-loader.d.ts.map +1 -0
- package/dist/world2d/scene2d-loader.js +274 -0
- package/dist/world2d/schema/entity2d.d.ts +102 -0
- package/dist/world2d/schema/entity2d.d.ts.map +1 -0
- package/dist/world2d/schema/entity2d.js +119 -0
- package/dist/world2d/schema/physics2d.d.ts +46 -0
- package/dist/world2d/schema/physics2d.d.ts.map +1 -0
- package/dist/world2d/schema/physics2d.js +45 -0
- package/dist/world2d/schema/sprite.d.ts +42 -0
- package/dist/world2d/schema/sprite.d.ts.map +1 -0
- package/dist/world2d/schema/sprite.js +63 -0
- package/dist/world2d/schema/tilemap.d.ts +15 -0
- package/dist/world2d/schema/tilemap.d.ts.map +1 -0
- package/dist/world2d/schema/tilemap.js +19 -0
- package/dist/world2d/schema/tuples2d.d.ts +20 -0
- package/dist/world2d/schema/tuples2d.d.ts.map +1 -0
- package/dist/world2d/schema/tuples2d.js +19 -0
- package/dist/world2d/system-adapters-2d.d.ts +16 -0
- package/dist/world2d/system-adapters-2d.d.ts.map +1 -0
- package/dist/world2d/system-adapters-2d.js +37 -0
- package/dist/world2d/transform-writer-2d.d.ts +16 -0
- package/dist/world2d/transform-writer-2d.d.ts.map +1 -0
- package/dist/world2d/transform-writer-2d.js +22 -0
- package/dist/world2d/types.d.ts +53 -0
- package/dist/world2d/types.d.ts.map +1 -0
- package/dist/world2d/types.js +1 -0
- package/package.json +18 -4
- package/src/adapter/authoring.ts +10 -0
- package/src/adapter/first-party-systems.ts +23 -34
- package/src/adapter/index.ts +1 -3
- package/src/adapter/system-adapter.ts +15 -22
- package/src/adapter/vgai-scene-game-adapter.ts +5 -14
- package/src/animation/anim-graph-types.ts +12 -43
- package/src/animation/animation-clock.ts +479 -0
- package/src/animation/camera-ownership.ts +467 -0
- package/src/animation/cinematic-cues.ts +451 -0
- package/src/animation/clip-map.ts +41 -0
- package/src/animation/gsap-registration.ts +184 -0
- package/src/animation/theatre-clock-binding.ts +111 -0
- package/src/animation/theatre-director.ts +347 -0
- package/src/animation/theatre-object-binding.ts +661 -0
- package/src/animation/xstate-animation-binding.ts +398 -0
- package/src/animation/xstate-animation-meta.ts +282 -0
- package/src/audio/index.ts +39 -7
- package/src/audio/tone-clock-binding.ts +98 -0
- package/src/audio/tone-context.ts +129 -0
- package/src/audio/tone-offline-render.ts +167 -0
- package/src/audio/wav-encode.ts +119 -0
- package/src/core/game-loop.ts +17 -0
- package/src/core/types.ts +13 -0
- package/src/data/data-asset.ts +167 -0
- package/src/data/data-check-core.ts +242 -0
- package/src/data/data-ref.ts +145 -0
- package/src/data/vite-plugin-data.ts +290 -0
- package/src/index.ts +48 -0
- package/src/input/input-manager.ts +1206 -32
- package/src/input/input-types.ts +155 -3
- package/src/input/prompt-labels.ts +122 -0
- package/src/input/rebind-controller.ts +105 -0
- package/src/input/schema.ts +206 -52
- package/src/react/use-data.ts +114 -0
- package/src/react/use-selection.tsx +135 -0
- package/src/runtime/create-runtime.ts +30 -9
- package/src/runtime/render-audio-control.ts +168 -0
- package/src/runtime/render-control.ts +522 -0
- package/src/runtime/render-seed.ts +79 -0
- package/src/runtime/types.ts +0 -5
- package/src/scene/asset-loaders.ts +10 -36
- package/src/scene/asset-paths.ts +0 -2
- package/src/scene/asset-ref-check.ts +248 -0
- package/src/scene/parse.ts +133 -0
- package/src/scene/scene-loader.ts +81 -96
- package/src/scene/schema/animation.ts +30 -79
- package/src/scene/schema/entity.ts +20 -0
- package/src/scene/schema/index.ts +2 -12
- package/src/scene/user-data.ts +8 -9
- package/src/setup/setup-renderer.ts +9 -2
- package/src/tools/define-tool.ts +152 -0
- package/src/animation/anim-graph.ts +0 -406
- package/src/animation/anim-system.ts +0 -28
- package/src/animation/property-track.ts +0 -178
- package/src/animation/schema.ts +0 -204
- package/src/audio/ambient.ts +0 -300
- package/src/audio/impacts.ts +0 -212
- package/src/audio/movement.ts +0 -140
- package/src/audio/musical.ts +0 -200
- package/src/audio/ui-sounds.ts +0 -171
- package/src/audio/vehicle.ts +0 -235
- package/src/audio/weapons.ts +0 -152
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import type { Actor, AnyActor, AnyStateMachine } from 'xstate';
|
|
3
|
+
import type { BlendTreeDef } from './anim-graph-types';
|
|
4
|
+
import { evaluateBlendTree } from './blend-node';
|
|
5
|
+
import {
|
|
6
|
+
type AnimationMetaStateNodeLike,
|
|
7
|
+
clipNamesOf,
|
|
8
|
+
collectMachineAnimationMeta,
|
|
9
|
+
isBlendTreeAnimationMeta,
|
|
10
|
+
type MachineAnimationMetaEntry,
|
|
11
|
+
type StateAnimationMeta,
|
|
12
|
+
} from './xstate-animation-meta';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* E2 — bind a native XState actor to a native `THREE.AnimationMixer` (spec
|
|
16
|
+
* §11 E2, §5.6, §3.3).
|
|
17
|
+
*
|
|
18
|
+
* A native XState actor drives native `THREE.AnimationAction`s: entering a
|
|
19
|
+
* state activates its `meta.animation` clip(s); leaving it deactivates them.
|
|
20
|
+
* There is no VGAI-specific trigger vocabulary — callers drive the machine
|
|
21
|
+
* with ordinary `actor.send(event)` (or the actor is externally owned and
|
|
22
|
+
* this binding merely observes it); XState alone decides which state is
|
|
23
|
+
* active.
|
|
24
|
+
*
|
|
25
|
+
* ## How enter/exit crossfades use NATIVE Three primitives (not a
|
|
26
|
+
* hand-rolled weight scheduler)
|
|
27
|
+
*
|
|
28
|
+
* The former `AnimGraph` runtime (removed by E5) pre-played every action at
|
|
29
|
+
* weight 0 and re-set every action's weight explicitly every frame,
|
|
30
|
+
* BYPASSING `AnimationAction.fadeIn`/`fadeOut`/`crossFadeTo` (to dodge
|
|
31
|
+
* multi-layer weight-summing bugs it hit historically).
|
|
32
|
+
* E2 deliberately does the opposite, per the spec AC ("use
|
|
33
|
+
* AnimationAction.crossFadeTo / fadeIn / fadeOut per the metadata"): the
|
|
34
|
+
* ENTER/EXIT edge of an XState state transition is represented by one real
|
|
35
|
+
* Three fade call, and `mixer.update(dt)` — the same call that advances clip
|
|
36
|
+
* time — is what advances the fade too. No parallel elapsed-time clock
|
|
37
|
+
* drives "is the fade done" for a plain (non-blend-tree) state; Three's own
|
|
38
|
+
* scheduler is authoritative end to end.
|
|
39
|
+
*
|
|
40
|
+
* A **blend-tree** state is, at the instant of entry, represented by ONE
|
|
41
|
+
* clip — its heaviest-weighted child at that instant (the same "representative"
|
|
42
|
+
* duration/action heuristic the removed `AnimGraph.LayerRuntime` used) — which
|
|
43
|
+
* receives the native crossFadeTo/fadeIn. Sibling
|
|
44
|
+
* children stay silent (weight 0) until the crossfade window elapses, at
|
|
45
|
+
* which point the FULL live blend-tree distribution (`evaluateBlendTree`,
|
|
46
|
+
* reused verbatim from `blend-node.ts` — no reimplementation) takes over via
|
|
47
|
+
* `setEffectiveWeight` every tick, recomputed from the actor's live
|
|
48
|
+
* context/selector value. This is a documented simplification (Three's
|
|
49
|
+
* `crossFadeTo` is strictly 1-to-1; there is no native N-ary blend-tree
|
|
50
|
+
* crossfade), the same spirit as `blend-node.ts`'s own documented 2D
|
|
51
|
+
* "smooth approximation, not Delaunay/barycentric" caveat. The crossfade
|
|
52
|
+
* window itself is tracked as a small elapsed-seconds counter, decremented
|
|
53
|
+
* by the SAME `dt` passed to `mixer.update(dt)` — it never drifts from
|
|
54
|
+
* Three's own fade schedule because both are driven by the identical `dt`
|
|
55
|
+
* stream, so it is bookkeeping for "when to hand off from native fade to
|
|
56
|
+
* live blend", not a second competing scheduler.
|
|
57
|
+
*
|
|
58
|
+
* ## Mixer advancement uses the canonical engine ANIMATION phase
|
|
59
|
+
*
|
|
60
|
+
* `tick(dt)` has exactly the engine's `SystemFn` shape (`core/types.ts`:
|
|
61
|
+
* `(dt: number) => void`) — a caller registers this binding on the engine's
|
|
62
|
+
* one real fixed-step scheduler at the ANIMATION phase, exactly as any other
|
|
63
|
+
* per-frame system: `ctx.systems.add(SystemPhase.ANIMATION, binding.tick)`
|
|
64
|
+
* (or, when the binding is owned by a `GameComponent` that also owns the
|
|
65
|
+
* actor — the §5.6 pattern the examples use — the component simply calls
|
|
66
|
+
* `binding.tick(dt)` from its own `update(dt)`). Either way mixer advancement
|
|
67
|
+
* rides that one scheduler; no second scheduler is invented here. (This is
|
|
68
|
+
* the same ANIMATION-phase slot the removed `AnimGraph` runtime's global
|
|
69
|
+
* `animationSystem` occupied before E5 — see
|
|
70
|
+
* docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §3.3/§11 E5. The D1
|
|
71
|
+
* `AnimationClock` — `animation-clock.ts` — is a distinct, sibling concern: a
|
|
72
|
+
* per-cinematic/Theatre-timeline seek clock for scrubbing/export, not wired
|
|
73
|
+
* into ordinary gameplay ticking today.)
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/** Thrown for a binding-time problem: a missing clip, or a target-less bind. */
|
|
77
|
+
export class AnimationBindingError extends Error {
|
|
78
|
+
constructor(
|
|
79
|
+
message: string,
|
|
80
|
+
readonly stateId?: string,
|
|
81
|
+
) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.name = 'AnimationBindingError';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface XStateAnimationBindingOptions {
|
|
88
|
+
/**
|
|
89
|
+
* Reads the blend-tree parameter map from the actor's live context.
|
|
90
|
+
* Defaults to treating `context` itself as `Record<string, number | boolean>`
|
|
91
|
+
* (matches the §5.6 example: `context: { speed: number; grounded: boolean }`
|
|
92
|
+
* fed straight into a blend tree keyed by `parameter: 'speed'`). Override
|
|
93
|
+
* when a machine's blend-tree parameters need deriving from context via a
|
|
94
|
+
* selector rather than reading top-level fields directly.
|
|
95
|
+
*/
|
|
96
|
+
selectParameters?: (context: unknown) => Record<string, number | boolean>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface XStateAnimationBinding {
|
|
100
|
+
/**
|
|
101
|
+
* Advance the bound mixer (and any live blend-tree weights) by `dt`
|
|
102
|
+
* seconds. Register on the engine's `'animation'` phase — see the module
|
|
103
|
+
* doc above. A no-op after `dispose()`.
|
|
104
|
+
*/
|
|
105
|
+
tick: (dt: number) => void;
|
|
106
|
+
/** The bound actor, for convenience (`binding.actor.send({...})`). */
|
|
107
|
+
readonly actor: AnyActor;
|
|
108
|
+
/**
|
|
109
|
+
* Stops observing the actor (does NOT stop the actor itself — the actor's
|
|
110
|
+
* lifecycle is the caller's, this binding only ever subscribed to it) and
|
|
111
|
+
* uncaches every `AnimationAction` this binding created from the mixer, so
|
|
112
|
+
* a dispose+rebind cycle (e.g. hot-reload, despawn/respawn) does not leak
|
|
113
|
+
* mixer-cached actions. Idempotent; `tick()` is a no-op after this.
|
|
114
|
+
*/
|
|
115
|
+
dispose: () => void;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface WeightedClip {
|
|
119
|
+
clip: string;
|
|
120
|
+
weight: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface ActiveState {
|
|
124
|
+
stateId: string;
|
|
125
|
+
meta: StateAnimationMeta;
|
|
126
|
+
/** The action(s) this state's `meta` refers to, one weighted entry per clip. */
|
|
127
|
+
natural: WeightedClip[];
|
|
128
|
+
/** The single clip carrying the native crossFadeTo/fadeIn schedule. */
|
|
129
|
+
dominantClip: string;
|
|
130
|
+
/** Seconds remaining in the enter crossfade; 0 once settled (or if duration was 0). */
|
|
131
|
+
crossfadeRemaining: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function dominantOf(weights: WeightedClip[]): WeightedClip {
|
|
135
|
+
return weights.reduce((a, b) => (b.weight > a.weight ? b : a), weights[0]!);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function naturalWeightsOf(meta: StateAnimationMeta): WeightedClip[] {
|
|
139
|
+
if (isBlendTreeAnimationMeta(meta)) {
|
|
140
|
+
// AnimationBlendTree (xstate-animation-meta.ts) is structurally
|
|
141
|
+
// identical to BlendTreeDef (anim-graph-types.ts) by design — reuse
|
|
142
|
+
// evaluateBlendTree verbatim, no conversion/reimplementation.
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
return [{ clip: meta.clip, weight: 1 }];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Bind an XState actor to a `THREE.AnimationMixer`.
|
|
150
|
+
*
|
|
151
|
+
* Validates EVERY state's `meta.animation` (via `collectMachineAnimationMeta`,
|
|
152
|
+
* E1) and resolves every referenced clip name against `clips` up front, at
|
|
153
|
+
* bind time — before the actor enters any of those states — so an agent
|
|
154
|
+
* authoring a machine with a typo'd clip name gets an actionable error
|
|
155
|
+
* immediately, not only once gameplay happens to reach that state.
|
|
156
|
+
*
|
|
157
|
+
* @throws {AnimationBindingError} if a state's meta references a clip name
|
|
158
|
+
* not present in `clips`, naming both the state and the clip.
|
|
159
|
+
*/
|
|
160
|
+
export function bindXStateAnimation(
|
|
161
|
+
actor: Actor<AnyStateMachine>,
|
|
162
|
+
mixer: THREE.AnimationMixer,
|
|
163
|
+
clips: Map<string, THREE.AnimationClip>,
|
|
164
|
+
options: XStateAnimationBindingOptions = {},
|
|
165
|
+
): XStateAnimationBinding {
|
|
166
|
+
const root = actor.logic.root as unknown as AnimationMetaStateNodeLike;
|
|
167
|
+
const entries: MachineAnimationMetaEntry[] = collectMachineAnimationMeta(root);
|
|
168
|
+
const metaByStateId = new Map(entries.map((e) => [e.stateId, e.meta]));
|
|
169
|
+
|
|
170
|
+
// Resolve every referenced clip up front — actionable, named errors before
|
|
171
|
+
// any state is ever entered.
|
|
172
|
+
for (const entry of entries) {
|
|
173
|
+
for (const clipName of clipNamesOf(entry.meta)) {
|
|
174
|
+
if (!clips.has(clipName)) {
|
|
175
|
+
throw new AnimationBindingError(
|
|
176
|
+
`[bindXStateAnimation] state "${entry.stateId}" references clip "${clipName}", which is ` +
|
|
177
|
+
`not present in the supplied clips map. Known clips: ${[...clips.keys()].join(', ') || '(none)'}`,
|
|
178
|
+
entry.stateId,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const actionCache = new Map<string, THREE.AnimationAction>();
|
|
185
|
+
function ensureAction(clipName: string, loop: boolean, speed: number): THREE.AnimationAction {
|
|
186
|
+
let action = actionCache.get(clipName);
|
|
187
|
+
if (!action) {
|
|
188
|
+
const clip = clips.get(clipName);
|
|
189
|
+
if (!clip) {
|
|
190
|
+
// Unreachable given the up-front resolution above; kept as a named,
|
|
191
|
+
// defensive error rather than a silent `mixer.clipAction(undefined)`.
|
|
192
|
+
throw new AnimationBindingError(`[bindXStateAnimation] clip "${clipName}" not found`);
|
|
193
|
+
}
|
|
194
|
+
action = mixer.clipAction(clip);
|
|
195
|
+
action.setEffectiveWeight(0);
|
|
196
|
+
action.play(); // must be "active" for mixer.update() to tick it at all (Three only advances active actions)
|
|
197
|
+
actionCache.set(clipName, action);
|
|
198
|
+
}
|
|
199
|
+
action.setLoop(loop ? THREE.LoopRepeat : THREE.LoopOnce, Infinity);
|
|
200
|
+
action.clampWhenFinished = !loop;
|
|
201
|
+
action.timeScale = speed;
|
|
202
|
+
return action;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function readParameters(context: unknown): Map<string, number | boolean> {
|
|
206
|
+
const raw = options.selectParameters
|
|
207
|
+
? options.selectParameters(context)
|
|
208
|
+
: (context as Record<string, number | boolean> | null | undefined);
|
|
209
|
+
return new Map(Object.entries(raw ?? {}));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let active: ActiveState | null = null;
|
|
213
|
+
let disposed = false;
|
|
214
|
+
|
|
215
|
+
// Known limitation (shared with plain Three.js `crossFadeTo` usage generally):
|
|
216
|
+
// `AnimationAction.fadeOut(duration)` always schedules a fixed 1→0 ramp, not
|
|
217
|
+
// "current weight → 0" — interrupting an action that is itself still
|
|
218
|
+
// mid-fade-in (e.g. a state transitions again before its own crossfade
|
|
219
|
+
// settles) can produce a brief visible pop back toward full weight before
|
|
220
|
+
// fading out. This is a native Three scheduling property, not something
|
|
221
|
+
// this binding's bookkeeping can smooth over without re-implementing
|
|
222
|
+
// Three's own weight scheduler; same accepted-approximation spirit as
|
|
223
|
+
// `blend-node.ts`'s documented 2D IDW caveat.
|
|
224
|
+
function silence(weights: WeightedClip[], except: string | undefined, duration: number): void {
|
|
225
|
+
for (const { clip: clipName } of weights) {
|
|
226
|
+
if (clipName === except) continue;
|
|
227
|
+
const action = actionCache.get(clipName);
|
|
228
|
+
if (!action) continue;
|
|
229
|
+
if (duration > 0) action.fadeOut(duration);
|
|
230
|
+
else action.setEffectiveWeight(0);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Ensure every clip in `weights` has an action; reset the dominant one to weight 1 (base
|
|
235
|
+
* multiplier for the fade schedule about to be applied) and every sibling to silent (0). */
|
|
236
|
+
function prepareStateActions(
|
|
237
|
+
weights: WeightedClip[],
|
|
238
|
+
dominantClip: string,
|
|
239
|
+
loop: boolean,
|
|
240
|
+
speed: number,
|
|
241
|
+
): THREE.AnimationAction {
|
|
242
|
+
for (const { clip: clipName } of weights) ensureAction(clipName, loop, speed);
|
|
243
|
+
|
|
244
|
+
const dominantAction = actionCache.get(dominantClip)!;
|
|
245
|
+
dominantAction.reset();
|
|
246
|
+
// `setEffectiveWeight(1)` (not just `.reset()`) is required here: reset() does not touch the
|
|
247
|
+
// base `.weight` multiplier a scheduled fade scales against, and this action may previously
|
|
248
|
+
// have been a silenced blend-tree sibling left at `.weight === 0` — left alone, that would mute
|
|
249
|
+
// the crossFadeTo/fadeIn schedule applied below to a permanent 0.
|
|
250
|
+
dominantAction.setEffectiveWeight(1);
|
|
251
|
+
|
|
252
|
+
// Non-dominant children of THIS state start silent; they pick up their
|
|
253
|
+
// live share once the crossfade window elapses (see `tick`).
|
|
254
|
+
for (const { clip: clipName } of weights) {
|
|
255
|
+
if (clipName === dominantClip) continue;
|
|
256
|
+
const action = actionCache.get(clipName)!;
|
|
257
|
+
action.reset();
|
|
258
|
+
action.setEffectiveWeight(0);
|
|
259
|
+
}
|
|
260
|
+
return dominantAction;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Apply the enter-transition schedule: native crossFadeTo (from a previous dominant), a plain
|
|
264
|
+
* fadeIn (first-ever activation with a duration), or an immediate hard cut (duration 0). */
|
|
265
|
+
function activateTransition(
|
|
266
|
+
dominantAction: THREE.AnimationAction,
|
|
267
|
+
outgoing: ActiveState | null,
|
|
268
|
+
duration: number,
|
|
269
|
+
warp: boolean,
|
|
270
|
+
): void {
|
|
271
|
+
if (!outgoing) {
|
|
272
|
+
if (duration > 0) dominantAction.fadeIn(duration);
|
|
273
|
+
else dominantAction.setEffectiveWeight(1);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// Fade out every non-dominant sibling of the OUTGOING state — a plain
|
|
277
|
+
// (non-blend) outgoing state has none.
|
|
278
|
+
silence(outgoing.natural, outgoing.dominantClip, duration);
|
|
279
|
+
const previousDominant = actionCache.get(outgoing.dominantClip);
|
|
280
|
+
if (previousDominant && duration > 0) {
|
|
281
|
+
previousDominant.crossFadeTo(dominantAction, duration, warp);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
previousDominant?.setEffectiveWeight(0);
|
|
285
|
+
dominantAction.setEffectiveWeight(1);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function enterState(stateId: string, meta: StateAnimationMeta, context: unknown): void {
|
|
289
|
+
const isBlend = isBlendTreeAnimationMeta(meta);
|
|
290
|
+
const weights = isBlend
|
|
291
|
+
? evaluateBlendTree(meta.blendTree as unknown as BlendTreeDef, readParameters(context))
|
|
292
|
+
: naturalWeightsOf(meta);
|
|
293
|
+
const dominant = dominantOf(weights);
|
|
294
|
+
const loop = isBlend ? true : meta.loop;
|
|
295
|
+
const speed = isBlend ? 1 : meta.speed;
|
|
296
|
+
|
|
297
|
+
const dominantAction = prepareStateActions(weights, dominant.clip, loop, speed);
|
|
298
|
+
const duration = meta.crossfade?.duration ?? 0;
|
|
299
|
+
const warp = meta.crossfade?.warp ?? false;
|
|
300
|
+
activateTransition(dominantAction, active, duration, warp);
|
|
301
|
+
|
|
302
|
+
active = {
|
|
303
|
+
stateId,
|
|
304
|
+
meta,
|
|
305
|
+
natural: weights,
|
|
306
|
+
dominantClip: dominant.clip,
|
|
307
|
+
crossfadeRemaining: duration,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// Zero-duration blend-tree entry: no crossfade window to wait out —
|
|
311
|
+
// apply the full live distribution immediately instead of leaving
|
|
312
|
+
// siblings silent for one extra tick.
|
|
313
|
+
if (isBlend && duration <= 0) {
|
|
314
|
+
applyLiveBlendWeights(active, context);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function applyLiveBlendWeights(state: ActiveState, context: unknown): void {
|
|
319
|
+
if (!isBlendTreeAnimationMeta(state.meta)) return;
|
|
320
|
+
const params = readParameters(context);
|
|
321
|
+
const weights = evaluateBlendTree(state.meta.blendTree as unknown as BlendTreeDef, params);
|
|
322
|
+
state.natural = weights;
|
|
323
|
+
for (const { clip: clipName, weight } of weights) {
|
|
324
|
+
actionCache.get(clipName)?.setEffectiveWeight(weight);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const initialSnapshot = actor.getSnapshot();
|
|
329
|
+
const initialStateId = leafStateIdOf(initialSnapshot, metaByStateId);
|
|
330
|
+
if (initialStateId) {
|
|
331
|
+
enterState(initialStateId, metaByStateId.get(initialStateId)!, initialSnapshot.context);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const subscription = actor.subscribe((snapshot) => {
|
|
335
|
+
if (disposed) return;
|
|
336
|
+
const stateId = leafStateIdOf(snapshot, metaByStateId);
|
|
337
|
+
const meta = stateId ? metaByStateId.get(stateId) : undefined;
|
|
338
|
+
if (meta && stateId && stateId !== active?.stateId) {
|
|
339
|
+
enterState(stateId, meta, snapshot.context);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
function tick(dt: number): void {
|
|
344
|
+
if (disposed) return;
|
|
345
|
+
if (active && active.crossfadeRemaining > 0) {
|
|
346
|
+
active.crossfadeRemaining = Math.max(0, active.crossfadeRemaining - dt);
|
|
347
|
+
if (active.crossfadeRemaining <= 0 && isBlendTreeAnimationMeta(active.meta)) {
|
|
348
|
+
applyLiveBlendWeights(active, actor.getSnapshot().context);
|
|
349
|
+
}
|
|
350
|
+
} else if (active && isBlendTreeAnimationMeta(active.meta)) {
|
|
351
|
+
applyLiveBlendWeights(active, actor.getSnapshot().context);
|
|
352
|
+
}
|
|
353
|
+
mixer.update(dt);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
tick,
|
|
358
|
+
actor,
|
|
359
|
+
dispose(): void {
|
|
360
|
+
if (disposed) return;
|
|
361
|
+
disposed = true;
|
|
362
|
+
subscription.unsubscribe();
|
|
363
|
+
for (const [clipName, action] of actionCache) {
|
|
364
|
+
action.stop();
|
|
365
|
+
mixer.uncacheAction(action.getClip(), action.getRoot());
|
|
366
|
+
actionCache.delete(clipName);
|
|
367
|
+
}
|
|
368
|
+
active = null;
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The full state id (e.g. `"character-animation.locomotion"`) of the single
|
|
375
|
+
* currently-active leaf that declares `meta.animation`, per `snapshot.value`.
|
|
376
|
+
* XState's `snapshot.getMeta()` is keyed by EVERY active state node's id
|
|
377
|
+
* (including ancestors with no meta of their own); this narrows to whichever
|
|
378
|
+
* one is actually described in `metaByStateId`. Machines with more than one
|
|
379
|
+
* simultaneously-active animated leaf (parallel regions each declaring
|
|
380
|
+
* `meta.animation`) are out of scope for this binding — v1 assumes exactly
|
|
381
|
+
* one active animation-bearing state at a time, matching the single-mixer
|
|
382
|
+
* §5.6 example; a machine that violates this throws rather than silently
|
|
383
|
+
* picking one.
|
|
384
|
+
*/
|
|
385
|
+
function leafStateIdOf(
|
|
386
|
+
snapshot: { getMeta: () => Record<string, unknown> },
|
|
387
|
+
metaByStateId: Map<string, StateAnimationMeta>,
|
|
388
|
+
): string | undefined {
|
|
389
|
+
const activeIds = Object.keys(snapshot.getMeta()).filter((id) => metaByStateId.has(id));
|
|
390
|
+
if (activeIds.length > 1) {
|
|
391
|
+
throw new AnimationBindingError(
|
|
392
|
+
`[bindXStateAnimation] more than one animated state is simultaneously active ` +
|
|
393
|
+
`(${activeIds.join(', ')}) — parallel-region animation composition is not supported by this ` +
|
|
394
|
+
'binding; exactly one active meta.animation state is required.',
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return activeIds[0];
|
|
398
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* E1 — Zod/TypeScript metadata attached to XState state `meta.animation`
|
|
5
|
+
* (spec §11 E1, §5.6, §3.3).
|
|
6
|
+
*
|
|
7
|
+
* This module defines ONLY animation data: which clip(s) a state plays, how
|
|
8
|
+
* they loop/blend/crossfade, and (for locomotion-style states) how a 1D/2D/
|
|
9
|
+
* direct blend tree distributes weight across several clips from a live
|
|
10
|
+
* parameter. It intentionally contains **no transition vocabulary** — no
|
|
11
|
+
* `on`/`target`/`guard`/`always`/`after`/`entry`/`exit`. XState alone owns
|
|
12
|
+
* states, transitions, events, and guards (§3.3); this metadata is read-only
|
|
13
|
+
* data a binding (`xstate-animation-binding.ts`, E2) consumes to drive a
|
|
14
|
+
* `THREE.AnimationMixer` when XState enters/exits a state. Every schema below
|
|
15
|
+
* is `.strict()` specifically so that smuggling a transition-shaped key into
|
|
16
|
+
* `meta.animation` is a loud Zod "unrecognized key" error, not a silently
|
|
17
|
+
* ignored field (E1 AC: "Metadata contains no transition language").
|
|
18
|
+
*
|
|
19
|
+
* ## Deliberately out of scope: layers, additive blending, bone masks
|
|
20
|
+
*
|
|
21
|
+
* The spec asks for layer/weight/additive metadata "if retained" and bone
|
|
22
|
+
* masks "if supported by the Three implementation" (§11 E1). Neither is
|
|
23
|
+
* supported by the current Three-facing animation code: no bone-masking or
|
|
24
|
+
* blend-compositing across layers exists (the former `AnimGraph` layers, now
|
|
25
|
+
* removed by E5, never had it either, and rejected `blendMode`/`weight`/
|
|
26
|
+
* `boneMask` at parse for exactly this reason — those fields had no runtime
|
|
27
|
+
* reader anywhere in the engine). Per this repo's policy ("Do not author
|
|
28
|
+
* schema fields that have no runtime reader" — CLAUDE.md), this module does
|
|
29
|
+
* not declare fields for capabilities nothing reads. A single XState machine
|
|
30
|
+
* drives one mixer's weight budget; multi-machine/multi-layer composition
|
|
31
|
+
* (if ever added) is a distinct, future binding concern with its own reader,
|
|
32
|
+
* not a dead field here.
|
|
33
|
+
*
|
|
34
|
+
* ## Blend tree shape
|
|
35
|
+
*
|
|
36
|
+
* Deliberately structurally IDENTICAL to `anim-graph-types.ts`'s
|
|
37
|
+
* `BlendTreeDef` (same `type`/`parameter`/`parameterY`/`children[].clip`/
|
|
38
|
+
* `.threshold`/`.thresholdY`/`.weight` fields) so the binding can hand a
|
|
39
|
+
* parsed `AnimationBlendTree` straight to the existing, already-tested
|
|
40
|
+
* `evaluateBlendTree` (`blend-node.ts`) with zero conversion — E2 reuses that
|
|
41
|
+
* function verbatim rather than re-implementing 1D/2D/direct blend math.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Blend tree children
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
const AnimationBlendChildSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
clip: z.string().min(1).describe('Animation clip name this child plays'),
|
|
51
|
+
threshold: z.number().describe('1D/2D blend position on the primary parameter axis'),
|
|
52
|
+
thresholdY: z
|
|
53
|
+
.number()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('2D blend position on the secondary parameter axis (2D blend trees only)'),
|
|
56
|
+
weight: z
|
|
57
|
+
.number()
|
|
58
|
+
.min(0)
|
|
59
|
+
.optional()
|
|
60
|
+
.describe('Explicit weight for "direct" blend trees (ignored by 1D/2D)'),
|
|
61
|
+
})
|
|
62
|
+
.strict();
|
|
63
|
+
|
|
64
|
+
export type AnimationBlendChild = z.infer<typeof AnimationBlendChildSchema>;
|
|
65
|
+
|
|
66
|
+
const AnimationBlendTree1DSchema = z
|
|
67
|
+
.object({
|
|
68
|
+
type: z.literal('1D').describe('Interpolates linearly between sorted children by threshold'),
|
|
69
|
+
parameter: z.string().min(1).describe('Context/selector value driving the blend'),
|
|
70
|
+
children: z
|
|
71
|
+
.array(AnimationBlendChildSchema)
|
|
72
|
+
.min(2, '1D blend tree needs at least 2 children to interpolate between'),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
75
|
+
|
|
76
|
+
const AnimationBlendTree2DSchema = z
|
|
77
|
+
.object({
|
|
78
|
+
type: z
|
|
79
|
+
.literal('2D')
|
|
80
|
+
.describe('Inverse-distance-weights children by (parameter, parameterY) position'),
|
|
81
|
+
parameter: z.string().min(1).describe('Primary axis context/selector value'),
|
|
82
|
+
parameterY: z.string().min(1).describe('Secondary axis context/selector value'),
|
|
83
|
+
children: z
|
|
84
|
+
.array(AnimationBlendChildSchema.extend({ thresholdY: z.number() }))
|
|
85
|
+
.min(1, '2D blend tree needs at least 1 child'),
|
|
86
|
+
})
|
|
87
|
+
.strict();
|
|
88
|
+
|
|
89
|
+
const AnimationBlendTreeDirectSchema = z
|
|
90
|
+
.object({
|
|
91
|
+
type: z.literal('direct').describe('Each child has an explicit weight, normalized to sum to 1'),
|
|
92
|
+
// `parameter` is unused by direct blending (evaluateBlendTree never reads
|
|
93
|
+
// it for `type: 'direct'`) but kept required+defaulted so the parsed
|
|
94
|
+
// value stays structurally assignable to `BlendTreeDef`
|
|
95
|
+
// (`anim-graph-types.ts`), which declares `parameter: string` for every
|
|
96
|
+
// blend mode — the same convention that type already uses.
|
|
97
|
+
parameter: z.string().default(''),
|
|
98
|
+
children: z
|
|
99
|
+
.array(AnimationBlendChildSchema.extend({ weight: z.number().min(0) }))
|
|
100
|
+
.min(1, 'direct blend tree needs at least 1 child'),
|
|
101
|
+
})
|
|
102
|
+
.strict();
|
|
103
|
+
|
|
104
|
+
export const AnimationBlendTreeSchema = z.discriminatedUnion('type', [
|
|
105
|
+
AnimationBlendTree1DSchema,
|
|
106
|
+
AnimationBlendTree2DSchema,
|
|
107
|
+
AnimationBlendTreeDirectSchema,
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
export type AnimationBlendTree = z.infer<typeof AnimationBlendTreeSchema>;
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Crossfade / blend-in-out metadata (shared by clip and blend-tree states)
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
const AnimationCrossfadeMetaSchema = z
|
|
117
|
+
.object({
|
|
118
|
+
duration: z
|
|
119
|
+
.number()
|
|
120
|
+
.min(0)
|
|
121
|
+
.default(0)
|
|
122
|
+
.describe(
|
|
123
|
+
'Seconds to blend from the previously active clip into this state on entry, applied via ' +
|
|
124
|
+
'native `AnimationAction.crossFadeTo`/`fadeIn` (E2). 0 = instant activation, no fade.',
|
|
125
|
+
),
|
|
126
|
+
warp: z
|
|
127
|
+
.boolean()
|
|
128
|
+
.default(false)
|
|
129
|
+
.describe(
|
|
130
|
+
"Passed through to AnimationAction.crossFadeTo's `warp` param — linearly warps timeScale " +
|
|
131
|
+
'across the two clips during the blend so differing clip lengths line up. Default false.',
|
|
132
|
+
),
|
|
133
|
+
})
|
|
134
|
+
.strict()
|
|
135
|
+
.describe('Blend-in/out/crossfade timing for entering this state');
|
|
136
|
+
|
|
137
|
+
export type AnimationCrossfadeMeta = z.infer<typeof AnimationCrossfadeMetaSchema>;
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Per-state animation metadata: single clip OR blend tree, plus crossfade
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
const ClipAnimationMetaSchema = z
|
|
144
|
+
.object({
|
|
145
|
+
clip: z.string().min(1).describe('Single clip to play while this XState state is active'),
|
|
146
|
+
loop: z
|
|
147
|
+
.boolean()
|
|
148
|
+
.default(true)
|
|
149
|
+
.describe(
|
|
150
|
+
'Loop the clip (THREE.LoopRepeat) vs play once (THREE.LoopOnce, clamped on last frame)',
|
|
151
|
+
),
|
|
152
|
+
speed: z
|
|
153
|
+
.number()
|
|
154
|
+
.positive()
|
|
155
|
+
.default(1)
|
|
156
|
+
.describe('Playback speed multiplier (AnimationAction.timeScale)'),
|
|
157
|
+
crossfade: AnimationCrossfadeMetaSchema.optional(),
|
|
158
|
+
})
|
|
159
|
+
.strict();
|
|
160
|
+
|
|
161
|
+
export type ClipAnimationMeta = z.infer<typeof ClipAnimationMetaSchema>;
|
|
162
|
+
|
|
163
|
+
const BlendTreeAnimationMetaSchema = z
|
|
164
|
+
.object({
|
|
165
|
+
blendTree: AnimationBlendTreeSchema.describe(
|
|
166
|
+
'Multiple clips blended by a live context/selector-driven parameter',
|
|
167
|
+
),
|
|
168
|
+
crossfade: AnimationCrossfadeMetaSchema.optional(),
|
|
169
|
+
})
|
|
170
|
+
.strict();
|
|
171
|
+
|
|
172
|
+
export type BlendTreeAnimationMeta = z.infer<typeof BlendTreeAnimationMetaSchema>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The full shape of `state.meta.animation` for one XState state (spec §5.6):
|
|
176
|
+
* exactly one of a single clip or a blend tree, plus optional crossfade
|
|
177
|
+
* timing. `.strict()` on both branches is what makes authoring a transition
|
|
178
|
+
* keyword (`on`, `target`, `guard`, …) inside `meta.animation` a validation
|
|
179
|
+
* error instead of a silently-accepted second transition dialect.
|
|
180
|
+
*/
|
|
181
|
+
export const StateAnimationMetaSchema = z.union([
|
|
182
|
+
ClipAnimationMetaSchema,
|
|
183
|
+
BlendTreeAnimationMetaSchema,
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
export type StateAnimationMeta = z.infer<typeof StateAnimationMetaSchema>;
|
|
187
|
+
|
|
188
|
+
/** True iff a validated meta declares a single clip rather than a blend tree. */
|
|
189
|
+
export function isClipAnimationMeta(meta: StateAnimationMeta): meta is ClipAnimationMeta {
|
|
190
|
+
return 'clip' in meta;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** True iff a validated meta declares a blend tree rather than a single clip. */
|
|
194
|
+
export function isBlendTreeAnimationMeta(meta: StateAnimationMeta): meta is BlendTreeAnimationMeta {
|
|
195
|
+
return 'blendTree' in meta;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Validation entry point
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
/** Thrown by `parseStateAnimationMeta` — always names the offending state id. */
|
|
203
|
+
export class AnimationMetaValidationError extends Error {
|
|
204
|
+
constructor(
|
|
205
|
+
message: string,
|
|
206
|
+
readonly stateId: string,
|
|
207
|
+
readonly issues: z.ZodIssue[],
|
|
208
|
+
) {
|
|
209
|
+
super(message);
|
|
210
|
+
this.name = 'AnimationMetaValidationError';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Validate one state's `meta.animation` value. Called by the E2 binding for
|
|
216
|
+
* every state a machine declares (both at bind time, up front, and — for
|
|
217
|
+
* belt-and-suspenders — again whenever a state is entered).
|
|
218
|
+
*
|
|
219
|
+
* @throws {AnimationMetaValidationError} naming the state id and every Zod
|
|
220
|
+
* issue (path + message) — e.g. an unrecognized `on`/`target` key, a
|
|
221
|
+
* missing `clip`, or a 1D blend tree with only one child.
|
|
222
|
+
*/
|
|
223
|
+
export function parseStateAnimationMeta(stateId: string, raw: unknown): StateAnimationMeta {
|
|
224
|
+
const result = StateAnimationMetaSchema.safeParse(raw);
|
|
225
|
+
if (!result.success) {
|
|
226
|
+
const detail = result.error.issues
|
|
227
|
+
.map((issue) => `${issue.path.length ? issue.path.join('.') : '(root)'}: ${issue.message}`)
|
|
228
|
+
.join('; ');
|
|
229
|
+
throw new AnimationMetaValidationError(
|
|
230
|
+
`[xstate-animation] invalid meta.animation for state "${stateId}": ${detail}`,
|
|
231
|
+
stateId,
|
|
232
|
+
result.error.issues,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return result.data;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// Machine-wide collection (used by E2 to validate + resolve clips up front)
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
/** The minimal shape of an XState `StateNode` this module needs to walk. */
|
|
243
|
+
export interface AnimationMetaStateNodeLike {
|
|
244
|
+
id: string;
|
|
245
|
+
meta?: { animation?: unknown } & Record<string, unknown>;
|
|
246
|
+
states?: Record<string, AnimationMetaStateNodeLike>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface MachineAnimationMetaEntry {
|
|
250
|
+
stateId: string;
|
|
251
|
+
meta: StateAnimationMeta;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Walk every state node in a machine (from its root `StateNode`, e.g.
|
|
256
|
+
* `actor.logic.root`) and validate every `meta.animation` value found.
|
|
257
|
+
* States with no `meta.animation` are skipped (not every XState state needs
|
|
258
|
+
* to drive animation). Returns one entry per animated state, in document
|
|
259
|
+
* order.
|
|
260
|
+
*
|
|
261
|
+
* @throws {AnimationMetaValidationError} on the FIRST invalid meta found —
|
|
262
|
+
* naming the specific state id, so an agent authoring a machine gets an
|
|
263
|
+
* actionable pointer straight to the bad state instead of a generic error.
|
|
264
|
+
*/
|
|
265
|
+
export function collectMachineAnimationMeta(
|
|
266
|
+
root: AnimationMetaStateNodeLike,
|
|
267
|
+
): MachineAnimationMetaEntry[] {
|
|
268
|
+
const out: MachineAnimationMetaEntry[] = [];
|
|
269
|
+
const visit = (node: AnimationMetaStateNodeLike): void => {
|
|
270
|
+
if (node.meta && 'animation' in node.meta && node.meta.animation !== undefined) {
|
|
271
|
+
out.push({ stateId: node.id, meta: parseStateAnimationMeta(node.id, node.meta.animation) });
|
|
272
|
+
}
|
|
273
|
+
for (const child of Object.values(node.states ?? {})) visit(child);
|
|
274
|
+
};
|
|
275
|
+
visit(root);
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Every clip name referenced anywhere in a validated meta (1 for clip states, N for blend trees). */
|
|
280
|
+
export function clipNamesOf(meta: StateAnimationMeta): string[] {
|
|
281
|
+
return isClipAnimationMeta(meta) ? [meta.clip] : meta.blendTree.children.map((c) => c.clip);
|
|
282
|
+
}
|