@vgai/engine 0.3.0 → 0.4.0-canary.20260715.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/package.json +8 -2
- package/schemas/engine-api.json +124 -0
- package/schemas/engine-api.md +53 -0
- package/schemas/engine-capabilities.json +124 -0
- package/schemas/inputmap.schema.json +314 -0
- package/schemas/mat.schema.json +286 -0
- package/schemas/prefab.schema.json +10148 -0
- package/schemas/scn2d.schema.json +475 -0
- package/schemas/vgai-game.schema.json +383 -0
- package/schemas/vscn.schema.json +11007 -0
- package/src/adapter/{world-kind.ts → adapter-surface.ts} +6 -6
- package/src/adapter/authoring.ts +67 -0
- package/src/adapter/game-adapter.ts +8 -8
- package/src/adapter/host-context.ts +2 -4
- package/src/adapter/index.ts +3 -1
- package/src/adapter/system-adapter.ts +73 -0
- package/src/adapter/vgai-scene-game-adapter.ts +239 -180
- package/src/animation/xstate-animation-binding.ts +300 -262
- package/src/animation/xstate-animation-meta.ts +50 -13
- package/src/character/cloth-sim.ts +533 -0
- package/src/character/spring-chain.ts +307 -0
- package/src/core/game-loop.ts +41 -3
- package/src/core/seeded-random.ts +161 -0
- package/src/core/system-runner.ts +20 -3
- package/src/core/types.ts +37 -0
- package/src/data/data-asset.ts +1 -1
- package/src/dev/performance-profiler.ts +213 -0
- package/src/dev/webgl-gpu-timer.ts +53 -0
- package/src/ecs/component-manager.ts +45 -12
- package/src/ecs/game-component.ts +95 -11
- package/src/humanoid/bake.operation.ts +326 -0
- package/src/humanoid/body.ts +663 -0
- package/src/humanoid/clips.ts +149 -0
- package/src/humanoid/compose.ts +209 -0
- package/src/humanoid/generate.ts +189 -0
- package/src/humanoid/index.ts +36 -0
- package/src/humanoid/schema.ts +108 -0
- package/src/humanoid/skeleton.ts +345 -0
- package/src/index.ts +5 -5
- package/src/input/input-manager.ts +701 -22
- package/src/input/input-types.ts +5 -2
- package/src/manifest/index.ts +5 -5
- package/src/manifest/load.ts +125 -72
- package/src/manifest/schema.ts +362 -255
- package/src/react/game-state.tsx +135 -32
- package/src/react/root-adapter.tsx +49 -0
- package/src/react/unmanaged-root-detector.ts +66 -0
- package/src/react/use-data.ts +12 -2
- package/src/runtime/create-runtime.ts +100 -282
- package/src/runtime/debug-bridge.ts +483 -0
- package/src/runtime/debug-registry.ts +856 -0
- package/src/runtime/game.ts +342 -93
- package/src/runtime/gameplay-rng-trap.ts +134 -0
- package/src/runtime/input-router.ts +7 -7
- package/src/runtime/mount-game.ts +40 -38
- package/src/runtime/mount-manifest.ts +169 -37
- package/src/runtime/render-control.ts +13 -13
- package/src/runtime/render-seed.ts +1 -1
- package/src/runtime/state-bridge.ts +24 -10
- package/src/runtime/types.ts +110 -28
- package/src/scene/asset-registry.ts +22 -0
- package/src/scene/component-registry.ts +14 -3
- package/src/scene/defaults.ts +1 -0
- package/src/scene/light-camera-factory.ts +11 -3
- package/src/scene/scene-apply.ts +55 -4
- package/src/scene/scene-loader.ts +10 -27
- package/src/scene/scene-types.ts +0 -1
- package/src/scene/schema/index.ts +0 -34
- package/src/scene/schema/light.ts +16 -1
- package/src/scene/schema/material.ts +96 -91
- package/src/scene/schema/scene-file.ts +1 -7
- package/src/scene/user-data.ts +14 -1
- package/src/setup/setup-renderer.ts +1 -1
- package/src/tools/define-tool.ts +50 -11
- package/src/world2d/authoring-2d.ts +17 -1
- package/src/world2d/collision-2d.ts +1 -1
- package/src/world2d/pixi-game-adapter.ts +19 -17
- package/src/world2d/scene2d-loader.ts +1 -0
- package/src/world2d/types.ts +8 -2
- package/dist/adapter/authoring.d.ts +0 -404
- package/dist/adapter/authoring.d.ts.map +0 -1
- package/dist/adapter/authoring.js +0 -22
- package/dist/adapter/colyseus-networking-adapter.d.ts +0 -43
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +0 -1
- package/dist/adapter/colyseus-networking-adapter.js +0 -38
- package/dist/adapter/first-party-systems.d.ts +0 -38
- package/dist/adapter/first-party-systems.d.ts.map +0 -1
- package/dist/adapter/first-party-systems.js +0 -77
- package/dist/adapter/game-adapter.d.ts +0 -133
- package/dist/adapter/game-adapter.d.ts.map +0 -1
- package/dist/adapter/game-adapter.js +0 -10
- package/dist/adapter/host-context.d.ts +0 -73
- package/dist/adapter/host-context.d.ts.map +0 -1
- package/dist/adapter/host-context.js +0 -16
- package/dist/adapter/index.d.ts +0 -22
- package/dist/adapter/index.d.ts.map +0 -1
- package/dist/adapter/index.js +0 -15
- package/dist/adapter/ingest/game-contract.d.ts +0 -53
- package/dist/adapter/ingest/game-contract.d.ts.map +0 -1
- package/dist/adapter/ingest/game-contract.js +0 -30
- package/dist/adapter/ingest/overlay-applier.d.ts +0 -118
- package/dist/adapter/ingest/overlay-applier.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-applier.js +0 -132
- package/dist/adapter/ingest/overlay-apply.d.ts +0 -60
- package/dist/adapter/ingest/overlay-apply.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-apply.js +0 -101
- package/dist/adapter/ingest/overlay-file.d.ts +0 -79
- package/dist/adapter/ingest/overlay-file.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-file.js +0 -66
- package/dist/adapter/ingest/overlay-report.d.ts +0 -113
- package/dist/adapter/ingest/overlay-report.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-report.js +0 -103
- package/dist/adapter/ingest/scene-capture.d.ts +0 -97
- package/dist/adapter/ingest/scene-capture.d.ts.map +0 -1
- package/dist/adapter/ingest/scene-capture.js +0 -248
- package/dist/adapter/ingest/upstream-pin.d.ts +0 -47
- package/dist/adapter/ingest/upstream-pin.d.ts.map +0 -1
- package/dist/adapter/ingest/upstream-pin.js +0 -50
- package/dist/adapter/loop-gate-report.d.ts +0 -34
- package/dist/adapter/loop-gate-report.d.ts.map +0 -1
- package/dist/adapter/loop-gate-report.js +0 -37
- package/dist/adapter/rapier-physics-adapter.d.ts +0 -12
- package/dist/adapter/rapier-physics-adapter.d.ts.map +0 -1
- package/dist/adapter/rapier-physics-adapter.js +0 -46
- package/dist/adapter/system-adapter.d.ts +0 -133
- package/dist/adapter/system-adapter.d.ts.map +0 -1
- package/dist/adapter/system-adapter.js +0 -13
- package/dist/adapter/transform.d.ts +0 -18
- package/dist/adapter/transform.d.ts.map +0 -1
- package/dist/adapter/transform.js +0 -1
- package/dist/adapter/vgai-scene-game-adapter.d.ts +0 -116
- package/dist/adapter/vgai-scene-game-adapter.d.ts.map +0 -1
- package/dist/adapter/vgai-scene-game-adapter.js +0 -714
- package/dist/adapter/world-kind.d.ts +0 -30
- package/dist/adapter/world-kind.d.ts.map +0 -1
- package/dist/adapter/world-kind.js +0 -18
- package/dist/ai/navigation.d.ts +0 -64
- package/dist/ai/navigation.d.ts.map +0 -1
- package/dist/ai/navigation.js +0 -133
- package/dist/animation/anim-graph-types.d.ts +0 -25
- package/dist/animation/anim-graph-types.d.ts.map +0 -1
- package/dist/animation/anim-graph-types.js +0 -1
- package/dist/animation/animation-clock.d.ts +0 -213
- package/dist/animation/animation-clock.d.ts.map +0 -1
- package/dist/animation/animation-clock.js +0 -301
- package/dist/animation/blend-node.d.ts +0 -22
- package/dist/animation/blend-node.d.ts.map +0 -1
- package/dist/animation/blend-node.js +0 -97
- package/dist/animation/camera-ownership.d.ts +0 -250
- package/dist/animation/camera-ownership.d.ts.map +0 -1
- package/dist/animation/camera-ownership.js +0 -169
- package/dist/animation/cinematic-cues.d.ts +0 -200
- package/dist/animation/cinematic-cues.d.ts.map +0 -1
- package/dist/animation/cinematic-cues.js +0 -213
- package/dist/animation/clip-map.d.ts +0 -12
- package/dist/animation/clip-map.d.ts.map +0 -1
- package/dist/animation/clip-map.js +0 -37
- package/dist/animation/gsap-registration.d.ts +0 -119
- package/dist/animation/gsap-registration.d.ts.map +0 -1
- package/dist/animation/gsap-registration.js +0 -92
- package/dist/animation/theatre-clock-binding.d.ts +0 -89
- package/dist/animation/theatre-clock-binding.d.ts.map +0 -1
- package/dist/animation/theatre-clock-binding.js +0 -29
- package/dist/animation/theatre-director.d.ts +0 -242
- package/dist/animation/theatre-director.d.ts.map +0 -1
- package/dist/animation/theatre-director.js +0 -235
- package/dist/animation/theatre-object-binding.d.ts +0 -305
- package/dist/animation/theatre-object-binding.d.ts.map +0 -1
- package/dist/animation/theatre-object-binding.js +0 -351
- package/dist/animation/xstate-animation-binding.d.ts +0 -111
- package/dist/animation/xstate-animation-binding.d.ts.map +0 -1
- package/dist/animation/xstate-animation-binding.js +0 -307
- package/dist/animation/xstate-animation-meta.d.ts +0 -228
- package/dist/animation/xstate-animation-meta.d.ts.map +0 -1
- package/dist/animation/xstate-animation-meta.js +0 -221
- package/dist/assets.d.ts +0 -26
- package/dist/assets.d.ts.map +0 -1
- package/dist/assets.js +0 -55
- package/dist/audio/index.d.ts +0 -4
- package/dist/audio/index.d.ts.map +0 -1
- package/dist/audio/index.js +0 -24
- package/dist/audio/tone-clock-binding.d.ts +0 -75
- package/dist/audio/tone-clock-binding.d.ts.map +0 -1
- package/dist/audio/tone-clock-binding.js +0 -29
- package/dist/audio/tone-context.d.ts +0 -114
- package/dist/audio/tone-context.d.ts.map +0 -1
- package/dist/audio/tone-context.js +0 -39
- package/dist/audio/tone-offline-render.d.ts +0 -90
- package/dist/audio/tone-offline-render.d.ts.map +0 -1
- package/dist/audio/tone-offline-render.js +0 -56
- package/dist/audio/wav-encode.d.ts +0 -38
- package/dist/audio/wav-encode.d.ts.map +0 -1
- package/dist/audio/wav-encode.js +0 -97
- package/dist/core/game-loop.d.ts +0 -21
- package/dist/core/game-loop.d.ts.map +0 -1
- package/dist/core/game-loop.js +0 -130
- package/dist/core/system-runner.d.ts +0 -120
- package/dist/core/system-runner.d.ts.map +0 -1
- package/dist/core/system-runner.js +0 -286
- package/dist/core/types.d.ts +0 -58
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -25
- package/dist/data/data-asset.d.ts +0 -101
- package/dist/data/data-asset.d.ts.map +0 -1
- package/dist/data/data-asset.js +0 -122
- package/dist/data/data-check-core.d.ts +0 -110
- package/dist/data/data-check-core.d.ts.map +0 -1
- package/dist/data/data-check-core.js +0 -178
- package/dist/data/data-ref.d.ts +0 -79
- package/dist/data/data-ref.d.ts.map +0 -1
- package/dist/data/data-ref.js +0 -130
- package/dist/data/vite-plugin-data.d.ts +0 -79
- package/dist/data/vite-plugin-data.d.ts.map +0 -1
- package/dist/data/vite-plugin-data.js +0 -217
- package/dist/dev/console-bridge.d.ts +0 -22
- package/dist/dev/console-bridge.d.ts.map +0 -1
- package/dist/dev/console-bridge.js +0 -71
- package/dist/dev/debug-draw.d.ts +0 -24
- package/dist/dev/debug-draw.d.ts.map +0 -1
- package/dist/dev/debug-draw.js +0 -73
- package/dist/dev/logger.d.ts +0 -30
- package/dist/dev/logger.d.ts.map +0 -1
- package/dist/dev/logger.js +0 -90
- package/dist/ecs/component-manager.d.ts +0 -189
- package/dist/ecs/component-manager.d.ts.map +0 -1
- package/dist/ecs/component-manager.js +0 -676
- package/dist/ecs/game-component.d.ts +0 -97
- package/dist/ecs/game-component.d.ts.map +0 -1
- package/dist/ecs/game-component.js +0 -80
- package/dist/ecs/hmr-swap-report.d.ts +0 -53
- package/dist/ecs/hmr-swap-report.d.ts.map +0 -1
- package/dist/ecs/hmr-swap-report.js +0 -55
- package/dist/index.d.ts +0 -37
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -30
- package/dist/input/input-manager.d.ts +0 -596
- package/dist/input/input-manager.d.ts.map +0 -1
- package/dist/input/input-manager.js +0 -1564
- package/dist/input/input-types.d.ts +0 -171
- package/dist/input/input-types.d.ts.map +0 -1
- package/dist/input/input-types.js +0 -1
- package/dist/input/prompt-labels.d.ts +0 -21
- package/dist/input/prompt-labels.d.ts.map +0 -1
- package/dist/input/prompt-labels.js +0 -121
- package/dist/input/rebind-controller.d.ts +0 -59
- package/dist/input/rebind-controller.d.ts.map +0 -1
- package/dist/input/rebind-controller.js +0 -85
- package/dist/input/schema.d.ts +0 -16
- package/dist/input/schema.d.ts.map +0 -1
- package/dist/input/schema.js +0 -228
- package/dist/loader.d.ts +0 -43
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js +0 -59
- package/dist/manifest/index.d.ts +0 -5
- package/dist/manifest/index.d.ts.map +0 -1
- package/dist/manifest/index.js +0 -8
- package/dist/manifest/load-file.d.ts +0 -4
- package/dist/manifest/load-file.d.ts.map +0 -1
- package/dist/manifest/load-file.js +0 -14
- package/dist/manifest/load.d.ts +0 -117
- package/dist/manifest/load.d.ts.map +0 -1
- package/dist/manifest/load.js +0 -255
- package/dist/manifest/schema.d.ts +0 -173
- package/dist/manifest/schema.d.ts.map +0 -1
- package/dist/manifest/schema.js +0 -302
- package/dist/physics/collision-system.d.ts +0 -41
- package/dist/physics/collision-system.d.ts.map +0 -1
- package/dist/physics/collision-system.js +0 -64
- package/dist/physics/physics-registry.d.ts +0 -45
- package/dist/physics/physics-registry.d.ts.map +0 -1
- package/dist/physics/physics-registry.js +0 -65
- package/dist/physics/transform-writer.d.ts +0 -16
- package/dist/physics/transform-writer.d.ts.map +0 -1
- package/dist/physics/transform-writer.js +0 -38
- package/dist/physics/trigger-dispatch.d.ts +0 -24
- package/dist/physics/trigger-dispatch.d.ts.map +0 -1
- package/dist/physics/trigger-dispatch.js +0 -80
- package/dist/react/game-state.d.ts +0 -105
- package/dist/react/game-state.d.ts.map +0 -1
- package/dist/react/game-state.js +0 -144
- package/dist/react/use-data.d.ts +0 -60
- package/dist/react/use-data.d.ts.map +0 -1
- package/dist/react/use-data.js +0 -96
- package/dist/react/use-selection.d.ts +0 -75
- package/dist/react/use-selection.d.ts.map +0 -1
- package/dist/react/use-selection.js +0 -90
- package/dist/render/auto-batcher.d.ts +0 -34
- package/dist/render/auto-batcher.d.ts.map +0 -1
- package/dist/render/auto-batcher.js +0 -140
- package/dist/render/render-batch-system.d.ts +0 -33
- package/dist/render/render-batch-system.d.ts.map +0 -1
- package/dist/render/render-batch-system.js +0 -230
- package/dist/render/render-features.d.ts +0 -47
- package/dist/render/render-features.d.ts.map +0 -1
- package/dist/render/render-features.js +0 -105
- package/dist/render/render-settings.d.ts +0 -36
- package/dist/render/render-settings.d.ts.map +0 -1
- package/dist/render/render-settings.js +0 -51
- package/dist/runtime/create-runtime.d.ts +0 -280
- package/dist/runtime/create-runtime.d.ts.map +0 -1
- package/dist/runtime/create-runtime.js +0 -795
- package/dist/runtime/frame-selector-cache.d.ts +0 -41
- package/dist/runtime/frame-selector-cache.d.ts.map +0 -1
- package/dist/runtime/frame-selector-cache.js +0 -65
- package/dist/runtime/game.d.ts +0 -433
- package/dist/runtime/game.d.ts.map +0 -1
- package/dist/runtime/game.js +0 -569
- package/dist/runtime/input-router.d.ts +0 -78
- package/dist/runtime/input-router.d.ts.map +0 -1
- package/dist/runtime/input-router.js +0 -171
- package/dist/runtime/mount-game.d.ts +0 -129
- package/dist/runtime/mount-game.d.ts.map +0 -1
- package/dist/runtime/mount-game.js +0 -152
- package/dist/runtime/mount-manifest.d.ts +0 -119
- package/dist/runtime/mount-manifest.d.ts.map +0 -1
- package/dist/runtime/mount-manifest.js +0 -204
- package/dist/runtime/render-audio-control.d.ts +0 -110
- package/dist/runtime/render-audio-control.d.ts.map +0 -1
- package/dist/runtime/render-audio-control.js +0 -111
- package/dist/runtime/render-control.d.ts +0 -291
- package/dist/runtime/render-control.d.ts.map +0 -1
- package/dist/runtime/render-control.js +0 -264
- package/dist/runtime/render-seed.d.ts +0 -57
- package/dist/runtime/render-seed.d.ts.map +0 -1
- package/dist/runtime/render-seed.js +0 -74
- package/dist/runtime/scene-ui-bridge.d.ts +0 -69
- package/dist/runtime/scene-ui-bridge.d.ts.map +0 -1
- package/dist/runtime/scene-ui-bridge.js +0 -23
- package/dist/runtime/scene-ui-data.d.ts +0 -13
- package/dist/runtime/scene-ui-data.d.ts.map +0 -1
- package/dist/runtime/scene-ui-data.js +0 -92
- package/dist/runtime/state-bridge.d.ts +0 -47
- package/dist/runtime/state-bridge.d.ts.map +0 -1
- package/dist/runtime/state-bridge.js +0 -53
- package/dist/runtime/types.d.ts +0 -179
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/scene/asset-loaders.d.ts +0 -79
- package/dist/scene/asset-loaders.d.ts.map +0 -1
- package/dist/scene/asset-loaders.js +0 -141
- package/dist/scene/asset-paths.d.ts +0 -11
- package/dist/scene/asset-paths.d.ts.map +0 -1
- package/dist/scene/asset-paths.js +0 -108
- package/dist/scene/asset-ref-check.d.ts +0 -88
- package/dist/scene/asset-ref-check.d.ts.map +0 -1
- package/dist/scene/asset-ref-check.js +0 -214
- package/dist/scene/asset-registry.d.ts +0 -30
- package/dist/scene/asset-registry.d.ts.map +0 -1
- package/dist/scene/asset-registry.js +0 -50
- package/dist/scene/collider-dimensions.d.ts +0 -63
- package/dist/scene/collider-dimensions.d.ts.map +0 -1
- package/dist/scene/collider-dimensions.js +0 -98
- package/dist/scene/component-registry.d.ts +0 -17
- package/dist/scene/component-registry.d.ts.map +0 -1
- package/dist/scene/component-registry.js +0 -28
- package/dist/scene/defaults.d.ts +0 -252
- package/dist/scene/defaults.d.ts.map +0 -1
- package/dist/scene/defaults.js +0 -151
- package/dist/scene/geometries/index.d.ts +0 -8
- package/dist/scene/geometries/index.d.ts.map +0 -1
- package/dist/scene/geometries/index.js +0 -7
- package/dist/scene/geometries/terrain.d.ts +0 -2
- package/dist/scene/geometries/terrain.d.ts.map +0 -1
- package/dist/scene/geometries/terrain.js +0 -37
- package/dist/scene/geometry-registry.d.ts +0 -24
- package/dist/scene/geometry-registry.d.ts.map +0 -1
- package/dist/scene/geometry-registry.js +0 -20
- package/dist/scene/instance-registry.d.ts +0 -36
- package/dist/scene/instance-registry.d.ts.map +0 -1
- package/dist/scene/instance-registry.js +0 -55
- package/dist/scene/instancers/grid.d.ts +0 -2
- package/dist/scene/instancers/grid.d.ts.map +0 -1
- package/dist/scene/instancers/grid.js +0 -36
- package/dist/scene/instancers/index.d.ts +0 -8
- package/dist/scene/instancers/index.d.ts.map +0 -1
- package/dist/scene/instancers/index.js +0 -7
- package/dist/scene/light-camera-factory.d.ts +0 -23
- package/dist/scene/light-camera-factory.d.ts.map +0 -1
- package/dist/scene/light-camera-factory.js +0 -68
- package/dist/scene/material-factory.d.ts +0 -17
- package/dist/scene/material-factory.d.ts.map +0 -1
- package/dist/scene/material-factory.js +0 -197
- package/dist/scene/material-registry.d.ts +0 -40
- package/dist/scene/material-registry.d.ts.map +0 -1
- package/dist/scene/material-registry.js +0 -44
- package/dist/scene/materials/index.d.ts +0 -8
- package/dist/scene/materials/index.d.ts.map +0 -1
- package/dist/scene/materials/index.js +0 -7
- package/dist/scene/materials/water.d.ts +0 -2
- package/dist/scene/materials/water.d.ts.map +0 -1
- package/dist/scene/materials/water.js +0 -54
- package/dist/scene/parse.d.ts +0 -23
- package/dist/scene/parse.d.ts.map +0 -1
- package/dist/scene/parse.js +0 -179
- package/dist/scene/particles-factory.d.ts +0 -33
- package/dist/scene/particles-factory.d.ts.map +0 -1
- package/dist/scene/particles-factory.js +0 -282
- package/dist/scene/scene-apply.d.ts +0 -109
- package/dist/scene/scene-apply.d.ts.map +0 -1
- package/dist/scene/scene-apply.js +0 -326
- package/dist/scene/scene-diff-schema.d.ts +0 -285
- package/dist/scene/scene-diff-schema.d.ts.map +0 -1
- package/dist/scene/scene-diff-schema.js +0 -92
- package/dist/scene/scene-diff-types.d.ts +0 -40
- package/dist/scene/scene-diff-types.d.ts.map +0 -1
- package/dist/scene/scene-diff-types.js +0 -14
- package/dist/scene/scene-loader.d.ts +0 -177
- package/dist/scene/scene-loader.d.ts.map +0 -1
- package/dist/scene/scene-loader.js +0 -1213
- package/dist/scene/scene-query.d.ts +0 -11
- package/dist/scene/scene-query.d.ts.map +0 -1
- package/dist/scene/scene-query.js +0 -63
- package/dist/scene/scene-types.d.ts +0 -10
- package/dist/scene/scene-types.d.ts.map +0 -1
- package/dist/scene/scene-types.js +0 -8
- package/dist/scene/scene-version.d.ts +0 -26
- package/dist/scene/scene-version.d.ts.map +0 -1
- package/dist/scene/scene-version.js +0 -34
- package/dist/scene/schema/animation.d.ts +0 -8
- package/dist/scene/schema/animation.d.ts.map +0 -1
- package/dist/scene/schema/animation.js +0 -42
- package/dist/scene/schema/audio.d.ts +0 -13
- package/dist/scene/schema/audio.d.ts.map +0 -1
- package/dist/scene/schema/audio.js +0 -22
- package/dist/scene/schema/camera.d.ts +0 -28
- package/dist/scene/schema/camera.d.ts.map +0 -1
- package/dist/scene/schema/camera.js +0 -17
- package/dist/scene/schema/collider.d.ts +0 -40
- package/dist/scene/schema/collider.d.ts.map +0 -1
- package/dist/scene/schema/collider.js +0 -55
- package/dist/scene/schema/entity-ref.d.ts +0 -31
- package/dist/scene/schema/entity-ref.d.ts.map +0 -1
- package/dist/scene/schema/entity-ref.js +0 -61
- package/dist/scene/schema/entity.d.ts +0 -1036
- package/dist/scene/schema/entity.d.ts.map +0 -1
- package/dist/scene/schema/entity.js +0 -153
- package/dist/scene/schema/environment.d.ts +0 -425
- package/dist/scene/schema/environment.d.ts.map +0 -1
- package/dist/scene/schema/environment.js +0 -357
- package/dist/scene/schema/index.d.ts +0 -39
- package/dist/scene/schema/index.d.ts.map +0 -1
- package/dist/scene/schema/index.js +0 -20
- package/dist/scene/schema/instances.d.ts +0 -14
- package/dist/scene/schema/instances.d.ts.map +0 -1
- package/dist/scene/schema/instances.js +0 -28
- package/dist/scene/schema/joint.d.ts +0 -23
- package/dist/scene/schema/joint.d.ts.map +0 -1
- package/dist/scene/schema/joint.js +0 -23
- package/dist/scene/schema/light.d.ts +0 -38
- package/dist/scene/schema/light.d.ts.map +0 -1
- package/dist/scene/schema/light.js +0 -26
- package/dist/scene/schema/material.d.ts +0 -199
- package/dist/scene/schema/material.d.ts.map +0 -1
- package/dist/scene/schema/material.js +0 -99
- package/dist/scene/schema/mesh.d.ts +0 -48
- package/dist/scene/schema/mesh.d.ts.map +0 -1
- package/dist/scene/schema/mesh.js +0 -82
- package/dist/scene/schema/particles.d.ts +0 -2178
- package/dist/scene/schema/particles.d.ts.map +0 -1
- package/dist/scene/schema/particles.js +0 -321
- package/dist/scene/schema/physics.d.ts +0 -56
- package/dist/scene/schema/physics.d.ts.map +0 -1
- package/dist/scene/schema/physics.js +0 -37
- package/dist/scene/schema/scene-file.d.ts +0 -304
- package/dist/scene/schema/scene-file.d.ts.map +0 -1
- package/dist/scene/schema/scene-file.js +0 -262
- package/dist/scene/schema/shadow.d.ts +0 -28
- package/dist/scene/schema/shadow.d.ts.map +0 -1
- package/dist/scene/schema/shadow.js +0 -20
- package/dist/scene/schema/spline.d.ts +0 -25
- package/dist/scene/schema/spline.d.ts.map +0 -1
- package/dist/scene/schema/spline.js +0 -15
- package/dist/scene/schema/tuples.d.ts +0 -12
- package/dist/scene/schema/tuples.d.ts.map +0 -1
- package/dist/scene/schema/tuples.js +0 -14
- package/dist/scene/schema/ui.d.ts +0 -880
- package/dist/scene/schema/ui.d.ts.map +0 -1
- package/dist/scene/schema/ui.js +0 -506
- package/dist/scene/user-data.d.ts +0 -171
- package/dist/scene/user-data.d.ts.map +0 -1
- package/dist/scene/user-data.js +0 -137
- package/dist/setup/setup-audio.d.ts +0 -20
- package/dist/setup/setup-audio.d.ts.map +0 -1
- package/dist/setup/setup-audio.js +0 -41
- package/dist/setup/setup-particles.d.ts +0 -17
- package/dist/setup/setup-particles.d.ts.map +0 -1
- package/dist/setup/setup-particles.js +0 -16
- package/dist/setup/setup-physics.d.ts +0 -25
- package/dist/setup/setup-physics.d.ts.map +0 -1
- package/dist/setup/setup-physics.js +0 -51
- package/dist/setup/setup-renderer.d.ts +0 -69
- package/dist/setup/setup-renderer.d.ts.map +0 -1
- package/dist/setup/setup-renderer.js +0 -363
- package/dist/tools/define-tool.d.ts +0 -105
- package/dist/tools/define-tool.d.ts.map +0 -1
- package/dist/tools/define-tool.js +0 -82
- package/dist/world2d/authoring-2d.d.ts +0 -76
- package/dist/world2d/authoring-2d.d.ts.map +0 -1
- package/dist/world2d/authoring-2d.js +0 -176
- package/dist/world2d/capture-to-scene2d.d.ts +0 -5
- package/dist/world2d/capture-to-scene2d.d.ts.map +0 -1
- package/dist/world2d/capture-to-scene2d.js +0 -49
- package/dist/world2d/collision-2d.d.ts +0 -52
- package/dist/world2d/collision-2d.d.ts.map +0 -1
- package/dist/world2d/collision-2d.js +0 -70
- package/dist/world2d/components-2d.d.ts +0 -47
- package/dist/world2d/components-2d.d.ts.map +0 -1
- package/dist/world2d/components-2d.js +0 -77
- package/dist/world2d/index.d.ts +0 -34
- package/dist/world2d/index.d.ts.map +0 -1
- package/dist/world2d/index.js +0 -32
- package/dist/world2d/ingest-iframe-2d.d.ts +0 -82
- package/dist/world2d/ingest-iframe-2d.d.ts.map +0 -1
- package/dist/world2d/ingest-iframe-2d.js +0 -166
- package/dist/world2d/ingest2d.d.ts +0 -97
- package/dist/world2d/ingest2d.d.ts.map +0 -1
- package/dist/world2d/ingest2d.js +0 -40
- package/dist/world2d/physics2d-registry.d.ts +0 -24
- package/dist/world2d/physics2d-registry.d.ts.map +0 -1
- package/dist/world2d/physics2d-registry.js +0 -38
- package/dist/world2d/pixi-game-adapter.d.ts +0 -107
- package/dist/world2d/pixi-game-adapter.d.ts.map +0 -1
- package/dist/world2d/pixi-game-adapter.js +0 -211
- package/dist/world2d/pixi-surface.d.ts +0 -39
- package/dist/world2d/pixi-surface.d.ts.map +0 -1
- package/dist/world2d/pixi-surface.js +0 -47
- package/dist/world2d/scene-capture-2d.d.ts +0 -43
- package/dist/world2d/scene-capture-2d.d.ts.map +0 -1
- package/dist/world2d/scene-capture-2d.js +0 -92
- package/dist/world2d/scene2d-loader.d.ts +0 -27
- package/dist/world2d/scene2d-loader.d.ts.map +0 -1
- package/dist/world2d/scene2d-loader.js +0 -274
- package/dist/world2d/schema/entity2d.d.ts +0 -102
- package/dist/world2d/schema/entity2d.d.ts.map +0 -1
- package/dist/world2d/schema/entity2d.js +0 -119
- package/dist/world2d/schema/physics2d.d.ts +0 -46
- package/dist/world2d/schema/physics2d.d.ts.map +0 -1
- package/dist/world2d/schema/physics2d.js +0 -45
- package/dist/world2d/schema/sprite.d.ts +0 -42
- package/dist/world2d/schema/sprite.d.ts.map +0 -1
- package/dist/world2d/schema/sprite.js +0 -63
- package/dist/world2d/schema/tilemap.d.ts +0 -15
- package/dist/world2d/schema/tilemap.d.ts.map +0 -1
- package/dist/world2d/schema/tilemap.js +0 -19
- package/dist/world2d/schema/tuples2d.d.ts +0 -20
- package/dist/world2d/schema/tuples2d.d.ts.map +0 -1
- package/dist/world2d/schema/tuples2d.js +0 -19
- package/dist/world2d/system-adapters-2d.d.ts +0 -16
- package/dist/world2d/system-adapters-2d.d.ts.map +0 -1
- package/dist/world2d/system-adapters-2d.js +0 -37
- package/dist/world2d/transform-writer-2d.d.ts +0 -16
- package/dist/world2d/transform-writer-2d.d.ts.map +0 -1
- package/dist/world2d/transform-writer-2d.js +0 -22
- package/dist/world2d/types.d.ts +0 -53
- package/dist/world2d/types.d.ts.map +0 -1
- package/dist/world2d/types.js +0 -1
- package/src/runtime/scene-ui-bridge.ts +0 -86
- package/src/runtime/scene-ui-data.ts +0 -119
- package/src/scene/schema/ui.ts +0 -602
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import type { Actor, AnyActor, AnyStateMachine } from 'xstate';
|
|
3
|
+
import type { createSystemRunner } from '../core/system-runner';
|
|
4
|
+
import { deleteUserData, getUserData, setUserData } from '../scene/user-data';
|
|
3
5
|
import type { BlendTreeDef } from './anim-graph-types';
|
|
4
6
|
import { evaluateBlendTree } from './blend-node';
|
|
5
7
|
import {
|
|
8
|
+
type AnimationBoneMask,
|
|
6
9
|
type AnimationMetaStateNodeLike,
|
|
7
10
|
clipNamesOf,
|
|
8
11
|
collectMachineAnimationMeta,
|
|
@@ -11,69 +14,7 @@ import {
|
|
|
11
14
|
type StateAnimationMeta,
|
|
12
15
|
} from './xstate-animation-meta';
|
|
13
16
|
|
|
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. */
|
|
17
|
+
/** A binding-time problem such as a missing clip, bone, or ambiguous layer. */
|
|
77
18
|
export class AnimationBindingError extends Error {
|
|
78
19
|
constructor(
|
|
79
20
|
message: string,
|
|
@@ -85,77 +26,161 @@ export class AnimationBindingError extends Error {
|
|
|
85
26
|
}
|
|
86
27
|
|
|
87
28
|
export interface XStateAnimationBindingOptions {
|
|
29
|
+
/** Select live numeric/boolean blend-tree parameters from actor context. */
|
|
30
|
+
selectParameters?: (context: unknown) => Record<string, number | boolean>;
|
|
31
|
+
/** Root whose named Bone/Object3D hierarchy is used by `boneMask`. Defaults to mixer root. */
|
|
32
|
+
root?: THREE.Object3D;
|
|
33
|
+
/** Entity that exposes this binding to editor/runtime inspection. Defaults to `root`. */
|
|
34
|
+
owner?: THREE.Object3D;
|
|
88
35
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
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.
|
|
36
|
+
* Engine runner used to register `tick` in the canonical animation phase.
|
|
37
|
+
* Pass `ctx.systems`; disposal removes the callback automatically. When
|
|
38
|
+
* omitted, the caller owns ticking (useful for standalone Three tests).
|
|
95
39
|
*/
|
|
96
|
-
|
|
40
|
+
systems?: Pick<ReturnType<typeof createSystemRunner>, 'add' | 'remove'>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface XStateAnimationLayerState {
|
|
44
|
+
readonly layer: string;
|
|
45
|
+
readonly stateId: string;
|
|
46
|
+
readonly clips: readonly string[];
|
|
47
|
+
readonly weight: number;
|
|
48
|
+
readonly blendMode: 'override' | 'additive';
|
|
49
|
+
readonly boneMask?: AnimationBoneMask;
|
|
97
50
|
}
|
|
98
51
|
|
|
99
52
|
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
|
-
*/
|
|
53
|
+
/** Advance native actions/mixer. Register this in the engine animation phase. */
|
|
105
54
|
tick: (dt: number) => void;
|
|
106
|
-
/** The bound actor, for convenience (`binding.actor.send({...})`). */
|
|
107
55
|
readonly actor: AnyActor;
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
*/
|
|
56
|
+
/** Current native composition, useful to the editor and game diagnostics. */
|
|
57
|
+
getActiveLayers: () => readonly XStateAnimationLayerState[];
|
|
58
|
+
/** Unsubscribe, stop/uncache actions, and remove owner inspection data. Idempotent. */
|
|
115
59
|
dispose: () => void;
|
|
116
60
|
}
|
|
117
61
|
|
|
62
|
+
let inspectionVersion = 0;
|
|
63
|
+
const inspectionListeners = new Set<() => void>();
|
|
64
|
+
|
|
65
|
+
/** React/useSyncExternalStore-compatible lifecycle signal for live editor inspection. */
|
|
66
|
+
export function subscribeXStateAnimationBindings(listener: () => void): () => void {
|
|
67
|
+
inspectionListeners.add(listener);
|
|
68
|
+
return () => inspectionListeners.delete(listener);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Monotonic snapshot changed whenever a binding is attached or disposed. */
|
|
72
|
+
export function getXStateAnimationBindingsVersion(): number {
|
|
73
|
+
return inspectionVersion;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function notifyInspectionLifecycle(): void {
|
|
77
|
+
inspectionVersion++;
|
|
78
|
+
for (const listener of inspectionListeners) listener();
|
|
79
|
+
}
|
|
80
|
+
|
|
118
81
|
interface WeightedClip {
|
|
119
82
|
clip: string;
|
|
83
|
+
actionKey: string;
|
|
120
84
|
weight: number;
|
|
121
85
|
}
|
|
122
86
|
|
|
123
87
|
interface ActiveState {
|
|
88
|
+
layer: string;
|
|
124
89
|
stateId: string;
|
|
125
90
|
meta: StateAnimationMeta;
|
|
126
|
-
/** The action(s) this state's `meta` refers to, one weighted entry per clip. */
|
|
127
91
|
natural: WeightedClip[];
|
|
128
|
-
|
|
129
|
-
dominantClip: string;
|
|
130
|
-
/** Seconds remaining in the enter crossfade; 0 once settled (or if duration was 0). */
|
|
92
|
+
dominantActionKey: string;
|
|
131
93
|
crossfadeRemaining: number;
|
|
132
94
|
}
|
|
133
95
|
|
|
134
|
-
|
|
135
|
-
|
|
96
|
+
interface CachedAction {
|
|
97
|
+
action: THREE.AnimationAction;
|
|
98
|
+
clip: THREE.AnimationClip;
|
|
136
99
|
}
|
|
137
100
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
101
|
+
const layerOf = (meta: StateAnimationMeta): string => meta.layer ?? 'base';
|
|
102
|
+
const layerWeightOf = (meta: StateAnimationMeta): number => meta.weight ?? 1;
|
|
103
|
+
const blendModeOf = (meta: StateAnimationMeta): 'override' | 'additive' =>
|
|
104
|
+
meta.blendMode ?? 'override';
|
|
105
|
+
|
|
106
|
+
function targetNameOf(track: THREE.KeyframeTrack): string {
|
|
107
|
+
try {
|
|
108
|
+
return THREE.PropertyBinding.parseTrackName(track.name).nodeName ?? '';
|
|
109
|
+
} catch {
|
|
110
|
+
const dot = track.name.indexOf('.');
|
|
111
|
+
return dot < 0 ? track.name : track.name.slice(0, dot);
|
|
144
112
|
}
|
|
145
|
-
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function subtreeNames(root: THREE.Object3D, requested: readonly string[], trackNames: Set<string>) {
|
|
116
|
+
const names = new Set<string>();
|
|
117
|
+
const missing: string[] = [];
|
|
118
|
+
for (const requestedName of requested) {
|
|
119
|
+
let found = false;
|
|
120
|
+
root.traverse((object) => {
|
|
121
|
+
if (object.name !== requestedName) return;
|
|
122
|
+
found = true;
|
|
123
|
+
object.traverse((descendant) => {
|
|
124
|
+
if (descendant.name) names.add(descendant.name);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
// Some exporters target a named node not retained as a discoverable Bone.
|
|
128
|
+
// Exact track-target matches remain useful, but cannot imply descendants.
|
|
129
|
+
if (trackNames.has(requestedName)) {
|
|
130
|
+
found = true;
|
|
131
|
+
names.add(requestedName);
|
|
132
|
+
}
|
|
133
|
+
if (!found) missing.push(requestedName);
|
|
134
|
+
}
|
|
135
|
+
return { names, missing };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function maskedClip(
|
|
139
|
+
source: THREE.AnimationClip,
|
|
140
|
+
mask: AnimationBoneMask,
|
|
141
|
+
root: THREE.Object3D,
|
|
142
|
+
stateId: string,
|
|
143
|
+
): THREE.AnimationClip {
|
|
144
|
+
const trackNames = new Set(source.tracks.map(targetNameOf));
|
|
145
|
+
const included = mask.include ? subtreeNames(root, mask.include, trackNames) : undefined;
|
|
146
|
+
const excluded = mask.exclude ? subtreeNames(root, mask.exclude, trackNames) : undefined;
|
|
147
|
+
const missing = [...(included?.missing ?? []), ...(excluded?.missing ?? [])];
|
|
148
|
+
if (missing.length) {
|
|
149
|
+
throw new AnimationBindingError(
|
|
150
|
+
`[bindXStateAnimation] state "${stateId}" boneMask names unknown bones/objects: ${missing.join(', ')}`,
|
|
151
|
+
stateId,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const tracks = source.tracks.filter((track) => {
|
|
155
|
+
const name = targetNameOf(track);
|
|
156
|
+
return (!included || included.names.has(name)) && !excluded?.names.has(name);
|
|
157
|
+
});
|
|
158
|
+
if (tracks.length === 0) {
|
|
159
|
+
throw new AnimationBindingError(
|
|
160
|
+
`[bindXStateAnimation] state "${stateId}" boneMask removes every track from clip "${source.name}"`,
|
|
161
|
+
stateId,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return new THREE.AnimationClip(
|
|
165
|
+
source.name,
|
|
166
|
+
source.duration,
|
|
167
|
+
tracks.map((track) => track.clone()),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function dominantOf(weights: WeightedClip[]): WeightedClip {
|
|
172
|
+
return weights.reduce((a, b) => (b.weight > a.weight ? b : a), weights[0]!);
|
|
146
173
|
}
|
|
147
174
|
|
|
148
175
|
/**
|
|
149
|
-
* Bind
|
|
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.
|
|
176
|
+
* Bind a native XState actor to native Three actions.
|
|
156
177
|
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
178
|
+
* One active animated state is allowed per named layer. Parallel XState
|
|
179
|
+
* regions therefore compose lower-body locomotion, upper-body weapon states,
|
|
180
|
+
* facial animation, and other independent layers without introducing a
|
|
181
|
+
* second transition language. Bone masks clone/filter native clip tracks;
|
|
182
|
+
* additive layers use `AnimationUtils.makeClipAdditive` and Three's additive
|
|
183
|
+
* blend mode. Single unlayered machines retain the original clip/action path.
|
|
159
184
|
*/
|
|
160
185
|
export function bindXStateAnimation(
|
|
161
186
|
actor: Actor<AnyStateMachine>,
|
|
@@ -163,12 +188,18 @@ export function bindXStateAnimation(
|
|
|
163
188
|
clips: Map<string, THREE.AnimationClip>,
|
|
164
189
|
options: XStateAnimationBindingOptions = {},
|
|
165
190
|
): XStateAnimationBinding {
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
|
|
191
|
+
const mixerRoot = mixer.getRoot();
|
|
192
|
+
const root = options.root ?? (mixerRoot instanceof THREE.Object3D ? mixerRoot : undefined);
|
|
193
|
+
if (!root) {
|
|
194
|
+
throw new AnimationBindingError(
|
|
195
|
+
'[bindXStateAnimation] a THREE.Object3D `root` option is required when the mixer uses AnimationObjectGroup',
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const owner = options.owner ?? root;
|
|
199
|
+
const machineRoot = actor.logic.root as unknown as AnimationMetaStateNodeLike;
|
|
200
|
+
const entries: MachineAnimationMetaEntry[] = collectMachineAnimationMeta(machineRoot);
|
|
201
|
+
const metaByStateId = new Map(entries.map((entry) => [entry.stateId, entry.meta]));
|
|
169
202
|
|
|
170
|
-
// Resolve every referenced clip up front — actionable, named errors before
|
|
171
|
-
// any state is ever entered.
|
|
172
203
|
for (const entry of entries) {
|
|
173
204
|
for (const clipName of clipNamesOf(entry.meta)) {
|
|
174
205
|
if (!clips.has(clipName)) {
|
|
@@ -181,25 +212,58 @@ export function bindXStateAnimation(
|
|
|
181
212
|
}
|
|
182
213
|
}
|
|
183
214
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
215
|
+
// Different layers need distinct AnimationActions even when they use the
|
|
216
|
+
// same source clip. A plain base state keeps the original clip identity for
|
|
217
|
+
// backwards-compatible mixer.existingAction(sourceClip) behavior.
|
|
218
|
+
const preparedClips = new Map<string, THREE.AnimationClip>();
|
|
219
|
+
function actionKey(stateId: string, meta: StateAnimationMeta, clipName: string): string {
|
|
220
|
+
const transformed =
|
|
221
|
+
layerOf(meta) !== 'base' || Boolean(meta.boneMask) || blendModeOf(meta) === 'additive';
|
|
222
|
+
return transformed ? `${stateId}\u0000${clipName}` : clipName;
|
|
223
|
+
}
|
|
224
|
+
for (const { stateId, meta } of entries) {
|
|
225
|
+
for (const clipName of clipNamesOf(meta)) {
|
|
226
|
+
const key = actionKey(stateId, meta, clipName);
|
|
227
|
+
if (preparedClips.has(key)) continue;
|
|
228
|
+
const source = clips.get(clipName)!;
|
|
229
|
+
let prepared = meta.boneMask ? maskedClip(source, meta.boneMask, root, stateId) : source;
|
|
230
|
+
if (prepared !== source || key !== clipName) {
|
|
231
|
+
if (prepared === source) prepared = source.clone();
|
|
232
|
+
prepared.name = `${source.name}@${layerOf(meta)}:${stateId}`;
|
|
193
233
|
}
|
|
194
|
-
|
|
234
|
+
if (blendModeOf(meta) === 'additive') {
|
|
235
|
+
THREE.AnimationUtils.makeClipAdditive(prepared);
|
|
236
|
+
prepared.blendMode = THREE.AdditiveAnimationBlendMode;
|
|
237
|
+
} else {
|
|
238
|
+
prepared.blendMode = THREE.NormalAnimationBlendMode;
|
|
239
|
+
}
|
|
240
|
+
preparedClips.set(key, prepared);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const actionCache = new Map<string, CachedAction>();
|
|
245
|
+
function ensureAction(
|
|
246
|
+
key: string,
|
|
247
|
+
loop: boolean,
|
|
248
|
+
speed: number,
|
|
249
|
+
weight: number,
|
|
250
|
+
): THREE.AnimationAction {
|
|
251
|
+
let cached = actionCache.get(key);
|
|
252
|
+
if (!cached) {
|
|
253
|
+
const clip = preparedClips.get(key);
|
|
254
|
+
if (!clip)
|
|
255
|
+
throw new AnimationBindingError(`[bindXStateAnimation] prepared clip "${key}" not found`);
|
|
256
|
+
const action = mixer.clipAction(clip);
|
|
195
257
|
action.setEffectiveWeight(0);
|
|
196
|
-
action.play();
|
|
197
|
-
|
|
258
|
+
action.play();
|
|
259
|
+
cached = { action, clip };
|
|
260
|
+
actionCache.set(key, cached);
|
|
198
261
|
}
|
|
199
|
-
action.setLoop(loop ? THREE.LoopRepeat : THREE.LoopOnce, Infinity);
|
|
200
|
-
action.clampWhenFinished = !loop;
|
|
201
|
-
action.timeScale = speed;
|
|
202
|
-
|
|
262
|
+
cached.action.setLoop(loop ? THREE.LoopRepeat : THREE.LoopOnce, Infinity);
|
|
263
|
+
cached.action.clampWhenFinished = !loop;
|
|
264
|
+
cached.action.timeScale = speed;
|
|
265
|
+
cached.action.weight = weight;
|
|
266
|
+
return cached.action;
|
|
203
267
|
}
|
|
204
268
|
|
|
205
269
|
function readParameters(context: unknown): Map<string, number | boolean> {
|
|
@@ -209,190 +273,164 @@ export function bindXStateAnimation(
|
|
|
209
273
|
return new Map(Object.entries(raw ?? {}));
|
|
210
274
|
}
|
|
211
275
|
|
|
212
|
-
|
|
276
|
+
function weightsOf(stateId: string, meta: StateAnimationMeta, context: unknown): WeightedClip[] {
|
|
277
|
+
const weights = isBlendTreeAnimationMeta(meta)
|
|
278
|
+
? evaluateBlendTree(meta.blendTree as unknown as BlendTreeDef, readParameters(context))
|
|
279
|
+
: [{ clip: meta.clip, weight: 1 }];
|
|
280
|
+
return weights.map(({ clip, weight }) => ({
|
|
281
|
+
clip,
|
|
282
|
+
weight,
|
|
283
|
+
actionKey: actionKey(stateId, meta, clip),
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const activeLayers = new Map<string, ActiveState>();
|
|
213
288
|
let disposed = false;
|
|
214
289
|
|
|
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
290
|
function silence(weights: WeightedClip[], except: string | undefined, duration: number): void {
|
|
225
|
-
for (const
|
|
226
|
-
if (
|
|
227
|
-
const action = actionCache.get(
|
|
291
|
+
for (const weighted of weights) {
|
|
292
|
+
if (weighted.actionKey === except) continue;
|
|
293
|
+
const action = actionCache.get(weighted.actionKey)?.action;
|
|
228
294
|
if (!action) continue;
|
|
229
295
|
if (duration > 0) action.fadeOut(duration);
|
|
230
296
|
else action.setEffectiveWeight(0);
|
|
231
297
|
}
|
|
232
298
|
}
|
|
233
299
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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;
|
|
300
|
+
function applyLiveBlendWeights(state: ActiveState, context: unknown): void {
|
|
301
|
+
if (!isBlendTreeAnimationMeta(state.meta)) return;
|
|
302
|
+
state.natural = weightsOf(state.stateId, state.meta, context);
|
|
303
|
+
const layerWeight = layerWeightOf(state.meta);
|
|
304
|
+
for (const weighted of state.natural) {
|
|
305
|
+
actionCache.get(weighted.actionKey)?.action.setEffectiveWeight(weighted.weight * layerWeight);
|
|
283
306
|
}
|
|
284
|
-
previousDominant?.setEffectiveWeight(0);
|
|
285
|
-
dominantAction.setEffectiveWeight(1);
|
|
286
307
|
}
|
|
287
308
|
|
|
288
309
|
function enterState(stateId: string, meta: StateAnimationMeta, context: unknown): void {
|
|
289
|
-
const
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
: naturalWeightsOf(meta);
|
|
310
|
+
const layer = layerOf(meta);
|
|
311
|
+
const outgoing = activeLayers.get(layer) ?? null;
|
|
312
|
+
const weights = weightsOf(stateId, meta, context);
|
|
293
313
|
const dominant = dominantOf(weights);
|
|
294
|
-
const loop =
|
|
295
|
-
const speed =
|
|
314
|
+
const loop = isBlendTreeAnimationMeta(meta) ? true : meta.loop;
|
|
315
|
+
const speed = isBlendTreeAnimationMeta(meta) ? 1 : meta.speed;
|
|
316
|
+
const layerWeight = layerWeightOf(meta);
|
|
317
|
+
|
|
318
|
+
for (const weighted of weights) ensureAction(weighted.actionKey, loop, speed, layerWeight);
|
|
319
|
+
const dominantAction = actionCache.get(dominant.actionKey)!.action;
|
|
320
|
+
dominantAction.reset().setEffectiveWeight(layerWeight);
|
|
321
|
+
for (const weighted of weights) {
|
|
322
|
+
if (weighted.actionKey === dominant.actionKey) continue;
|
|
323
|
+
actionCache.get(weighted.actionKey)!.action.reset().setEffectiveWeight(0);
|
|
324
|
+
}
|
|
296
325
|
|
|
297
|
-
const dominantAction = prepareStateActions(weights, dominant.clip, loop, speed);
|
|
298
326
|
const duration = meta.crossfade?.duration ?? 0;
|
|
299
327
|
const warp = meta.crossfade?.warp ?? false;
|
|
300
|
-
|
|
328
|
+
if (!outgoing) {
|
|
329
|
+
if (duration > 0) dominantAction.fadeIn(duration);
|
|
330
|
+
} else {
|
|
331
|
+
silence(outgoing.natural, outgoing.dominantActionKey, duration);
|
|
332
|
+
const previous = actionCache.get(outgoing.dominantActionKey)?.action;
|
|
333
|
+
if (previous && duration > 0) previous.crossFadeTo(dominantAction, duration, warp);
|
|
334
|
+
else {
|
|
335
|
+
previous?.setEffectiveWeight(0);
|
|
336
|
+
dominantAction.setEffectiveWeight(layerWeight);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
301
339
|
|
|
302
|
-
active = {
|
|
340
|
+
const active: ActiveState = {
|
|
341
|
+
layer,
|
|
303
342
|
stateId,
|
|
304
343
|
meta,
|
|
305
344
|
natural: weights,
|
|
306
|
-
|
|
345
|
+
dominantActionKey: dominant.actionKey,
|
|
307
346
|
crossfadeRemaining: duration,
|
|
308
347
|
};
|
|
348
|
+
activeLayers.set(layer, active);
|
|
349
|
+
if (isBlendTreeAnimationMeta(meta) && duration <= 0) applyLiveBlendWeights(active, context);
|
|
350
|
+
}
|
|
309
351
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
// siblings silent for one extra tick.
|
|
313
|
-
if (isBlend && duration <= 0) {
|
|
314
|
-
applyLiveBlendWeights(active, context);
|
|
315
|
-
}
|
|
352
|
+
function activeStateIds(snapshot: { getMeta: () => Record<string, unknown> }): string[] {
|
|
353
|
+
return Object.keys(snapshot.getMeta()).filter((id) => metaByStateId.has(id));
|
|
316
354
|
}
|
|
317
355
|
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
356
|
+
function reconcile(snapshot: { getMeta: () => Record<string, unknown>; context: unknown }): void {
|
|
357
|
+
const nextByLayer = new Map<string, string>();
|
|
358
|
+
for (const stateId of activeStateIds(snapshot)) {
|
|
359
|
+
const meta = metaByStateId.get(stateId)!;
|
|
360
|
+
const layer = layerOf(meta);
|
|
361
|
+
const previous = nextByLayer.get(layer);
|
|
362
|
+
if (previous) {
|
|
363
|
+
throw new AnimationBindingError(
|
|
364
|
+
`[bindXStateAnimation] animated states "${previous}" and "${stateId}" are both active ` +
|
|
365
|
+
`on layer "${layer}". Give parallel regions distinct meta.animation.layer names.`,
|
|
366
|
+
stateId,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
nextByLayer.set(layer, stateId);
|
|
325
370
|
}
|
|
326
|
-
}
|
|
327
371
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
372
|
+
for (const [layer, active] of activeLayers) {
|
|
373
|
+
if (nextByLayer.has(layer)) continue;
|
|
374
|
+
silence(active.natural, undefined, 0);
|
|
375
|
+
activeLayers.delete(layer);
|
|
376
|
+
}
|
|
377
|
+
for (const [layer, stateId] of nextByLayer) {
|
|
378
|
+
if (activeLayers.get(layer)?.stateId === stateId) continue;
|
|
379
|
+
enterState(stateId, metaByStateId.get(stateId)!, snapshot.context);
|
|
380
|
+
}
|
|
332
381
|
}
|
|
333
382
|
|
|
383
|
+
const initialSnapshot = actor.getSnapshot();
|
|
384
|
+
reconcile(initialSnapshot);
|
|
334
385
|
const subscription = actor.subscribe((snapshot) => {
|
|
335
|
-
if (disposed)
|
|
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
|
-
}
|
|
386
|
+
if (!disposed) reconcile(snapshot);
|
|
341
387
|
});
|
|
342
388
|
|
|
343
389
|
function tick(dt: number): void {
|
|
344
390
|
if (disposed) return;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (active.crossfadeRemaining
|
|
348
|
-
|
|
391
|
+
const context = actor.getSnapshot().context;
|
|
392
|
+
for (const active of activeLayers.values()) {
|
|
393
|
+
if (active.crossfadeRemaining > 0) {
|
|
394
|
+
active.crossfadeRemaining = Math.max(0, active.crossfadeRemaining - dt);
|
|
395
|
+
if (active.crossfadeRemaining <= 0) applyLiveBlendWeights(active, context);
|
|
396
|
+
} else {
|
|
397
|
+
applyLiveBlendWeights(active, context);
|
|
349
398
|
}
|
|
350
|
-
} else if (active && isBlendTreeAnimationMeta(active.meta)) {
|
|
351
|
-
applyLiveBlendWeights(active, actor.getSnapshot().context);
|
|
352
399
|
}
|
|
353
400
|
mixer.update(dt);
|
|
354
401
|
}
|
|
355
402
|
|
|
356
|
-
|
|
403
|
+
const binding: XStateAnimationBinding = {
|
|
357
404
|
tick,
|
|
358
405
|
actor,
|
|
406
|
+
getActiveLayers: () =>
|
|
407
|
+
[...activeLayers.values()].map((active) => ({
|
|
408
|
+
layer: active.layer,
|
|
409
|
+
stateId: active.stateId,
|
|
410
|
+
clips: active.natural.map((weighted) => weighted.clip),
|
|
411
|
+
weight: layerWeightOf(active.meta),
|
|
412
|
+
blendMode: blendModeOf(active.meta),
|
|
413
|
+
...(active.meta.boneMask ? { boneMask: active.meta.boneMask } : {}),
|
|
414
|
+
})),
|
|
359
415
|
dispose(): void {
|
|
360
416
|
if (disposed) return;
|
|
361
417
|
disposed = true;
|
|
418
|
+
options.systems?.remove('animation', tick);
|
|
362
419
|
subscription.unsubscribe();
|
|
363
|
-
for (const
|
|
420
|
+
for (const { action, clip } of actionCache.values()) {
|
|
364
421
|
action.stop();
|
|
365
|
-
mixer.uncacheAction(
|
|
366
|
-
|
|
422
|
+
mixer.uncacheAction(clip, action.getRoot());
|
|
423
|
+
}
|
|
424
|
+
actionCache.clear();
|
|
425
|
+
activeLayers.clear();
|
|
426
|
+
if (getUserData(owner, '_xstateAnimation') === binding) {
|
|
427
|
+
deleteUserData(owner, '_xstateAnimation');
|
|
428
|
+
notifyInspectionLifecycle();
|
|
367
429
|
}
|
|
368
|
-
active = null;
|
|
369
430
|
},
|
|
370
431
|
};
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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];
|
|
432
|
+
setUserData(owner, '_xstateAnimation', binding);
|
|
433
|
+
notifyInspectionLifecycle();
|
|
434
|
+
options.systems?.add('animation', tick);
|
|
435
|
+
return binding;
|
|
398
436
|
}
|