@downdraft/engine 0.0.0 → 0.1.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/app/src/index.ts +10 -0
- package/app/src/renderer/bootstrap.ts +271 -0
- package/app/src/renderer/downdraft-base.css +93 -0
- package/app/src/renderer/feature-log.ts +200 -0
- package/app/src/renderer/game-module.ts +923 -0
- package/app/src/renderer/hooks.ts +88 -0
- package/app/src/renderer/hot-reload.ts +178 -0
- package/app/src/renderer/import-cache.ts +88 -0
- package/app/src/renderer/index.ts +206 -0
- package/app/src/renderer/mcp-harness.ts +144 -0
- package/app/src/renderer/save-store-factory.ts +239 -0
- package/app/src/renderer/sim-bridge.ts +183 -0
- package/app/src/renderer/standard-automation-tools.ts +771 -0
- package/app/src/shared/feature-log.ts +71 -0
- package/app/src/shared/gpu-info.ts +82 -0
- package/app/src/shared/import-cache-store.ts +142 -0
- package/app/src/shared/index.ts +6 -0
- package/app/src/shared/mcp-discovery.ts +47 -0
- package/app/src/shared/mcp-proxy.ts +82 -0
- package/app/src/shared/types.ts +324 -0
- package/asset-bake/src/bake-audio.ts +119 -0
- package/asset-bake/src/bake-gltf.ts +172 -0
- package/asset-bake/src/bake.ts +78 -0
- package/asset-bake/src/cache.ts +188 -0
- package/asset-bake/src/config.ts +186 -0
- package/asset-bake/src/index.ts +36 -0
- package/core/src/animation/animation-event.ts +41 -0
- package/core/src/animation/bone-mask.ts +122 -0
- package/core/src/animation/clip.ts +252 -0
- package/core/src/animation/index.ts +22 -0
- package/core/src/animation/mixamo.ts +113 -0
- package/core/src/animation/morph-target.ts +69 -0
- package/core/src/animation/player.ts +697 -0
- package/core/src/animation/retarget.ts +158 -0
- package/core/src/animation/skeleton-animator.ts +1399 -0
- package/core/src/animation/skeleton.ts +214 -0
- package/core/src/animation/state-machine.ts +280 -0
- package/core/src/assets/blob-store.ts +66 -0
- package/core/src/assets/cache.ts +186 -0
- package/core/src/assets/cubemap-converter.ts +155 -0
- package/core/src/assets/environment-manager.ts +121 -0
- package/core/src/assets/import-cache.ts +65 -0
- package/core/src/assets/import-settings.ts +72 -0
- package/core/src/assets/importer.ts +108 -0
- package/core/src/assets/index.ts +74 -0
- package/core/src/assets/irradiance-generator.ts +198 -0
- package/core/src/assets/loader-audio.ts +86 -0
- package/core/src/assets/loader-cubemap.ts +211 -0
- package/core/src/assets/loader-dds.ts +236 -0
- package/core/src/assets/loader-hdr.ts +474 -0
- package/core/src/assets/loader-mesh.ts +143 -0
- package/core/src/assets/loader-shader.ts +73 -0
- package/core/src/assets/loader-texture.ts +349 -0
- package/core/src/assets/lod.ts +198 -0
- package/core/src/assets/manager.ts +480 -0
- package/core/src/assets/manifest.ts +81 -0
- package/core/src/assets/material-bridge.ts +186 -0
- package/core/src/assets/model-importer.ts +72 -0
- package/core/src/assets/model-normalizer.ts +248 -0
- package/core/src/assets/model-to-gpu.ts +70 -0
- package/core/src/assets/model-to-mesh.ts +237 -0
- package/core/src/assets/prefilter-generator.ts +250 -0
- package/core/src/audio/engine.ts +177 -0
- package/core/src/audio/interface.ts +130 -0
- package/core/src/audio/listener.ts +32 -0
- package/core/src/audio/mixer.ts +141 -0
- package/core/src/audio/registry.ts +52 -0
- package/core/src/audio/sab.ts +87 -0
- package/core/src/audio/source.ts +89 -0
- package/core/src/change-detection/atomics.ts +1 -0
- package/core/src/change-detection/tracker.ts +47 -0
- package/core/src/debug-draw/lines.ts +64 -0
- package/core/src/debug-draw/points.ts +17 -0
- package/core/src/debug-draw/queue.ts +124 -0
- package/core/src/debug-draw/text.ts +25 -0
- package/core/src/ecs/archetype.ts +323 -0
- package/core/src/ecs/component.ts +188 -0
- package/core/src/ecs/entity.ts +25 -0
- package/core/src/ecs/events.ts +93 -0
- package/core/src/ecs/hierarchy.ts +124 -0
- package/core/src/ecs/hmr-swap-registry.ts +22 -0
- package/core/src/ecs/index.ts +25 -0
- package/core/src/ecs/query.ts +141 -0
- package/core/src/ecs/resource.ts +8 -0
- package/core/src/ecs/schedule.ts +181 -0
- package/core/src/ecs/system-params.ts +99 -0
- package/core/src/ecs/system.ts +42 -0
- package/core/src/ecs/world.ts +348 -0
- package/core/src/index.ts +352 -0
- package/core/src/input/context.ts +73 -0
- package/core/src/input/dom-handler.ts +380 -0
- package/core/src/input/local-player-manager.ts +218 -0
- package/core/src/input/mapping.ts +40 -0
- package/core/src/input/multi-sab-bridge.ts +276 -0
- package/core/src/input/multi-state.ts +54 -0
- package/core/src/input/sab-bridge.ts +71 -0
- package/core/src/input/state.ts +132 -0
- package/core/src/input/ui-router.ts +87 -0
- package/core/src/library/host.ts +317 -0
- package/core/src/library/index.ts +16 -0
- package/core/src/library/library.ts +292 -0
- package/core/src/material/compiler.ts +80 -0
- package/core/src/material/graph-bridge.ts +172 -0
- package/core/src/material/index.ts +9 -0
- package/core/src/material/library.ts +442 -0
- package/core/src/material/material.ts +156 -0
- package/core/src/material/variants.ts +61 -0
- package/core/src/math/mat4.ts +202 -0
- package/core/src/math/perlin-noise-2d.ts +77 -0
- package/core/src/math/perlin-noise-3d.ts +110 -0
- package/core/src/math/quat.ts +33 -0
- package/core/src/math/rng.ts +29 -0
- package/core/src/math/trig.ts +47 -0
- package/core/src/math/value-noise.ts +67 -0
- package/core/src/mesh/builder.ts +177 -0
- package/core/src/mesh/csg.ts +312 -0
- package/core/src/mesh/greased-line.ts +145 -0
- package/core/src/mesh/mesh.ts +17 -0
- package/core/src/mesh/parametric.ts +501 -0
- package/core/src/mesh/skinning.ts +67 -0
- package/core/src/mesh/vertex-layout.ts +91 -0
- package/core/src/module/cross-thread.ts +163 -0
- package/core/src/module/diagnostics.ts +123 -0
- package/core/src/module/host.ts +451 -0
- package/core/src/module/index.ts +22 -0
- package/core/src/module/module.ts +109 -0
- package/core/src/module/registry.ts +94 -0
- package/core/src/module/renderer-module.ts +247 -0
- package/core/src/module/ts-loader.ts +26 -0
- package/core/src/particles/compute-pass.ts +378 -0
- package/core/src/particles/emitter.ts +156 -0
- package/core/src/particles/index.ts +11 -0
- package/core/src/particles/particle-data.ts +47 -0
- package/core/src/particles/render-pass.ts +365 -0
- package/core/src/particles/simulator.ts +264 -0
- package/core/src/particles/system.ts +193 -0
- package/core/src/physics/body.ts +59 -0
- package/core/src/physics/ccd-heuristic.ts +55 -0
- package/core/src/physics/character.ts +282 -0
- package/core/src/physics/collider.ts +55 -0
- package/core/src/physics/collision-system.ts +91 -0
- package/core/src/physics/events.ts +133 -0
- package/core/src/physics/index.ts +34 -0
- package/core/src/physics/interface.ts +473 -0
- package/core/src/physics/interpolation-buffer.ts +192 -0
- package/core/src/physics/lifecycle.ts +87 -0
- package/core/src/physics/load-shedder.ts +164 -0
- package/core/src/physics/physics-accumulator.ts +101 -0
- package/core/src/physics/physics-system.ts +240 -0
- package/core/src/physics/ragdoll-presets.ts +272 -0
- package/core/src/physics/ragdoll-system.ts +220 -0
- package/core/src/physics/ragdoll.ts +182 -0
- package/core/src/physics/raycast.ts +143 -0
- package/core/src/physics/realm-manager.ts +499 -0
- package/core/src/physics/realm-worker-pool.ts +191 -0
- package/core/src/physics/realm-worker.ts +102 -0
- package/core/src/physics/realm.ts +189 -0
- package/core/src/physics/registry.ts +52 -0
- package/core/src/physics/safety.ts +247 -0
- package/core/src/physics/snapshot-manager.ts +141 -0
- package/core/src/physics/worker-protocol.ts +30 -0
- package/core/src/platform/bun-preload.ts +169 -0
- package/core/src/platform/css-stub.ts +4 -0
- package/core/src/platform/glob-polyfill.ts +124 -0
- package/core/src/platform/hidpi.ts +57 -0
- package/core/src/platform/render-surface.ts +77 -0
- package/core/src/platform/rpc.ts +98 -0
- package/core/src/platform/runtime.ts +123 -0
- package/core/src/plugin/context.ts +364 -0
- package/core/src/plugin/diagnostics.ts +61 -0
- package/core/src/plugin/discovery.ts +186 -0
- package/core/src/plugin/extension-loaders.ts +172 -0
- package/core/src/plugin/host.ts +659 -0
- package/core/src/plugin/index.ts +106 -0
- package/core/src/plugin/loader-asset.ts +124 -0
- package/core/src/plugin/loader-quickjs.ts +84 -0
- package/core/src/plugin/loader-wasm.ts +411 -0
- package/core/src/plugin/loader-worker.ts +261 -0
- package/core/src/plugin/manifest.ts +653 -0
- package/core/src/plugin/material-registry.ts +65 -0
- package/core/src/plugin/mcp-tools.ts +165 -0
- package/core/src/plugin/permissions.ts +175 -0
- package/core/src/plugin/quickjs-bridge.ts +294 -0
- package/core/src/plugin/registry.ts +107 -0
- package/core/src/plugin/sandbox-shim.ts +86 -0
- package/core/src/plugin/sandbox-worker.ts +215 -0
- package/core/src/plugin/wasm-abi.ts +152 -0
- package/core/src/plugin/wasm-worker.ts +229 -0
- package/core/src/plugin/workshop.ts +169 -0
- package/core/src/profiling/event-loop.ts +180 -0
- package/core/src/profiling/index.ts +69 -0
- package/core/src/profiling/iops/idb-patch.ts +242 -0
- package/core/src/profiling/iops/opfs-patch.ts +276 -0
- package/core/src/profiling/profiling-sab.ts +839 -0
- package/core/src/profiling/task-latency.ts +134 -0
- package/core/src/profiling/thread-metrics.ts +126 -0
- package/core/src/profiling/trace-event-writer.ts +205 -0
- package/core/src/profiling/warnings.ts +423 -0
- package/core/src/profiling/worker-prelude.ts +376 -0
- package/core/src/render/bind-group.ts +38 -0
- package/core/src/render/bindless/bindless-struct.ts +42 -0
- package/core/src/render/bindless/bindless.wgsl.ts +167 -0
- package/core/src/render/bindless/frame-bindings.ts +176 -0
- package/core/src/render/bindless/index.ts +10 -0
- package/core/src/render/bindless/material-manager.ts +257 -0
- package/core/src/render/bindless/texture-registry.ts +609 -0
- package/core/src/render/buffer.ts +82 -0
- package/core/src/render/camera-2d.ts +217 -0
- package/core/src/render/camera-controller.ts +440 -0
- package/core/src/render/camera.ts +409 -0
- package/core/src/render/canvas-resize-watcher.ts +46 -0
- package/core/src/render/compute-kernel.ts +172 -0
- package/core/src/render/constants.ts +2 -0
- package/core/src/render/device.ts +111 -0
- package/core/src/render/frame-graph.ts +768 -0
- package/core/src/render/frustum.ts +185 -0
- package/core/src/render/fullscreen-vs.ts +25 -0
- package/core/src/render/g-buffer.ts +186 -0
- package/core/src/render/game-renderer.ts +1452 -0
- package/core/src/render/gpu-driven/cull-pass.ts +394 -0
- package/core/src/render/gpu-driven/hzb.ts +215 -0
- package/core/src/render/gpu-driven/index.ts +7 -0
- package/core/src/render/gpu-driven/indirect-draw-pass.ts +324 -0
- package/core/src/render/gpu-driven/instance-buffer.ts +178 -0
- package/core/src/render/gpu-driven/mesh-table.ts +175 -0
- package/core/src/render/gpu-driven/passes/hzb-build-pass.ts +47 -0
- package/core/src/render/gpu-utils.ts +96 -0
- package/core/src/render/hdr.ts +88 -0
- package/core/src/render/hot-reload.ts +295 -0
- package/core/src/render/ibl-bind-group.ts +266 -0
- package/core/src/render/ibl.ts +180 -0
- package/core/src/render/index.ts +163 -0
- package/core/src/render/input-manager.ts +259 -0
- package/core/src/render/lighting/area-light.ts +196 -0
- package/core/src/render/lighting/cluster-grid.ts +288 -0
- package/core/src/render/lighting/cluster-types.ts +193 -0
- package/core/src/render/lighting/ies-parser.ts +162 -0
- package/core/src/render/lighting.ts +454 -0
- package/core/src/render/material-types/depth.wgsl +44 -0
- package/core/src/render/material-types/line.wgsl +50 -0
- package/core/src/render/material-types/matcap.wgsl +51 -0
- package/core/src/render/material-types/normal.wgsl +41 -0
- package/core/src/render/material-types/physical.wgsl +152 -0
- package/core/src/render/material-types/sprite.wgsl +46 -0
- package/core/src/render/material-types/sss.wgsl +79 -0
- package/core/src/render/material-types/toon.wgsl +79 -0
- package/core/src/render/passes/README.md +121 -0
- package/core/src/render/passes/atmosphere.ts +152 -0
- package/core/src/render/passes/cluster-lighting-pass.ts +476 -0
- package/core/src/render/passes/csm.ts +408 -0
- package/core/src/render/passes/cubemap-capture.ts +114 -0
- package/core/src/render/passes/debug-viz.ts +278 -0
- package/core/src/render/passes/debug.ts +538 -0
- package/core/src/render/passes/decal-mesh.ts +111 -0
- package/core/src/render/passes/decal-pass.ts +377 -0
- package/core/src/render/passes/deferred-lighting.ts +434 -0
- package/core/src/render/passes/depth-prepass.ts +163 -0
- package/core/src/render/passes/dome-360.ts +83 -0
- package/core/src/render/passes/fluid-render.ts +137 -0
- package/core/src/render/passes/gi-types.ts +118 -0
- package/core/src/render/passes/graph-compute.ts +300 -0
- package/core/src/render/passes/greased-line-pass.ts +232 -0
- package/core/src/render/passes/morph-target.ts +40 -0
- package/core/src/render/passes/opaque.ts +1028 -0
- package/core/src/render/passes/point-light-shadow.ts +266 -0
- package/core/src/render/passes/procedural-texture.ts +185 -0
- package/core/src/render/passes/reflection-probe.ts +140 -0
- package/core/src/render/passes/rsm-pass.ts +248 -0
- package/core/src/render/passes/sdf-text.ts +155 -0
- package/core/src/render/passes/shadow-map.ts +163 -0
- package/core/src/render/passes/shadow.ts +220 -0
- package/core/src/render/passes/skinning-vs.ts +121 -0
- package/core/src/render/passes/skinning.ts +326 -0
- package/core/src/render/passes/sky-dome.ts +404 -0
- package/core/src/render/passes/skybox.ts +162 -0
- package/core/src/render/passes/spot-light-shadow.ts +273 -0
- package/core/src/render/passes/terrain.ts +326 -0
- package/core/src/render/passes/transparent.ts +353 -0
- package/core/src/render/passes/underwater-fog.ts +185 -0
- package/core/src/render/passes/video-texture.ts +79 -0
- package/core/src/render/passes/volumetric-pass.ts +312 -0
- package/core/src/render/passes/volumetric-types.ts +171 -0
- package/core/src/render/passes/water.ts +753 -0
- package/core/src/render/pbr.ts +296 -0
- package/core/src/render/pipeline.ts +52 -0
- package/core/src/render/render-pass.ts +30 -0
- package/core/src/render/renderer-input-bus.ts +220 -0
- package/core/src/render/renderer-module-host.ts +623 -0
- package/core/src/render/resource-tracker.ts +67 -0
- package/core/src/render/sab-canvas-overlay.ts +122 -0
- package/core/src/render/shader-validator.ts +200 -0
- package/core/src/render/shaders/debug/debug-line.wgsl +27 -0
- package/core/src/render/shaders/debug/debug-point.wgsl +30 -0
- package/core/src/render/shaders/debug/debug-text.wgsl +42 -0
- package/core/src/render/shaders/debug-viz/aabb.wgsl +20 -0
- package/core/src/render/shaders/debug-viz/depth.wgsl +29 -0
- package/core/src/render/shaders/debug-viz/lod.wgsl +38 -0
- package/core/src/render/shaders/debug-viz/normals.wgsl +30 -0
- package/core/src/render/shaders/debug-viz/overdraw.wgsl +21 -0
- package/core/src/render/shaders/debug-viz/tangents.wgsl +29 -0
- package/core/src/render/shaders/debug-viz/wireframe.wgsl +33 -0
- package/core/src/render/shaders/shadow-chunks.ts +135 -0
- package/core/src/render/skybox.ts +146 -0
- package/core/src/render/splitscreen.ts +85 -0
- package/core/src/render/surface.ts +78 -0
- package/core/src/render/tonemap.ts +180 -0
- package/core/src/render/tracked-render-pass.ts +158 -0
- package/core/src/render/viewport.ts +109 -0
- package/core/src/render/wgsl-hmr.ts +70 -0
- package/core/src/sab/core-input-channel.ts +23 -0
- package/core/src/sab/define.ts +239 -0
- package/core/src/sab/errors.ts +76 -0
- package/core/src/sab/game-input.ts +343 -0
- package/core/src/sab/gamepad-devices.ts +259 -0
- package/core/src/sab/grid-buffer.ts +117 -0
- package/core/src/sab/input.ts +55 -0
- package/core/src/sab/layout.ts +219 -0
- package/core/src/sab/multi-input-channel.ts +50 -0
- package/core/src/sab/record-buffer.ts +155 -0
- package/core/src/sab/sab-polyfill.ts +56 -0
- package/core/src/sab/sim-channel.ts +453 -0
- package/core/src/sab/slot-buffer.ts +190 -0
- package/core/src/sab/types.ts +158 -0
- package/core/src/safety/bounds.ts +76 -0
- package/core/src/safety/json.ts +80 -0
- package/core/src/safety/path.ts +167 -0
- package/core/src/save/binary-format.ts +117 -0
- package/core/src/save/game-save-system.ts +161 -0
- package/core/src/save/grid-save-system.ts +158 -0
- package/core/src/save/migrate.ts +44 -0
- package/core/src/save/migration-registry.ts +94 -0
- package/core/src/save/persist-types.ts +208 -0
- package/core/src/save/schema.ts +46 -0
- package/core/src/save/serializer.ts +180 -0
- package/core/src/scene/builder.ts +65 -0
- package/core/src/scene/camera.ts +142 -0
- package/core/src/scene/checkpoint.ts +109 -0
- package/core/src/scene/game-loop.ts +198 -0
- package/core/src/scene/index.ts +24 -0
- package/core/src/scene/layer.ts +6 -0
- package/core/src/scene/prefab.ts +145 -0
- package/core/src/scene/scene-manager.ts +157 -0
- package/core/src/scene/scene.ts +192 -0
- package/core/src/scene/sky.ts +256 -0
- package/core/src/scene/spatial-grid.ts +176 -0
- package/core/src/scene/streaming.ts +304 -0
- package/core/src/scene/vegetation.ts +163 -0
- package/core/src/scene/world.ts +89 -0
- package/core/src/scripting/binding.ts +129 -0
- package/core/src/scripting/hot-reload.ts +144 -0
- package/core/src/scripting/script.ts +99 -0
- package/core/src/sim/hot-reload-pipeline.ts +49 -0
- package/core/src/sim/sim-state-helper.ts +47 -0
- package/core/src/sim/transient-state-registry.ts +77 -0
- package/core/src/sim/types.ts +113 -0
- package/core/src/sim/worker-loop.ts +222 -0
- package/core/src/stores/game-store.ts +158 -0
- package/core/src/stores/hot-reload-store.ts +44 -0
- package/core/src/telemetry/collector.ts +410 -0
- package/core/src/telemetry/gc-controller.ts +453 -0
- package/core/src/telemetry/gc-profiler.ts +126 -0
- package/core/src/telemetry/gc-tracker.ts +69 -0
- package/core/src/telemetry/gpu-profiler.ts +703 -0
- package/core/src/telemetry/gpu-resource-tracker.ts +285 -0
- package/core/src/telemetry/gpu-timer-pool.ts +234 -0
- package/core/src/telemetry/gpu-timer.ts +123 -0
- package/core/src/telemetry/gpu-timestamp-gate.ts +21 -0
- package/core/src/telemetry/reporter.ts +62 -0
- package/core/src/types/engine-types.ts +129 -0
- package/core/src/ui-font-scale.ts +112 -0
- package/core/src/util/broad-phase-grid.ts +82 -0
- package/core/src/util/feature-log.ts +220 -0
- package/core/src/util/index.ts +17 -0
- package/core/src/util/logger.ts +604 -0
- package/core/src/webgpu-destroy-augmentation.d.ts +25 -0
- package/core/src/worker/base-worker-host.ts +183 -0
- package/core/src/worker/buffer-sync.ts +550 -0
- package/core/src/worker/crash-recovery.ts +201 -0
- package/core/src/worker/entity-sim-worker-host.ts +190 -0
- package/core/src/worker/instrumented-worker-host.ts +171 -0
- package/core/src/worker/rpc.ts +253 -0
- package/core/src/worker/sim-worker-base.ts +913 -0
- package/core/src/worker/sim-worker-host.ts +358 -0
- package/core/src/worker/task-pool.ts +256 -0
- package/core/src/worker/task-worker.ts +83 -0
- package/libraries/asset-browser/src/index.ts +11 -0
- package/libraries/asset-browser/src/software-thumbnails.ts +949 -0
- package/libraries/audio-kira/README.md +17 -0
- package/libraries/audio-kira/native/Cargo.toml +14 -0
- package/libraries/audio-kira/native/src/engine.rs +27 -0
- package/libraries/audio-kira/native/src/lib.rs +185 -0
- package/libraries/audio-kira/native/src/mixer.rs +11 -0
- package/libraries/audio-kira/native/src/source.rs +80 -0
- package/libraries/audio-kira/src/backend.ts +469 -0
- package/libraries/audio-kira/src/index.ts +8 -0
- package/libraries/audio-kira/src/library.ts +82 -0
- package/libraries/blitz-ui/native/Cargo.toml +33 -0
- package/libraries/blitz-ui/native/assets/DejaVuSans.woff2 +0 -0
- package/libraries/blitz-ui/native/src/bridge.rs +100 -0
- package/libraries/blitz-ui/native/src/fonts.rs +20 -0
- package/libraries/blitz-ui/native/src/input.rs +80 -0
- package/libraries/blitz-ui/native/src/lib.rs +96 -0
- package/libraries/blitz-ui/native/src/shell.rs +301 -0
- package/libraries/blitz-ui/native-osr/Cargo.toml +24 -0
- package/libraries/blitz-ui/native-osr/src/events.rs +237 -0
- package/libraries/blitz-ui/native-osr/src/lib.rs +846 -0
- package/libraries/blitz-ui/native-osr/src/mutate.rs +385 -0
- package/libraries/blitz-ui/native-osr/src/resources.rs +45 -0
- package/libraries/blitz-ui/native-osr-spike.ts +70 -0
- package/libraries/blitz-ui/src/index.ts +15 -0
- package/libraries/blitz-ui/src/native-module.ts +221 -0
- package/libraries/blitz-ui/src/native-osr-ffi.ts +423 -0
- package/libraries/blitz-ui/src/router.ts +101 -0
- package/libraries/blitz-ui/src/shaders/ui-blit.wgsl.ts +35 -0
- package/libraries/blitz-ui/src/ui-blit-pass.ts +116 -0
- package/libraries/character/README.md +25 -0
- package/libraries/character/src/character-animator.ts +374 -0
- package/libraries/character/src/index.ts +24 -0
- package/libraries/character/src/loader.ts +375 -0
- package/libraries/character/src/locomotion.ts +124 -0
- package/libraries/character/src/preview.ts +287 -0
- package/libraries/devtools/README.md +51 -0
- package/libraries/devtools/native/Cargo.toml +12 -0
- package/libraries/devtools/native/src/lib.rs +1646 -0
- package/libraries/devtools/native/src/panels/console.rs +338 -0
- package/libraries/devtools/native/src/panels/dom_tree.rs +47 -0
- package/libraries/devtools/native/src/panels/generic.rs +317 -0
- package/libraries/devtools/native/src/panels/gpu.rs +107 -0
- package/libraries/devtools/native/src/panels/input.rs +88 -0
- package/libraries/devtools/native/src/panels/mod.rs +154 -0
- package/libraries/devtools/native/src/panels/perf_metrics.rs +91 -0
- package/libraries/devtools/native/src/panels/perf_recorder.rs +158 -0
- package/libraries/devtools/native/src/panels/scene.rs +72 -0
- package/libraries/devtools/native/src/state.rs +394 -0
- package/libraries/devtools/src/cdp-bridge.ts +349 -0
- package/libraries/devtools/src/collectors.ts +346 -0
- package/libraries/devtools/src/egui-ffi.ts +804 -0
- package/libraries/devtools/src/egui-renderer.ts +407 -0
- package/libraries/devtools/src/host.ts +363 -0
- package/libraries/devtools/src/index.ts +40 -0
- package/libraries/devtools/src/mirror.ts +794 -0
- package/libraries/devtools/src/native-providers.ts +744 -0
- package/libraries/devtools/src/verify.ts +167 -0
- package/libraries/devtools/src/web/browser.ts +102 -0
- package/libraries/devtools/src/web/host.ts +212 -0
- package/libraries/devtools/src/web/mirror.ts +430 -0
- package/libraries/devtools/src/web/server.ts +198 -0
- package/libraries/entities/README.md +17 -0
- package/libraries/entities/src/index.ts +4 -0
- package/libraries/entities/src/library.ts +64 -0
- package/libraries/entities/src/model-renderer.ts +1345 -0
- package/libraries/entities/src/shaders/model.wgsl +375 -0
- package/libraries/gamepad/src/feed-input.ts +83 -0
- package/libraries/gamepad/src/filters.ts +49 -0
- package/libraries/gamepad/src/glyphs.ts +93 -0
- package/libraries/gamepad/src/hub.ts +206 -0
- package/libraries/gamepad/src/index.ts +22 -0
- package/libraries/gamepad/src/library.ts +68 -0
- package/libraries/gamepad/src/source-sab.ts +70 -0
- package/libraries/gamepad/src/sysfs-enrich.ts +138 -0
- package/libraries/gaussian-splats/README.md +25 -0
- package/libraries/gaussian-splats/src/gpu-sort.ts +595 -0
- package/libraries/gaussian-splats/src/index.ts +31 -0
- package/libraries/gaussian-splats/src/library.ts +60 -0
- package/libraries/gaussian-splats/src/parser.ts +382 -0
- package/libraries/gaussian-splats/src/renderer.ts +427 -0
- package/libraries/gaussian-splats/src/sh-eval.ts +211 -0
- package/libraries/gaussian-splats/src/sorter.ts +110 -0
- package/libraries/gaussian-splats/src/tile-raster.ts +473 -0
- package/libraries/gpu-kernels/README.md +154 -0
- package/libraries/gpu-kernels/src/index.ts +11 -0
- package/libraries/gpu-kernels/src/kernel.ts +696 -0
- package/libraries/gpu-kernels/src/transpile.ts +958 -0
- package/libraries/html-ui-kit/src/behaviors.ts +250 -0
- package/libraries/html-ui-kit/src/components.ts +309 -0
- package/libraries/html-ui-kit/src/index.ts +30 -0
- package/libraries/html-ui-kit/src/nav/nav-controller.ts +238 -0
- package/libraries/html-ui-kit/src/nav/nav-input.ts +145 -0
- package/libraries/html-ui-kit/src/nav/osk.ts +153 -0
- package/libraries/html-ui-kit/src/nav/router.ts +167 -0
- package/libraries/html-ui-kit/src/nav/vcursor.ts +203 -0
- package/libraries/html-ui-kit/src/ten-foot.ts +182 -0
- package/libraries/html-ui-kit/src/theme.ts +238 -0
- package/libraries/lighting/README.md +20 -0
- package/libraries/lighting/src/index.ts +5 -0
- package/libraries/lighting/src/library.ts +66 -0
- package/libraries/lighting/src/light-system.ts +452 -0
- package/libraries/lighting/src/lighting-system.ts +68 -0
- package/libraries/lighting/src/shaders/light-debug.wgsl +30 -0
- package/libraries/marching-cubes/README.md +25 -0
- package/libraries/marching-cubes/src/chunked-field.ts +177 -0
- package/libraries/marching-cubes/src/deformation.ts +63 -0
- package/libraries/marching-cubes/src/generator.ts +407 -0
- package/libraries/marching-cubes/src/index.ts +32 -0
- package/libraries/marching-cubes/src/library.ts +80 -0
- package/libraries/marching-cubes/src/lod.ts +122 -0
- package/libraries/marching-cubes/src/marching-tetrahedra.ts +252 -0
- package/libraries/marching-cubes/src/sab.ts +45 -0
- package/libraries/marching-cubes/src/streaming-config.ts +48 -0
- package/libraries/marching-cubes/src/streaming-manager.ts +686 -0
- package/libraries/marching-cubes/src/tables.ts +323 -0
- package/libraries/marching-cubes/src/types.ts +18 -0
- package/libraries/models/README.md +25 -0
- package/libraries/models/src/bake-node-transforms.ts +171 -0
- package/libraries/models/src/codecs/basisu-codec.ts +180 -0
- package/libraries/models/src/codecs/draco-codec.ts +372 -0
- package/libraries/models/src/codecs/draco3dgltf.d.ts +40 -0
- package/libraries/models/src/codecs/index.ts +22 -0
- package/libraries/models/src/codecs/meshopt-codec.ts +91 -0
- package/libraries/models/src/codecs/registry.ts +221 -0
- package/libraries/models/src/dae.ts +753 -0
- package/libraries/models/src/default-part-selection.ts +56 -0
- package/libraries/models/src/dxf.ts +200 -0
- package/libraries/models/src/fbx/index.ts +457 -0
- package/libraries/models/src/fbx/interpreter/animation.ts +431 -0
- package/libraries/models/src/fbx/interpreter/blend-shapes.ts +323 -0
- package/libraries/models/src/fbx/interpreter/connections.ts +166 -0
- package/libraries/models/src/fbx/interpreter/diagnostics.ts +65 -0
- package/libraries/models/src/fbx/interpreter/geometry.ts +535 -0
- package/libraries/models/src/fbx/interpreter/global-settings.ts +92 -0
- package/libraries/models/src/fbx/interpreter/materials.ts +265 -0
- package/libraries/models/src/fbx/interpreter/nodes.ts +197 -0
- package/libraries/models/src/fbx/interpreter/skeleton.ts +476 -0
- package/libraries/models/src/fbx/parsers/binary-parser.ts +244 -0
- package/libraries/models/src/fbx/types.ts +119 -0
- package/libraries/models/src/gltf-extensions.ts +196 -0
- package/libraries/models/src/gltf.ts +935 -0
- package/libraries/models/src/index.ts +87 -0
- package/libraries/models/src/library.ts +75 -0
- package/libraries/models/src/loader.ts +265 -0
- package/libraries/models/src/material-adapter.ts +108 -0
- package/libraries/models/src/mini-xml.ts +174 -0
- package/libraries/models/src/normalize.ts +216 -0
- package/libraries/models/src/obj.ts +264 -0
- package/libraries/models/src/off.ts +95 -0
- package/libraries/models/src/ply.ts +348 -0
- package/libraries/models/src/pose-sampler.ts +219 -0
- package/libraries/models/src/sidecar/blender-extras.ts +37 -0
- package/libraries/models/src/sidecar/ddmeta.ts +114 -0
- package/libraries/models/src/sidecar/godot-import.ts +86 -0
- package/libraries/models/src/sidecar/resolver.ts +145 -0
- package/libraries/models/src/sidecar/types.ts +11 -0
- package/libraries/models/src/sidecar/unity-meta.ts +61 -0
- package/libraries/models/src/skeleton-synthesis.ts +132 -0
- package/libraries/models/src/stl.ts +149 -0
- package/libraries/models/src/threeds.ts +363 -0
- package/libraries/models/src/threemf.ts +201 -0
- package/libraries/models/src/types.ts +188 -0
- package/libraries/models/src/vtk.ts +218 -0
- package/libraries/models/test-fixtures/fbx/LICENSE-notice.txt +24 -0
- package/libraries/models/test-fixtures/fbx/README.md +46 -0
- package/libraries/models/test-fixtures/fbx/m01_cube_phong.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m02_geo_ngons.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m03_normals.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m04_material_properties.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m05_external_diffuse.png +0 -0
- package/libraries/models/test-fixtures/fbx/m05_textures.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m06_uv_transform.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m07_multimaterial.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m08_transforms.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m09_skinning.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m10_morph.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m11_node_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m12_skeletal_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m13_morph_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m14_multiclip.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m15_camera_lights.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_axis_yup.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_axis_zup.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_units_254.fbx +0 -0
- package/libraries/navmesh/README.md +23 -0
- package/libraries/navmesh/src/crowd-system.ts +240 -0
- package/libraries/navmesh/src/debug-viz.ts +74 -0
- package/libraries/navmesh/src/index.ts +15 -0
- package/libraries/navmesh/src/library.ts +64 -0
- package/libraries/navmesh/src/navmesh-generator.ts +407 -0
- package/libraries/navmesh/src/navmesh.ts +159 -0
- package/libraries/navmesh/src/pathfinder.ts +252 -0
- package/libraries/navmesh/src/types.ts +63 -0
- package/libraries/networking/README.md +25 -0
- package/libraries/networking/src/authority.ts +121 -0
- package/libraries/networking/src/connection.ts +139 -0
- package/libraries/networking/src/discovery.ts +373 -0
- package/libraries/networking/src/enhanced-replication.ts +355 -0
- package/libraries/networking/src/epic-adapter.ts +129 -0
- package/libraries/networking/src/index.ts +45 -0
- package/libraries/networking/src/library.ts +69 -0
- package/libraries/networking/src/lobby.ts +108 -0
- package/libraries/networking/src/platform-adapter.ts +163 -0
- package/libraries/networking/src/rcs-adapter.ts +127 -0
- package/libraries/networking/src/remote-input.ts +188 -0
- package/libraries/networking/src/replication.ts +327 -0
- package/libraries/networking/src/rpc.ts +188 -0
- package/libraries/networking/src/session.ts +173 -0
- package/libraries/networking/src/transport.ts +201 -0
- package/libraries/networking/src/webrtc.ts +323 -0
- package/libraries/pathfinding-2d/README.md +15 -0
- package/libraries/pathfinding-2d/src/astar-grid.ts +312 -0
- package/libraries/pathfinding-2d/src/index.ts +11 -0
- package/libraries/persistence/README.md +25 -0
- package/libraries/persistence/src/autosave-manager.ts +112 -0
- package/libraries/persistence/src/binary-record-store.ts +244 -0
- package/libraries/persistence/src/browser.ts +51 -0
- package/libraries/persistence/src/file-save-store.ts +553 -0
- package/libraries/persistence/src/hash-utils.ts +59 -0
- package/libraries/persistence/src/index.ts +57 -0
- package/libraries/persistence/src/library.ts +74 -0
- package/libraries/persistence/src/mock-opfs.ts +141 -0
- package/libraries/persistence/src/node-fs-directory-handle.ts +161 -0
- package/libraries/persistence/src/opfs-save-store.ts +745 -0
- package/libraries/persistence/src/save-worker-proxy.ts +127 -0
- package/libraries/persistence/src/save-worker.ts +99 -0
- package/libraries/persistence/src/xxh3-ts.d.ts +10 -0
- package/libraries/physics-native/README.md +21 -0
- package/libraries/physics-native/native/Cargo.toml +11 -0
- package/libraries/physics-native/native/src/body.rs +403 -0
- package/libraries/physics-native/native/src/character.rs +226 -0
- package/libraries/physics-native/native/src/collider.rs +299 -0
- package/libraries/physics-native/native/src/joint.rs +81 -0
- package/libraries/physics-native/native/src/lib.rs +830 -0
- package/libraries/physics-native/native/src/query.rs +261 -0
- package/libraries/physics-native/native/src/realm.rs +33 -0
- package/libraries/physics-native/native/src/step.rs +54 -0
- package/libraries/physics-native/native/src/world.rs +148 -0
- package/libraries/physics-native/src/backend.ts +1046 -0
- package/libraries/physics-native/src/broadphase.ts +101 -0
- package/libraries/physics-native/src/ffi-spike-worker.ts +71 -0
- package/libraries/physics-native/src/index.ts +11 -0
- package/libraries/physics-native/src/library.ts +79 -0
- package/libraries/physics-native/src/narrowphase-shapes.ts +420 -0
- package/libraries/physics-native/src/narrowphase.ts +123 -0
- package/libraries/physics-native/src/solver.ts +141 -0
- package/libraries/physics-native/src/types.ts +76 -0
- package/libraries/physics-rapier/README.md +21 -0
- package/libraries/physics-rapier/bench/ffi-bench.ts +79 -0
- package/libraries/physics-rapier/bench/wasm-baseline.ts +90 -0
- package/libraries/physics-rapier/src/api.ts +452 -0
- package/libraries/physics-rapier/src/auto-backend.ts +59 -0
- package/libraries/physics-rapier/src/backend.ts +1207 -0
- package/libraries/physics-rapier/src/bulk-ops.ts +68 -0
- package/libraries/physics-rapier/src/ffi-backend.ts +21 -0
- package/libraries/physics-rapier/src/ffi-lib.ts +597 -0
- package/libraries/physics-rapier/src/index.ts +12 -0
- package/libraries/physics-rapier/src/library.ts +107 -0
- package/libraries/physics-rapier/src/rapier-backend.ts +1052 -0
- package/libraries/physics-rapier/src/realm-worker-script.ts +237 -0
- package/libraries/postfx/README.md +17 -0
- package/libraries/postfx/src/index.ts +19 -0
- package/libraries/postfx/src/library.ts +337 -0
- package/libraries/postfx/src/post-process-stack.ts +1752 -0
- package/libraries/postfx/src/shaders/post-process/afterimage.wgsl +14 -0
- package/libraries/postfx/src/shaders/post-process/ascii.wgsl +24 -0
- package/libraries/postfx/src/shaders/post-process/blit.wgsl +12 -0
- package/libraries/postfx/src/shaders/post-process/bloom-blur.wgsl +21 -0
- package/libraries/postfx/src/shaders/post-process/bloom-bright.wgsl +15 -0
- package/libraries/postfx/src/shaders/post-process/bloom-composite.wgsl +14 -0
- package/libraries/postfx/src/shaders/post-process/bloom-downsample.wgsl +60 -0
- package/libraries/postfx/src/shaders/post-process/bloom-soft.wgsl +46 -0
- package/libraries/postfx/src/shaders/post-process/bloom-upsample.wgsl +51 -0
- package/libraries/postfx/src/shaders/post-process/channel-mixer.wgsl +36 -0
- package/libraries/postfx/src/shaders/post-process/chromatic-aberration.wgsl +37 -0
- package/libraries/postfx/src/shaders/post-process/dithering.wgsl +64 -0
- package/libraries/postfx/src/shaders/post-process/dof.wgsl +131 -0
- package/libraries/postfx/src/shaders/post-process/edges.wgsl +45 -0
- package/libraries/postfx/src/shaders/post-process/fullscreen-vs.wgsl +15 -0
- package/libraries/postfx/src/shaders/post-process/fxaa.wgsl +102 -0
- package/libraries/postfx/src/shaders/post-process/gaussian-blur.wgsl +37 -0
- package/libraries/postfx/src/shaders/post-process/glow-blur.wgsl +33 -0
- package/libraries/postfx/src/shaders/post-process/glow-composite.wgsl +25 -0
- package/libraries/postfx/src/shaders/post-process/grain.wgsl +37 -0
- package/libraries/postfx/src/shaders/post-process/halftone.wgsl +80 -0
- package/libraries/postfx/src/shaders/post-process/highlight-blur.wgsl +33 -0
- package/libraries/postfx/src/shaders/post-process/highlight-composite.wgsl +32 -0
- package/libraries/postfx/src/shaders/post-process/lens-distortion.wgsl +43 -0
- package/libraries/postfx/src/shaders/post-process/lens-flare.wgsl +68 -0
- package/libraries/postfx/src/shaders/post-process/lut.wgsl +30 -0
- package/libraries/postfx/src/shaders/post-process/motion-blur.wgsl +55 -0
- package/libraries/postfx/src/shaders/post-process/occluder-chunk.wgsl +28 -0
- package/libraries/postfx/src/shaders/post-process/outline.wgsl +51 -0
- package/libraries/postfx/src/shaders/post-process/pixelation.wgsl +47 -0
- package/libraries/postfx/src/shaders/post-process/sharpen.wgsl +32 -0
- package/libraries/postfx/src/shaders/post-process/sobel.wgsl +26 -0
- package/libraries/postfx/src/shaders/post-process/split-tone.wgsl +36 -0
- package/libraries/postfx/src/shaders/post-process/ssao-blur.wgsl +38 -0
- package/libraries/postfx/src/shaders/post-process/ssao-composite.wgsl +25 -0
- package/libraries/postfx/src/shaders/post-process/ssao.wgsl +147 -0
- package/libraries/postfx/src/shaders/post-process/ssr.wgsl +155 -0
- package/libraries/postfx/src/shaders/post-process/taa.wgsl +98 -0
- package/libraries/postfx/src/shaders/post-process/tonemap.wgsl +40 -0
- package/libraries/postfx/src/shaders/post-process/watercolor.wgsl +82 -0
- package/libraries/postfx/src/shaders/post-process/white-balance.wgsl +41 -0
- package/libraries/recast/README.md +21 -0
- package/libraries/recast/src/backend.ts +150 -0
- package/libraries/recast/src/crowd-system.ts +352 -0
- package/libraries/recast/src/index.ts +12 -0
- package/libraries/recast/src/library.ts +91 -0
- package/libraries/recast/src/types.ts +86 -0
- package/libraries/sand/README.md +25 -0
- package/libraries/sand/src/cell.ts +43 -0
- package/libraries/sand/src/fields.ts +29 -0
- package/libraries/sand/src/fluid-grid.ts +261 -0
- package/libraries/sand/src/grid-dims.ts +74 -0
- package/libraries/sand/src/grid-sim-buffer.ts +271 -0
- package/libraries/sand/src/index.ts +81 -0
- package/libraries/sand/src/library.ts +92 -0
- package/libraries/sand/src/materials.ts +492 -0
- package/libraries/sand/src/palette.ts +93 -0
- package/libraries/sand/src/particles.ts +185 -0
- package/libraries/sand/src/render/sand-grid-pass.ts +430 -0
- package/libraries/sand/src/rng.ts +51 -0
- package/libraries/sand/src/rules/builtin-rules.ts +132 -0
- package/libraries/sand/src/rules/rule-compiler.ts +162 -0
- package/libraries/sand/src/rules/rule-engine.ts +231 -0
- package/libraries/sand/src/rules/rule-types.ts +87 -0
- package/libraries/sand/src/sand-step-pool.ts +484 -0
- package/libraries/sand/src/sand-step-worker.ts +97 -0
- package/libraries/sand/src/sand-world.ts +3227 -0
- package/libraries/stickman/README.md +25 -0
- package/libraries/stickman/src/index.ts +25 -0
- package/libraries/stickman/src/library.ts +61 -0
- package/libraries/stickman/src/proportions.ts +45 -0
- package/libraries/stickman/src/shader.ts +117 -0
- package/libraries/stickman/src/skeleton.ts +197 -0
- package/libraries/stickman/src/stickman-pass.ts +255 -0
- package/libraries/stickman/src/thick-line.ts +123 -0
- package/libraries/surface-nets/README.md +25 -0
- package/libraries/surface-nets/src/chunked-field.ts +14 -0
- package/libraries/surface-nets/src/deformation.ts +45 -0
- package/libraries/surface-nets/src/index.ts +36 -0
- package/libraries/surface-nets/src/library.ts +59 -0
- package/libraries/surface-nets/src/lod.ts +7 -0
- package/libraries/surface-nets/src/sab.ts +4 -0
- package/libraries/surface-nets/src/streaming-config.ts +5 -0
- package/libraries/surface-nets/src/streaming-manager.ts +15 -0
- package/libraries/surface-nets/src/surface-nets.ts +334 -0
- package/libraries/surface-nets/src/types.ts +23 -0
- package/libraries/water/README.md +25 -0
- package/libraries/water/src/buoyancy.ts +103 -0
- package/libraries/water/src/index.ts +43 -0
- package/libraries/water/src/library.ts +85 -0
- package/libraries/water/src/shore-damping.ts +93 -0
- package/libraries/water/src/water-buffer.ts +89 -0
- package/libraries/water/src/water-chunks.ts +13 -0
- package/libraries/water/src/water-physics.ts +376 -0
- package/libraries/water/src/water-renderer.ts +809 -0
- package/libraries/water/src/water-sab.ts +161 -0
- package/libraries/water/src/wave-sources.ts +84 -0
- package/libraries/weather/README.md +20 -0
- package/libraries/weather/src/index.ts +9 -0
- package/libraries/weather/src/library.ts +72 -0
- package/libraries/weather/src/types.ts +64 -0
- package/libraries/weather/src/weather-blend.ts +112 -0
- package/libraries/weather/src/weather-system.ts +271 -0
- package/libraries/weatherfx/README.md +24 -0
- package/libraries/weatherfx/src/cloud-provider.ts +31 -0
- package/libraries/weatherfx/src/cloud-system.ts +372 -0
- package/libraries/weatherfx/src/index.ts +9 -0
- package/libraries/weatherfx/src/library.ts +58 -0
- package/libraries/weatherfx/src/particle-system.ts +434 -0
- package/libraries/weatherfx/src/shaders/cloud.wgsl +111 -0
- package/libraries/weatherfx/src/shaders/particle-compute.wgsl +187 -0
- package/libraries/weatherfx/src/shaders/particle-render.wgsl +95 -0
- package/lint-plugin.mjs +41 -0
- package/mcp/src/client.ts +703 -0
- package/mcp/src/engine-context.ts +186 -0
- package/mcp/src/http-transport.ts +471 -0
- package/mcp/src/index.ts +13 -0
- package/mcp/src/prompts/add-entity.ts +42 -0
- package/mcp/src/prompts/create-scene.ts +44 -0
- package/mcp/src/prompts/debug-frame.ts +39 -0
- package/mcp/src/prompts/index.ts +12 -0
- package/mcp/src/resources/asset-list.ts +32 -0
- package/mcp/src/resources/checkpoint-list.ts +33 -0
- package/mcp/src/resources/entity-state.ts +55 -0
- package/mcp/src/resources/gpu-info.ts +103 -0
- package/mcp/src/resources/index.ts +57 -0
- package/mcp/src/resources/performance.ts +37 -0
- package/mcp/src/resources/scene-tree.ts +43 -0
- package/mcp/src/server.ts +375 -0
- package/mcp/src/tools/animation.ts +118 -0
- package/mcp/src/tools/asset.ts +161 -0
- package/mcp/src/tools/audio.ts +96 -0
- package/mcp/src/tools/build.ts +143 -0
- package/mcp/src/tools/camera.ts +167 -0
- package/mcp/src/tools/checkpoint.ts +100 -0
- package/mcp/src/tools/component.ts +199 -0
- package/mcp/src/tools/debug.ts +300 -0
- package/mcp/src/tools/entity.ts +361 -0
- package/mcp/src/tools/inspect.ts +210 -0
- package/mcp/src/tools/lighting.ts +263 -0
- package/mcp/src/tools/material.ts +243 -0
- package/mcp/src/tools/mesh.ts +239 -0
- package/mcp/src/tools/physics.ts +266 -0
- package/mcp/src/tools/scene.ts +153 -0
- package/mcp/src/tools/script.ts +122 -0
- package/mcp/src/types.ts +92 -0
- package/mcp/src/undo-redo.ts +77 -0
- package/modules/camera-controls/README.md +20 -0
- package/modules/camera-controls/src/index.ts +161 -0
- package/modules/controller-ui/src/index.ts +164 -0
- package/modules/devtools/README.md +25 -0
- package/modules/devtools/src/api.ts +461 -0
- package/modules/devtools/src/asset-utils.ts +105 -0
- package/modules/devtools/src/bind-debug-store.ts +48 -0
- package/modules/devtools/src/data-bridge.ts +286 -0
- package/modules/devtools/src/debug-overlay.ts +238 -0
- package/modules/devtools/src/debug-raycast.ts +328 -0
- package/modules/devtools/src/debug-store.ts +92 -0
- package/modules/devtools/src/debug-view-descriptors.ts +59 -0
- package/modules/devtools/src/doctor-panel.ts +182 -0
- package/modules/devtools/src/gizmo-module.ts +136 -0
- package/modules/devtools/src/graph-model.ts +509 -0
- package/modules/devtools/src/grid-renderer.ts +157 -0
- package/modules/devtools/src/height-ruler-renderer.ts +216 -0
- package/modules/devtools/src/index.ts +81 -0
- package/modules/devtools/src/init.ts +261 -0
- package/modules/devtools/src/label-overlay.ts +154 -0
- package/modules/devtools/src/material-stats-panel.ts +188 -0
- package/modules/devtools/src/profiling-bridge.ts +377 -0
- package/modules/devtools/src/profiling-hooks.ts +98 -0
- package/modules/devtools/src/renderer-adapter.ts +76 -0
- package/modules/devtools/src/scene-inspector.ts +637 -0
- package/modules/devtools/src/scene-store.ts +320 -0
- package/modules/devtools/src/scene-sync.ts +116 -0
- package/modules/devtools/src/shaders/transform-gizmo.wgsl +38 -0
- package/modules/devtools/src/sim-stats-panel.ts +272 -0
- package/modules/devtools/src/sim-stats-provider.ts +105 -0
- package/modules/devtools/src/skeleton-renderer.ts +351 -0
- package/modules/devtools/src/transform-gizmo.ts +928 -0
- package/modules/devtools/src/types.ts +290 -0
- package/modules/devtools/src/worker-expose.ts +67 -0
- package/modules/devtools/src/worker-sync.ts +137 -0
- package/modules/editor/src/commands/component.ts +156 -0
- package/modules/editor/src/commands/entity.ts +287 -0
- package/modules/editor/src/commands/hierarchy.ts +83 -0
- package/modules/editor/src/commands/index.ts +61 -0
- package/modules/editor/src/commands/prefab.ts +163 -0
- package/modules/editor/src/commands/registry.ts +331 -0
- package/modules/editor/src/commands/scene-io.ts +211 -0
- package/modules/editor/src/commands/selection.ts +73 -0
- package/modules/editor/src/commands/transform.ts +91 -0
- package/modules/editor/src/ddscene.ts +267 -0
- package/modules/editor/src/document.ts +123 -0
- package/modules/editor/src/editor-context.ts +172 -0
- package/modules/editor/src/index.ts +41 -0
- package/modules/editor/src/mcp.ts +103 -0
- package/modules/editor/src/module.ts +148 -0
- package/modules/editor/src/selection.ts +70 -0
- package/modules/editor/src/shell.ts +456 -0
- package/modules/editor/src/viewport.ts +199 -0
- package/modules/html-ui/src/composite.ts +135 -0
- package/modules/html-ui/src/create-html-ui.ts +188 -0
- package/modules/html-ui/src/doc-backend.ts +343 -0
- package/modules/html-ui/src/host.ts +646 -0
- package/modules/html-ui/src/index.ts +13 -0
- package/modules/html-ui/src/jsx-dev-runtime.ts +12 -0
- package/modules/html-ui/src/jsx-runtime.ts +126 -0
- package/modules/html-ui/src/protocol.ts +88 -0
- package/modules/html-ui/src/ui-worker.ts +16 -0
- package/modules/mcp/README.md +18 -0
- package/modules/mcp/src/index.ts +27 -0
- package/modules/movement-2d/README.md +19 -0
- package/modules/movement-2d/src/grid-character.ts +455 -0
- package/modules/movement-2d/src/index.ts +45 -0
- package/modules/movement-3d/README.md +21 -0
- package/modules/movement-3d/src/character-motor.ts +230 -0
- package/modules/movement-3d/src/index.ts +46 -0
- package/modules/native-osr/src/index.ts +22 -0
- package/modules/native-osr/src/input-router.ts +401 -0
- package/modules/native-osr/src/native-osr-manager.ts +247 -0
- package/modules/native-osr/src/native-texture-source.ts +86 -0
- package/modules/native-osr/src/types.ts +112 -0
- package/modules/native-osr/src/world-space-ui-pass.ts +351 -0
- package/modules/sailing/README.md +18 -0
- package/modules/sailing/src/index.ts +51 -0
- package/modules/terrain/README.md +18 -0
- package/modules/terrain/src/index.ts +78 -0
- package/modules/vitals/README.md +17 -0
- package/modules/vitals/src/index.ts +43 -0
- package/modules/vitals/src/vitals.ts +265 -0
- package/modules/xr/README.md +25 -0
- package/modules/xr/src/camera-rig.ts +146 -0
- package/modules/xr/src/debug.ts +59 -0
- package/modules/xr/src/frame-loop.ts +197 -0
- package/modules/xr/src/index.ts +16 -0
- package/modules/xr/src/input.ts +208 -0
- package/modules/xr/src/layer.ts +140 -0
- package/modules/xr/src/session.ts +156 -0
- package/modules/xr/src/sim-module.ts +23 -0
- package/modules/xr/src/types.ts +32 -0
- package/modules/xr/src/webxr.d.ts +182 -0
- package/modules/xr/src/xr-module.ts +179 -0
- package/package.json +232 -3
- package/shader-graph/src/chunks.ts +349 -0
- package/shader-graph/src/compiler.ts +732 -0
- package/shader-graph/src/compute-compiler.ts +453 -0
- package/shader-graph/src/compute-graph.ts +112 -0
- package/shader-graph/src/compute-profiles.ts +54 -0
- package/shader-graph/src/graph.ts +52 -0
- package/shader-graph/src/index.ts +53 -0
- package/shader-graph/src/profiles.ts +407 -0
- package/shader-graph/src/validator.ts +48 -0
- package/shader-graph/src/wgsl-binding-validator.ts +143 -0
- package/shader-graph/src/wgsl-struct-validator.ts +245 -0
- package/shader-graph/src/wgsl-struct.ts +397 -0
- package/test/src/index.ts +7 -0
- package/test/src/vision-test.ts +369 -0
|
@@ -0,0 +1,1399 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Skeleton Animator — skeletal animation with crossfade blending, retargeting,
|
|
3
|
+
// procedural idle, and foot IK. Generic, game-agnostic.
|
|
4
|
+
// Uses AnimationPlayer internally for blending (bone masks, additive, root motion).
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
import { createLogger } from "../util/logger";
|
|
8
|
+
import type { AnimationEvent } from "./animation-event";
|
|
9
|
+
import type { KeyframeTrack, TrackPath } from "./clip";
|
|
10
|
+
import { AnimationClip } from "./clip";
|
|
11
|
+
import { AnimationPlayer } from "./player";
|
|
12
|
+
import type { Bone, SkeletonData } from "./skeleton";
|
|
13
|
+
import { Skeleton } from "./skeleton";
|
|
14
|
+
|
|
15
|
+
const log = createLogger();
|
|
16
|
+
|
|
17
|
+
export interface AnimationChannel {
|
|
18
|
+
targetNode: string;
|
|
19
|
+
path: "translation" | "rotation" | "scale" | "weights";
|
|
20
|
+
keyframeTimes: Float32Array;
|
|
21
|
+
keyframeValues: Float32Array;
|
|
22
|
+
interpolation: "LINEAR" | "STEP" | "CUBICSPLINE";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AnimationData {
|
|
26
|
+
name: string;
|
|
27
|
+
duration: number;
|
|
28
|
+
channels: AnimationChannel[];
|
|
29
|
+
sourceRestRotations?: Map<string, [number, number, number, number]>;
|
|
30
|
+
sourcePreRotations?: Map<string, [number, number, number, number]>;
|
|
31
|
+
sourceRestTranslations?: Map<string, [number, number, number]>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BoneData {
|
|
35
|
+
name: string;
|
|
36
|
+
nodeIndex: number;
|
|
37
|
+
parentIndex: number;
|
|
38
|
+
inverseBindMatrix: Float32Array;
|
|
39
|
+
restTranslation: [number, number, number];
|
|
40
|
+
restRotation: [number, number, number, number];
|
|
41
|
+
restScale: [number, number, number];
|
|
42
|
+
rootAncestorMatrix?: Float32Array;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SkinData {
|
|
46
|
+
bones: BoneData[];
|
|
47
|
+
boneNameToIndex: Map<string, number>;
|
|
48
|
+
/** Native bone-space up axis of the rig ("z" for UE-style rigs). */
|
|
49
|
+
skeletonUpAxis?: "y" | "z";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type AnimState = string;
|
|
53
|
+
|
|
54
|
+
// ── SkinData → SkeletonData conversion ──
|
|
55
|
+
|
|
56
|
+
export function skinDataToSkeletonData(skin: SkinData): SkeletonData {
|
|
57
|
+
const bones: Bone[] = skin.bones.map((b) => ({
|
|
58
|
+
name: b.name,
|
|
59
|
+
parentIndex: b.parentIndex,
|
|
60
|
+
childrenIndices: [] as number[],
|
|
61
|
+
bindPosition: b.restTranslation,
|
|
62
|
+
bindRotation: b.restRotation,
|
|
63
|
+
bindScale: b.restScale,
|
|
64
|
+
inverseBindMatrix: b.inverseBindMatrix,
|
|
65
|
+
rootAncestorMatrix: b.rootAncestorMatrix,
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < bones.length; i++) {
|
|
69
|
+
const parentIdx = bones[i].parentIndex;
|
|
70
|
+
if (parentIdx >= 0 && parentIdx < bones.length) {
|
|
71
|
+
bones[parentIdx].childrenIndices.push(i);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let rootBoneIndex = 0;
|
|
76
|
+
for (let i = 0; i < bones.length; i++) {
|
|
77
|
+
if (bones[i].parentIndex < 0) {
|
|
78
|
+
rootBoneIndex = i;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { bones, name: "skeleton", rootBoneIndex };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Vec3 / Quaternion math helpers ──
|
|
87
|
+
|
|
88
|
+
const IDENTITY_QUAT: readonly [number, number, number, number] = [0, 0, 0, 1];
|
|
89
|
+
|
|
90
|
+
function vec3Lerp(a: Float32Array, b: Float32Array, t: number, out: Float32Array): void {
|
|
91
|
+
out[0] = a[0] + (b[0] - a[0]) * t;
|
|
92
|
+
out[1] = a[1] + (b[1] - a[1]) * t;
|
|
93
|
+
out[2] = a[2] + (b[2] - a[2]) * t;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function quatInvert(q: Float32Array, out: Float32Array): void {
|
|
97
|
+
out[0] = -q[0];
|
|
98
|
+
out[1] = -q[1];
|
|
99
|
+
out[2] = -q[2];
|
|
100
|
+
out[3] = q[3];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function quatSlerp(a: Float32Array, b: Float32Array, t: number, out: Float32Array): void {
|
|
104
|
+
let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
|
|
105
|
+
if (dot < 0) {
|
|
106
|
+
out[0] = -b[0]; out[1] = -b[1]; out[2] = -b[2]; out[3] = -b[3];
|
|
107
|
+
dot = -dot;
|
|
108
|
+
} else {
|
|
109
|
+
out[0] = b[0]; out[1] = b[1]; out[2] = b[2]; out[3] = b[3];
|
|
110
|
+
}
|
|
111
|
+
if (dot > 0.9995) {
|
|
112
|
+
out[0] = a[0] + (out[0] - a[0]) * t;
|
|
113
|
+
out[1] = a[1] + (out[1] - a[1]) * t;
|
|
114
|
+
out[2] = a[2] + (out[2] - a[2]) * t;
|
|
115
|
+
out[3] = a[3] + (out[3] - a[3]) * t;
|
|
116
|
+
const len = Math.sqrt(out[0] * out[0] + out[1] * out[1] + out[2] * out[2] + out[3] * out[3]);
|
|
117
|
+
if (len > 0) { out[0] /= len; out[1] /= len; out[2] /= len; out[3] /= len; }
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const theta = Math.acos(dot);
|
|
121
|
+
const sinTheta = Math.sin(theta);
|
|
122
|
+
const sinT = Math.sin(t * theta) / sinTheta;
|
|
123
|
+
const oneMinusT = Math.sin((1 - t) * theta) / sinTheta;
|
|
124
|
+
out[0] = a[0] * oneMinusT + out[0] * sinT;
|
|
125
|
+
out[1] = a[1] * oneMinusT + out[1] * sinT;
|
|
126
|
+
out[2] = a[2] * oneMinusT + out[2] * sinT;
|
|
127
|
+
out[3] = a[3] * oneMinusT + out[3] * sinT;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function quatRotateVec(q: Float32Array | [number, number, number, number], v: [number, number, number]): [number, number, number] {
|
|
131
|
+
const x = q[0], y = q[1], z = q[2], w = q[3];
|
|
132
|
+
const c = 2 * (x * v[0] + y * v[1] + z * v[2]);
|
|
133
|
+
const ss = w * w - (x * x + y * y + z * z);
|
|
134
|
+
return [
|
|
135
|
+
c * x + ss * v[0] + 2 * w * (y * v[2] - z * v[1]),
|
|
136
|
+
c * y + ss * v[1] + 2 * w * (z * v[0] - x * v[2]),
|
|
137
|
+
c * z + ss * v[2] + 2 * w * (x * v[1] - y * v[0]),
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function quatMul(q1: Float32Array, q2: Float32Array, out: Float32Array): void {
|
|
142
|
+
const ax = q1[0], ay = q1[1], az = q1[2], aw = q1[3];
|
|
143
|
+
const bx = q2[0], by = q2[1], bz = q2[2], bw = q2[3];
|
|
144
|
+
out[0] = aw * bx + ax * bw + ay * bz - az * by;
|
|
145
|
+
out[1] = aw * by - ax * bz + ay * bw + az * bx;
|
|
146
|
+
out[2] = aw * bz + ax * by - ay * bx + az * bw;
|
|
147
|
+
out[3] = aw * bw - ax * bx - ay * by - az * bz;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function buildLocalMatrix(pos: Float32Array, rot: Float32Array, scale: Float32Array, out: Float32Array): void {
|
|
151
|
+
const x = rot[0], y = rot[1], z = rot[2], w = rot[3];
|
|
152
|
+
const x2 = x + x, y2 = y + y, z2 = z + z;
|
|
153
|
+
const xx = x * x2, xy = x * y2, xz = x * z2;
|
|
154
|
+
const yy = y * y2, yz = y * z2, zz = z * z2;
|
|
155
|
+
const wx = w * x2, wy = w * y2, wz = w * z2;
|
|
156
|
+
const sx = scale[0], sy = scale[1], sz = scale[2];
|
|
157
|
+
|
|
158
|
+
out[0] = (1 - (yy + zz)) * sx;
|
|
159
|
+
out[1] = (xy + wz) * sx;
|
|
160
|
+
out[2] = (xz - wy) * sx;
|
|
161
|
+
out[3] = 0;
|
|
162
|
+
out[4] = (xy - wz) * sy;
|
|
163
|
+
out[5] = (1 - (xx + zz)) * sy;
|
|
164
|
+
out[6] = (yz + wx) * sy;
|
|
165
|
+
out[7] = 0;
|
|
166
|
+
out[8] = (xz + wy) * sz;
|
|
167
|
+
out[9] = (yz - wx) * sz;
|
|
168
|
+
out[10] = (1 - (xx + yy)) * sz;
|
|
169
|
+
out[11] = 0;
|
|
170
|
+
out[12] = pos[0];
|
|
171
|
+
out[13] = pos[1];
|
|
172
|
+
out[14] = pos[2];
|
|
173
|
+
out[15] = 1;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Extract the rotation part of a column-major 4x4 matrix as a quaternion
|
|
177
|
+
* (column lengths are normalized out, so uniform scale is tolerated). */
|
|
178
|
+
function quatFromMat4Rotation(m: Float32Array): Float32Array {
|
|
179
|
+
let m00 = m[0], m10 = m[1], m20 = m[2];
|
|
180
|
+
let m01 = m[4], m11 = m[5], m21 = m[6];
|
|
181
|
+
let m02 = m[8], m12 = m[9], m22 = m[10];
|
|
182
|
+
const n0 = Math.hypot(m00, m10, m20) || 1;
|
|
183
|
+
const n1 = Math.hypot(m01, m11, m21) || 1;
|
|
184
|
+
const n2 = Math.hypot(m02, m12, m22) || 1;
|
|
185
|
+
m00 /= n0; m10 /= n0; m20 /= n0;
|
|
186
|
+
m01 /= n1; m11 /= n1; m21 /= n1;
|
|
187
|
+
m02 /= n2; m12 /= n2; m22 /= n2;
|
|
188
|
+
const trace = m00 + m11 + m22;
|
|
189
|
+
const q = new Float32Array(4);
|
|
190
|
+
if (trace > 0) {
|
|
191
|
+
const s = Math.sqrt(trace + 1) * 2;
|
|
192
|
+
q[3] = s / 4; q[0] = (m21 - m12) / s; q[1] = (m02 - m20) / s; q[2] = (m10 - m01) / s;
|
|
193
|
+
} else if (m00 > m11 && m00 > m22) {
|
|
194
|
+
const s = Math.sqrt(1 + m00 - m11 - m22) * 2;
|
|
195
|
+
q[3] = (m21 - m12) / s; q[0] = s / 4; q[1] = (m01 + m10) / s; q[2] = (m02 + m20) / s;
|
|
196
|
+
} else if (m11 > m22) {
|
|
197
|
+
const s = Math.sqrt(1 + m11 - m00 - m22) * 2;
|
|
198
|
+
q[3] = (m02 - m20) / s; q[0] = (m01 + m10) / s; q[1] = s / 4; q[2] = (m12 + m21) / s;
|
|
199
|
+
} else {
|
|
200
|
+
const s = Math.sqrt(1 + m22 - m00 - m11) * 2;
|
|
201
|
+
q[3] = (m10 - m01) / s; q[0] = (m02 + m20) / s; q[1] = (m12 + m21) / s; q[2] = s / 4;
|
|
202
|
+
}
|
|
203
|
+
return q;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Slerp a packed quaternion keyframe channel (times, values) at time t into out. */
|
|
207
|
+
function sampleQuatChannel(times: Float32Array, values: Float32Array, t: number, out: Float32Array): void {
|
|
208
|
+
if (times.length <= 1 || t <= times[0]) {
|
|
209
|
+
out[0] = values[0]; out[1] = values[1]; out[2] = values[2]; out[3] = values[3];
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const last = times.length - 1;
|
|
213
|
+
if (t >= times[last]) {
|
|
214
|
+
const o = last * 4;
|
|
215
|
+
out[0] = values[o]; out[1] = values[o + 1]; out[2] = values[o + 2]; out[3] = values[o + 3];
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
let lo = 0, hi = last;
|
|
219
|
+
while (hi - lo > 1) { const mid = (lo + hi) >> 1; if (times[mid] <= t) lo = mid; else hi = mid; }
|
|
220
|
+
const alpha = (t - times[lo]) / (times[hi] - times[lo]);
|
|
221
|
+
const a = new Float32Array([values[lo * 4], values[lo * 4 + 1], values[lo * 4 + 2], values[lo * 4 + 3]]);
|
|
222
|
+
const b = new Float32Array([values[hi * 4], values[hi * 4 + 1], values[hi * 4 + 2], values[hi * 4 + 3]]);
|
|
223
|
+
quatSlerp(a, b, alpha, out);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Swap a quaternion from Y-up (Mixamo/FBX source space) to Z-up (engine
|
|
227
|
+
* target space): (x, y, z, w) → (x, -z, y, w). Matches the position map
|
|
228
|
+
* (x, y, z) → (x, -z, y). */
|
|
229
|
+
function quatYupToZup(q: Float32Array, out: Float32Array): void {
|
|
230
|
+
out[0] = q[0]; out[1] = -q[2]; out[2] = q[1]; out[3] = q[3];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function mat4Mul(a: Float32Array, b: Float32Array, out: Float32Array): void {
|
|
234
|
+
for (let c = 0; c < 4; c++) {
|
|
235
|
+
const b0 = b[c * 4], b1 = b[c * 4 + 1], b2 = b[c * 4 + 2], b3 = b[c * 4 + 3];
|
|
236
|
+
out[c * 4] = a[0] * b0 + a[4] * b1 + a[8] * b2 + a[12] * b3;
|
|
237
|
+
out[c * 4 + 1] = a[1] * b0 + a[5] * b1 + a[9] * b2 + a[13] * b3;
|
|
238
|
+
out[c * 4 + 2] = a[2] * b0 + a[6] * b1 + a[10] * b2 + a[14] * b3;
|
|
239
|
+
out[c * 4 + 3] = a[3] * b0 + a[7] * b1 + a[11] * b2 + a[15] * b3;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export class SkeletonAnimator {
|
|
244
|
+
private boneCount: number;
|
|
245
|
+
private parentIndices: Int32Array;
|
|
246
|
+
private inverseBindMatrices: Float32Array[];
|
|
247
|
+
|
|
248
|
+
private skeleton: Skeleton;
|
|
249
|
+
private player: AnimationPlayer;
|
|
250
|
+
|
|
251
|
+
private localPos: Float32Array[];
|
|
252
|
+
private localRot: Float32Array[];
|
|
253
|
+
private localScale: Float32Array[];
|
|
254
|
+
|
|
255
|
+
private restPos: Float32Array[];
|
|
256
|
+
private restRot: Float32Array[];
|
|
257
|
+
private restScale: Float32Array[];
|
|
258
|
+
// Rotation of the non-bone intermediate transform each bone hangs under
|
|
259
|
+
// (rootAncestorMatrix). Identity for bones directly under their bone parent.
|
|
260
|
+
private intermediateRot: Float32Array[];
|
|
261
|
+
// Full non-bone intermediate matrix (translation included) — needed to FK
|
|
262
|
+
// bind world positions for retargeting frame alignment.
|
|
263
|
+
private intermediateMat: (Float32Array | null)[];
|
|
264
|
+
|
|
265
|
+
private worldMatrices: Float32Array[];
|
|
266
|
+
|
|
267
|
+
private localPosFlat: Float32Array;
|
|
268
|
+
private localRotFlat: Float32Array;
|
|
269
|
+
private localScaleFlat: Float32Array;
|
|
270
|
+
|
|
271
|
+
private inverseBindMatricesFlat: Float32Array;
|
|
272
|
+
|
|
273
|
+
private normalizationMatrix: Float32Array | null = null;
|
|
274
|
+
private inverseNormalizationMatrix: Float32Array | null = null;
|
|
275
|
+
|
|
276
|
+
private clips: Map<string, AnimationClip> = new Map();
|
|
277
|
+
private currentState: AnimState = "Idle";
|
|
278
|
+
private timeSinceLastStateChange = 0;
|
|
279
|
+
private blendDuration = 0.2;
|
|
280
|
+
private sourceRestRotations: Map<string, Map<string, [number, number, number, number]>> = new Map();
|
|
281
|
+
private sourcePreRotations: Map<string, Map<string, [number, number, number, number]>> = new Map();
|
|
282
|
+
private sourceRestTranslations: Map<string, Map<string, [number, number, number]>> = new Map();
|
|
283
|
+
|
|
284
|
+
private ikEnabled = true;
|
|
285
|
+
private leftFootTarget: Float32Array | null = null;
|
|
286
|
+
private rightFootTarget: Float32Array | null = null;
|
|
287
|
+
private boneNameToIndex: Map<string, number>;
|
|
288
|
+
private boneNames: string[] = [];
|
|
289
|
+
|
|
290
|
+
private leftLegUpIdx = -1;
|
|
291
|
+
private leftLegDownIdx = -1;
|
|
292
|
+
private leftFootIdx = -1;
|
|
293
|
+
private rightLegUpIdx = -1;
|
|
294
|
+
private rightLegDownIdx = -1;
|
|
295
|
+
private rightFootIdx = -1;
|
|
296
|
+
|
|
297
|
+
private leftUpperArmIdx = -1;
|
|
298
|
+
private rightUpperArmIdx = -1;
|
|
299
|
+
private spineIdx = -1;
|
|
300
|
+
private headIdx = -1;
|
|
301
|
+
private proceduralTime = 0;
|
|
302
|
+
private isMixamoSkeleton = false;
|
|
303
|
+
/** True when the target skeleton's native bone space is Z-up (e.g. UE-style
|
|
304
|
+
* rigs like Aisha). Mixamo clips are Y-up, so world deltas and rest-pose
|
|
305
|
+
* direction alignment must be axis-swapped for Z-up targets but passed
|
|
306
|
+
* through unchanged for natively Y-up rigs (e.g. Humanling). Defaults to
|
|
307
|
+
* true for rigs loaded before SkinData.skeletonUpAxis existed. */
|
|
308
|
+
private targetZUp = true;
|
|
309
|
+
|
|
310
|
+
constructor(skin: SkinData) {
|
|
311
|
+
this.boneCount = skin.bones.length;
|
|
312
|
+
this.parentIndices = new Int32Array(this.boneCount);
|
|
313
|
+
this.inverseBindMatrices = [];
|
|
314
|
+
this.boneNameToIndex = skin.boneNameToIndex;
|
|
315
|
+
|
|
316
|
+
// Create Skeleton and AnimationPlayer from SkinData
|
|
317
|
+
const skelData = skinDataToSkeletonData(skin);
|
|
318
|
+
this.skeleton = new Skeleton(skelData);
|
|
319
|
+
this.player = new AnimationPlayer(this.skeleton);
|
|
320
|
+
|
|
321
|
+
this.localPos = [];
|
|
322
|
+
this.localRot = [];
|
|
323
|
+
this.localScale = [];
|
|
324
|
+
this.restPos = [];
|
|
325
|
+
this.restRot = [];
|
|
326
|
+
this.restScale = [];
|
|
327
|
+
this.intermediateRot = [];
|
|
328
|
+
this.intermediateMat = [];
|
|
329
|
+
this.worldMatrices = [];
|
|
330
|
+
|
|
331
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
332
|
+
const bone = skin.bones[i];
|
|
333
|
+
this.parentIndices[i] = bone.parentIndex;
|
|
334
|
+
this.boneNames[i] = bone.name;
|
|
335
|
+
|
|
336
|
+
const ibm = new Float32Array(16);
|
|
337
|
+
ibm.set(bone.inverseBindMatrix);
|
|
338
|
+
this.inverseBindMatrices.push(ibm);
|
|
339
|
+
|
|
340
|
+
const rp = new Float32Array(bone.restTranslation);
|
|
341
|
+
const rr = new Float32Array(bone.restRotation);
|
|
342
|
+
const rs = new Float32Array(bone.restScale);
|
|
343
|
+
this.restPos.push(rp);
|
|
344
|
+
this.restRot.push(rr);
|
|
345
|
+
this.restScale.push(rs);
|
|
346
|
+
this.intermediateRot.push(
|
|
347
|
+
bone.rootAncestorMatrix ? quatFromMat4Rotation(bone.rootAncestorMatrix) : new Float32Array(IDENTITY_QUAT),
|
|
348
|
+
);
|
|
349
|
+
this.intermediateMat.push(bone.rootAncestorMatrix ? new Float32Array(bone.rootAncestorMatrix) : null);
|
|
350
|
+
|
|
351
|
+
this.localPos.push(new Float32Array(rp));
|
|
352
|
+
this.localRot.push(new Float32Array(rr));
|
|
353
|
+
this.localScale.push(new Float32Array(rs));
|
|
354
|
+
|
|
355
|
+
this.worldMatrices.push(new Float32Array(16));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
this.inverseBindMatricesFlat = new Float32Array(this.boneCount * 16);
|
|
359
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
360
|
+
this.inverseBindMatricesFlat.set(this.inverseBindMatrices[i], i * 16);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this.localPosFlat = new Float32Array(this.boneCount * 4);
|
|
364
|
+
this.localRotFlat = new Float32Array(this.boneCount * 4);
|
|
365
|
+
this.localScaleFlat = new Float32Array(this.boneCount * 4);
|
|
366
|
+
|
|
367
|
+
this.leftLegUpIdx = this.findBone(["LegUp_Left", "thigh_l", "UpperLeg_Left", "mixamorig:LeftUpLeg"]);
|
|
368
|
+
this.leftLegDownIdx = this.findBone(["LegDown_Left", "calf_l", "LowerLeg_Left", "mixamorig:LeftLeg"]);
|
|
369
|
+
this.leftFootIdx = this.findBone(["FootStart_Left", "foot_l", "Foot_Left", "mixamorig:LeftFoot"]);
|
|
370
|
+
this.rightLegUpIdx = this.findBone(["LegUp_Right", "thigh_r", "UpperLeg_Right", "mixamorig:RightUpLeg"]);
|
|
371
|
+
this.rightLegDownIdx = this.findBone(["LegDown_Right", "calf_r", "LowerLeg_Right", "mixamorig:RightLeg"]);
|
|
372
|
+
this.rightFootIdx = this.findBone(["FootStart_Right", "foot_r", "Foot_Right", "mixamorig:RightFoot"]);
|
|
373
|
+
|
|
374
|
+
this.leftUpperArmIdx = this.findBone(["UpperArm_Left", "upperarm_l", "ArmUp_Left", "mixamorig:LeftArm"]);
|
|
375
|
+
this.rightUpperArmIdx = this.findBone(["UpperArm_Right", "upperarm_r", "ArmUp_Right", "mixamorig:RightArm"]);
|
|
376
|
+
this.spineIdx = this.findBone(["Spine", "spine_01", "Spine_01", "mixamorig:Spine"]);
|
|
377
|
+
this.headIdx = this.findBone(["Head", "head", "mixamorig:Head"]);
|
|
378
|
+
|
|
379
|
+
this.targetZUp = skin.skeletonUpAxis !== "y";
|
|
380
|
+
this.isMixamoSkeleton = this.boneNameToIndex.has("mixamorig:Hips");
|
|
381
|
+
if (this.isMixamoSkeleton) {
|
|
382
|
+
log.info("Anim", "Mixamo skeleton detected — direct animation mapping (no retargeting)");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private findBone(names: string[]): number {
|
|
387
|
+
for (let i = 0; i < names.length; i++) {
|
|
388
|
+
const idx = this.boneNameToIndex.get(names[i]);
|
|
389
|
+
if (idx !== undefined) return idx;
|
|
390
|
+
}
|
|
391
|
+
return -1;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
registerAnimations(animations: AnimationData[]): void {
|
|
395
|
+
for (let i = 0; i < animations.length; i++) {
|
|
396
|
+
const clip = this.animationDataToClip(animations[i], animations[i].name);
|
|
397
|
+
if (clip) {
|
|
398
|
+
this.clips.set(animations[i].name, clip);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Register a pre-built AnimationClip directly (bypassing the
|
|
405
|
+
* AnimationData→retargeting pipeline). Use this for procedural clips
|
|
406
|
+
* authored directly in the skeleton's local space, or clips loaded from
|
|
407
|
+
* glTF that are already in the correct coordinate space.
|
|
408
|
+
*/
|
|
409
|
+
registerClip(name: string, clip: AnimationClip): void {
|
|
410
|
+
this.clips.set(name, clip);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Bake an external AnimationData (e.g. a Mixamo clip dropped onto a rigged
|
|
415
|
+
* model) into an AnimationClip for this skeleton — the same retargeting
|
|
416
|
+
* pipeline used by registerRetargetedAnimations, but the clip is returned
|
|
417
|
+
* instead of being registered under a state name. The clip's tracks are
|
|
418
|
+
* indexed against this skeleton's bone indices.
|
|
419
|
+
*/
|
|
420
|
+
bakeAnimationClip(anim: AnimationData, clipName: string): AnimationClip | null {
|
|
421
|
+
if (anim.sourceRestRotations?.size) {
|
|
422
|
+
this.sourceRestRotations.set(clipName, new Map(anim.sourceRestRotations));
|
|
423
|
+
}
|
|
424
|
+
if (anim.sourcePreRotations?.size) {
|
|
425
|
+
this.sourcePreRotations.set(clipName, new Map(anim.sourcePreRotations));
|
|
426
|
+
}
|
|
427
|
+
if (anim.sourceRestTranslations?.size) {
|
|
428
|
+
this.sourceRestTranslations.set(clipName, new Map(anim.sourceRestTranslations));
|
|
429
|
+
}
|
|
430
|
+
return this.animationDataToClip(anim, clipName);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private animationDataToClip(anim: AnimationData, animName: string): AnimationClip | null {
|
|
434
|
+
const tracks: KeyframeTrack[] = [];
|
|
435
|
+
// Keyed by SOURCE node name; falls back to the AnimationData's own maps
|
|
436
|
+
// when the caller didn't go through registerRetargetedAnimations.
|
|
437
|
+
const preRotMap = this.sourcePreRotations.get(animName) ?? anim.sourcePreRotations;
|
|
438
|
+
const restRotMap = this.sourceRestRotations.get(animName) ?? anim.sourceRestRotations;
|
|
439
|
+
|
|
440
|
+
if (!this.isMixamoSkeleton) {
|
|
441
|
+
// Per-frame world-space retargeting for non-Mixamo skeletons.
|
|
442
|
+
//
|
|
443
|
+
// mixamorig:* channels are evaluated as world rotations on the SOURCE
|
|
444
|
+
// (Mixamo) hierarchy — the target hierarchy may differ (e.g. the target
|
|
445
|
+
// can skip intermediate bones like spine_02). Each source world delta is
|
|
446
|
+
// then applied to the target's bind world rotation, and the target local
|
|
447
|
+
// is solved against the target's own (possibly animated) parent chain:
|
|
448
|
+
//
|
|
449
|
+
// srcDelta(t) = srcWorld(t) * inv(srcRestWorld) [Y-up space]
|
|
450
|
+
// desiredWorld(t) = axisSwap(srcDelta(t)) * tgtBindWorld
|
|
451
|
+
// local(t) = inv(intermediate) * inv(parentWorld(t)) * desiredWorld(t)
|
|
452
|
+
//
|
|
453
|
+
// where parentWorld(t) is the target parent's animated world rotation,
|
|
454
|
+
// which keeps parent and child deltas from double-applying.
|
|
455
|
+
//
|
|
456
|
+
// Non-Mixamo channels are applied directly as locals (same-rig clips).
|
|
457
|
+
|
|
458
|
+
// Retargeted tracks are baked to plain keyframes — never emit
|
|
459
|
+
// "cubicspline" (that layout expects tangent triplets).
|
|
460
|
+
const toInterp = (s: string): "linear" | "step" =>
|
|
461
|
+
s === "STEP" ? "step" : "linear";
|
|
462
|
+
|
|
463
|
+
// Partition channels.
|
|
464
|
+
interface SrcRotChannel {
|
|
465
|
+
times: Float32Array;
|
|
466
|
+
values: Float32Array;
|
|
467
|
+
boneIdx: number | undefined;
|
|
468
|
+
interpolation: string;
|
|
469
|
+
}
|
|
470
|
+
const srcRotChannels = new Map<string, SrcRotChannel>();
|
|
471
|
+
const directChannels: AnimationChannel[] = [];
|
|
472
|
+
let hipsPosChannel: AnimationChannel | null = null;
|
|
473
|
+
for (let c = 0; c < anim.channels.length; c++) {
|
|
474
|
+
const ch = anim.channels[c];
|
|
475
|
+
if (ch.targetNode.startsWith("mixamorig:")) {
|
|
476
|
+
if (ch.path === "rotation") {
|
|
477
|
+
const tgtName = SkeletonAnimator.MIXAMO_TO_UE[ch.targetNode] ?? ch.targetNode;
|
|
478
|
+
srcRotChannels.set(ch.targetNode, {
|
|
479
|
+
times: ch.keyframeTimes,
|
|
480
|
+
values: ch.keyframeValues,
|
|
481
|
+
boneIdx: this.boneNameToIndex.get(tgtName),
|
|
482
|
+
interpolation: ch.interpolation,
|
|
483
|
+
});
|
|
484
|
+
} else if (ch.path === "translation" && ch.targetNode === "mixamorig:Hips") {
|
|
485
|
+
hipsPosChannel = ch;
|
|
486
|
+
}
|
|
487
|
+
// Mixamo scale channels are dropped — bone proportions differ across rigs.
|
|
488
|
+
} else {
|
|
489
|
+
directChannels.push(ch);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ── Source (Mixamo) world-rotation evaluator ──
|
|
494
|
+
const srcLocalAt = (name: string, t: number, out: Float32Array): void => {
|
|
495
|
+
const ch = srcRotChannels.get(name);
|
|
496
|
+
if (ch) {
|
|
497
|
+
const q = new Float32Array(4);
|
|
498
|
+
sampleQuatChannel(ch.times, ch.values, t, q);
|
|
499
|
+
const pre = preRotMap?.get(name);
|
|
500
|
+
if (pre) {
|
|
501
|
+
quatMul(new Float32Array(pre), q, out);
|
|
502
|
+
} else {
|
|
503
|
+
out.set(q);
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
out.set(restRotMap?.get(name) ?? IDENTITY_QUAT);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
const srcWorldMemo = new Map<string, Map<number, Float32Array>>();
|
|
510
|
+
const srcWorld = (name: string, t: number): Float32Array => {
|
|
511
|
+
let memo = srcWorldMemo.get(name);
|
|
512
|
+
if (!memo) { memo = new Map(); srcWorldMemo.set(name, memo); }
|
|
513
|
+
const hit = memo.get(t);
|
|
514
|
+
if (hit) return hit;
|
|
515
|
+
const local = new Float32Array(4);
|
|
516
|
+
srcLocalAt(name, t, local);
|
|
517
|
+
const parent = SkeletonAnimator.MIXAMO_PARENT[name];
|
|
518
|
+
const world = new Float32Array(4);
|
|
519
|
+
if (parent) {
|
|
520
|
+
quatMul(srcWorld(parent, t), local, world);
|
|
521
|
+
} else {
|
|
522
|
+
world.set(local);
|
|
523
|
+
}
|
|
524
|
+
memo.set(t, world);
|
|
525
|
+
return world;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// ── Target bind world rotations (including non-bone intermediates) ──
|
|
529
|
+
const ID = new Float32Array([0, 0, 0, 1]);
|
|
530
|
+
const tgtBindWorld: Float32Array[] = [];
|
|
531
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
532
|
+
const parentIdx = this.parentIndices[i];
|
|
533
|
+
const pw = parentIdx >= 0 ? tgtBindWorld[parentIdx] : ID;
|
|
534
|
+
const tmp = new Float32Array(4);
|
|
535
|
+
const w = new Float32Array(4);
|
|
536
|
+
quatMul(pw, this.intermediateRot[i], tmp);
|
|
537
|
+
quatMul(tmp, this.restRot[i], w);
|
|
538
|
+
tgtBindWorld.push(w);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ── Source REST world rotations (Mixamo bind pose) ──
|
|
542
|
+
// The retarget delta must be measured against the source's rest pose,
|
|
543
|
+
// NOT the clip's first keyframe: locomotion clips start mid-stride, so
|
|
544
|
+
// a frame-0 reference would map that mid-stride pose onto the target's
|
|
545
|
+
// bind pose and shift every other frame by that offset (which shows up
|
|
546
|
+
// as e.g. knees hyperextending backwards).
|
|
547
|
+
const srcRestWorldMemo = new Map<string, Float32Array>();
|
|
548
|
+
const srcRestWorld = (name: string): Float32Array => {
|
|
549
|
+
const hit = srcRestWorldMemo.get(name);
|
|
550
|
+
if (hit) return hit;
|
|
551
|
+
const w = new Float32Array(4);
|
|
552
|
+
w.set(restRotMap?.get(name) ?? IDENTITY_QUAT);
|
|
553
|
+
const parent = SkeletonAnimator.MIXAMO_PARENT[name];
|
|
554
|
+
if (parent) {
|
|
555
|
+
const out = new Float32Array(4);
|
|
556
|
+
quatMul(srcRestWorld(parent), w, out);
|
|
557
|
+
w.set(out);
|
|
558
|
+
}
|
|
559
|
+
srcRestWorldMemo.set(name, w);
|
|
560
|
+
return w;
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// ── Rest-pose direction data ──
|
|
564
|
+
// Source rest (Mixamo T-pose) and target bind poses differ (e.g. A-pose
|
|
565
|
+
// arms). Bone rest directions are needed to align the two rest poses;
|
|
566
|
+
// they come from joint-to-child-joint vectors, so source rest
|
|
567
|
+
// translations and target bind positions are FK'd here.
|
|
568
|
+
const restTransMap = this.sourceRestTranslations.get(animName) ?? anim.sourceRestTranslations;
|
|
569
|
+
|
|
570
|
+
// Source rest world POSITIONS via the Mixamo chain.
|
|
571
|
+
const srcRestPosMemo = new Map<string, [number, number, number]>();
|
|
572
|
+
const srcRestPos = (name: string): [number, number, number] => {
|
|
573
|
+
const hit = srcRestPosMemo.get(name);
|
|
574
|
+
if (hit) return hit;
|
|
575
|
+
const lt = restTransMap?.get(name) ?? [0, 0, 0];
|
|
576
|
+
const parent = SkeletonAnimator.MIXAMO_PARENT[name];
|
|
577
|
+
let pos: [number, number, number];
|
|
578
|
+
if (parent) {
|
|
579
|
+
const pp = srcRestPos(parent);
|
|
580
|
+
const pr = srcRestWorld(parent);
|
|
581
|
+
const r = quatRotateVec(pr, lt);
|
|
582
|
+
pos = [pp[0] + r[0], pp[1] + r[1], pp[2] + r[2]];
|
|
583
|
+
} else {
|
|
584
|
+
pos = [lt[0], lt[1], lt[2]];
|
|
585
|
+
}
|
|
586
|
+
srcRestPosMemo.set(name, pos);
|
|
587
|
+
return pos;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// Source "tip" child per node (defines the bone's rest direction).
|
|
591
|
+
const srcChildren = new Map<string, string[]>();
|
|
592
|
+
for (const [child, parent] of Object.entries(SkeletonAnimator.MIXAMO_PARENT)) {
|
|
593
|
+
if (!parent) continue;
|
|
594
|
+
let list = srcChildren.get(parent);
|
|
595
|
+
if (!list) { list = []; srcChildren.set(parent, list); }
|
|
596
|
+
list.push(child);
|
|
597
|
+
}
|
|
598
|
+
const srcTip = (name: string): string | null => {
|
|
599
|
+
const kids = srcChildren.get(name);
|
|
600
|
+
if (!kids || kids.length === 0) return null;
|
|
601
|
+
// Prefer the axial continuation (spine/neck/head, middle finger, toe).
|
|
602
|
+
const preferred = kids.find((k) =>
|
|
603
|
+
/Spine|Neck|HeadTop|Middle1|Toe_End/.test(k));
|
|
604
|
+
return preferred ?? kids[0];
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// Target bind world positions via matrix FK (includes non-bone
|
|
608
|
+
// intermediate transforms in full, translation included).
|
|
609
|
+
const tgtBindWPos: [number, number, number][] = [];
|
|
610
|
+
{
|
|
611
|
+
const worldMats: Float32Array[] = [];
|
|
612
|
+
const localMat = new Float32Array(16);
|
|
613
|
+
const tmp = new Float32Array(16);
|
|
614
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
615
|
+
buildLocalMatrix(this.restPos[i], this.restRot[i], this.restScale[i], localMat);
|
|
616
|
+
const im = this.intermediateMat[i];
|
|
617
|
+
let lm = localMat;
|
|
618
|
+
if (im) { mat4Mul(im, localMat, tmp); lm = new Float32Array(tmp); }
|
|
619
|
+
const pi = this.parentIndices[i];
|
|
620
|
+
const wm = new Float32Array(16);
|
|
621
|
+
if (pi >= 0 && worldMats[pi]) mat4Mul(worldMats[pi], lm, wm);
|
|
622
|
+
else wm.set(lm);
|
|
623
|
+
worldMats[i] = wm;
|
|
624
|
+
tgtBindWPos.push([wm[12], wm[13], wm[14]]);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Target "tip" child per bone index (first bone child; prefer the
|
|
629
|
+
// axial continuation for spine/hand/foot chains).
|
|
630
|
+
const tgtChildren = new Map<number, number[]>();
|
|
631
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
632
|
+
const p = this.parentIndices[i];
|
|
633
|
+
if (p < 0) continue;
|
|
634
|
+
let list = tgtChildren.get(p);
|
|
635
|
+
if (!list) { list = []; tgtChildren.set(p, list); }
|
|
636
|
+
list.push(i);
|
|
637
|
+
}
|
|
638
|
+
const tgtTip = (i: number): number | null => {
|
|
639
|
+
const kids = tgtChildren.get(i);
|
|
640
|
+
if (!kids || kids.length === 0) return null;
|
|
641
|
+
const preferred = kids.find((k) =>
|
|
642
|
+
/spine|neck|head|middle_01|ball/.test(this.boneNames[k]));
|
|
643
|
+
return preferred ?? kids[0];
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
// Per-bone rest-direction alignment. The source (Mixamo T-pose) and
|
|
647
|
+
// target (e.g. A-pose) rest poses differ; measuring the animation delta
|
|
648
|
+
// against the raw source rest double-counts that difference (an A-pose
|
|
649
|
+
// arm receiving a "drop arm from T-pose" delta overshoots and crosses
|
|
650
|
+
// the chest). Instead, pre-rotate the source rest reference by R — the
|
|
651
|
+
// shortest-arc rotation taking the source bone's rest direction onto
|
|
652
|
+
// the target bone's bind direction (expressed in source space). With
|
|
653
|
+
// srcRest' = R·srcRest:
|
|
654
|
+
//
|
|
655
|
+
// desiredWorld(t) = axisSwap(srcW(t) · srcRest'⁻¹) · tgtBindWorld
|
|
656
|
+
//
|
|
657
|
+
// which makes the target bone's world direction exactly
|
|
658
|
+
// axisSwap(sourceBoneDirection(t)) at every frame, while twist about
|
|
659
|
+
// the bone axis still transfers through the delta. When the rest
|
|
660
|
+
// directions already match (e.g. legs), R ≈ identity and this reduces
|
|
661
|
+
// to the plain rest-pose delta.
|
|
662
|
+
const dirAlignMemo = new Map<number, Float32Array | null>();
|
|
663
|
+
// UE target name → mixamorig source name, for leaf-bone alignment
|
|
664
|
+
// inheritance (reverse of MIXAMO_TO_UE).
|
|
665
|
+
const ueToSrc = new Map<string, string>();
|
|
666
|
+
for (const [s, t] of Object.entries(SkeletonAnimator.MIXAMO_TO_UE)) ueToSrc.set(t, s);
|
|
667
|
+
const srcDirAlign = (srcName: string, boneIdx: number): Float32Array | null => {
|
|
668
|
+
let r = dirAlignMemo.get(boneIdx);
|
|
669
|
+
if (r !== undefined) return r;
|
|
670
|
+
r = null;
|
|
671
|
+
const sTip = restTransMap ? srcTip(srcName) : null;
|
|
672
|
+
const tTip = tgtTip(boneIdx);
|
|
673
|
+
if (sTip && tTip !== null) {
|
|
674
|
+
const a = srcRestPos(srcName), b = srcRestPos(sTip);
|
|
675
|
+
const ds = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
|
|
676
|
+
const ls = Math.hypot(ds[0], ds[1], ds[2]);
|
|
677
|
+
const c = tgtBindWPos[boneIdx], d = tgtBindWPos[tTip];
|
|
678
|
+
const dt = [d[0] - c[0], d[1] - c[1], d[2] - c[2]];
|
|
679
|
+
const lt = Math.hypot(dt[0], dt[1], dt[2]);
|
|
680
|
+
if (ls > 1e-4 && lt > 1e-4) {
|
|
681
|
+
const from = [ds[0] / ls, ds[1] / ls, ds[2] / ls];
|
|
682
|
+
// Target bind dir → source Y-up space. Z-up targets need
|
|
683
|
+
// (x,y,z) → (x,z,-y); Y-up targets are already in source space.
|
|
684
|
+
const to = this.targetZUp
|
|
685
|
+
? [dt[0] / lt, dt[2] / lt, -dt[1] / lt]
|
|
686
|
+
: [dt[0] / lt, dt[1] / lt, dt[2] / lt];
|
|
687
|
+
const dot = from[0] * to[0] + from[1] * to[1] + from[2] * to[2];
|
|
688
|
+
if (dot < 0.999999) {
|
|
689
|
+
let ax: number, ay: number, az: number;
|
|
690
|
+
if (dot < -0.999999) {
|
|
691
|
+
// Antiparallel: pick any perpendicular axis.
|
|
692
|
+
const px = Math.abs(from[0]) < 0.9 ? 1 : 0;
|
|
693
|
+
const pxv = px ? [1, 0, 0] : [0, 1, 0];
|
|
694
|
+
ax = from[1] * pxv[2] - from[2] * pxv[1];
|
|
695
|
+
ay = from[2] * pxv[0] - from[0] * pxv[2];
|
|
696
|
+
az = from[0] * pxv[1] - from[1] * pxv[0];
|
|
697
|
+
const al = Math.hypot(ax, ay, az) || 1;
|
|
698
|
+
r = new Float32Array([ax / al, ay / al, az / al, 0]);
|
|
699
|
+
} else {
|
|
700
|
+
ax = from[1] * to[2] - from[2] * to[1];
|
|
701
|
+
ay = from[2] * to[0] - from[0] * to[2];
|
|
702
|
+
az = from[0] * to[1] - from[1] * to[0];
|
|
703
|
+
const al = Math.hypot(ax, ay, az, 1 + dot) || 1;
|
|
704
|
+
r = new Float32Array([ax / al, ay / al, az / al, (1 + dot) / al]);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
} else {
|
|
709
|
+
// Leaf bone — no tip child to define a bone direction, so no R can
|
|
710
|
+
// be computed. Without one, the raw source delta carries the
|
|
711
|
+
// accumulated rest-pose mismatch of every ancestor in the chain
|
|
712
|
+
// (e.g. Mixamo finger tips deviating ~120° from the target bind).
|
|
713
|
+
// Inherit the parent's alignment so the leaf rides the corrected
|
|
714
|
+
// frame and stays at its bind local when the source is at rest.
|
|
715
|
+
const pIdx = this.parentIndices[boneIdx];
|
|
716
|
+
if (pIdx >= 0) {
|
|
717
|
+
const pSrc = ueToSrc.get(this.boneNames[pIdx]);
|
|
718
|
+
if (pSrc) r = srcDirAlign(pSrc, pIdx);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
dirAlignMemo.set(boneIdx, r);
|
|
722
|
+
return r;
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// ── Desired world rotations per channeled target bone ──
|
|
726
|
+
interface BoneChannel {
|
|
727
|
+
srcName: string;
|
|
728
|
+
times: Float32Array;
|
|
729
|
+
desired: Float32Array;
|
|
730
|
+
interpolation: string;
|
|
731
|
+
}
|
|
732
|
+
const boneChannels = new Map<number, BoneChannel>();
|
|
733
|
+
|
|
734
|
+
const emitDesired = (srcName: string, boneIdx: number, times: Float32Array, interpolation: string): void => {
|
|
735
|
+
const desired = new Float32Array(times.length * 4);
|
|
736
|
+
// srcRest' = R·srcRestW where R aligns the source rest bone direction
|
|
737
|
+
// with the target bind direction. inv(srcRest') = inv(srcRestW)·inv(R).
|
|
738
|
+
const srcRestW = srcRestWorld(srcName);
|
|
739
|
+
const invSrcW = new Float32Array(4);
|
|
740
|
+
quatInvert(srcRestW, invSrcW);
|
|
741
|
+
const align = srcDirAlign(srcName, boneIdx);
|
|
742
|
+
const invSrcRef = new Float32Array(4);
|
|
743
|
+
if (align) {
|
|
744
|
+
const invR = new Float32Array(4);
|
|
745
|
+
quatInvert(align, invR);
|
|
746
|
+
quatMul(invSrcW, invR, invSrcRef);
|
|
747
|
+
} else {
|
|
748
|
+
invSrcRef.set(invSrcW);
|
|
749
|
+
}
|
|
750
|
+
const tgtRef = tgtBindWorld[boneIdx];
|
|
751
|
+
const tmpA = new Float32Array(4);
|
|
752
|
+
const tmpB = new Float32Array(4);
|
|
753
|
+
let prev: Float32Array | null = null;
|
|
754
|
+
for (let k = 0; k < times.length; k++) {
|
|
755
|
+
quatMul(srcWorld(srcName, times[k]), invSrcRef, tmpA); // source deviation from reference frame
|
|
756
|
+
if (this.targetZUp) quatYupToZup(tmpA, tmpB); else tmpB.set(tmpA);
|
|
757
|
+
const w = new Float32Array(4);
|
|
758
|
+
quatMul(tmpB, tgtRef, w);
|
|
759
|
+
if (prev && w[0] * prev[0] + w[1] * prev[1] + w[2] * prev[2] + w[3] * prev[3] < 0) {
|
|
760
|
+
w[0] = -w[0]; w[1] = -w[1]; w[2] = -w[2]; w[3] = -w[3];
|
|
761
|
+
}
|
|
762
|
+
prev = w;
|
|
763
|
+
desired.set(w, k * 4);
|
|
764
|
+
}
|
|
765
|
+
boneChannels.set(boneIdx, { srcName, times, desired, interpolation });
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
for (const [srcName, ch] of srcRotChannels.entries()) {
|
|
769
|
+
if (ch.boneIdx === undefined) continue;
|
|
770
|
+
emitDesired(srcName, ch.boneIdx, ch.times, ch.interpolation);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// A mapped target bone whose SOURCE node has no channel still needs a
|
|
774
|
+
// track when an ancestor in the source chain is animated (e.g.
|
|
775
|
+
// mixamorig:Spine2 → spine_03 when only mixamorig:Spine1 is keyed).
|
|
776
|
+
const hasChanneledAncestor = (srcName: string): boolean => {
|
|
777
|
+
let p = SkeletonAnimator.MIXAMO_PARENT[srcName];
|
|
778
|
+
while (p) {
|
|
779
|
+
if (srcRotChannels.has(p)) return true;
|
|
780
|
+
p = SkeletonAnimator.MIXAMO_PARENT[p];
|
|
781
|
+
}
|
|
782
|
+
return false;
|
|
783
|
+
};
|
|
784
|
+
let unionTimes: Float32Array | null = null;
|
|
785
|
+
for (const [srcName, tgtName] of Object.entries(SkeletonAnimator.MIXAMO_TO_UE)) {
|
|
786
|
+
if (srcRotChannels.has(srcName)) continue;
|
|
787
|
+
const boneIdx = this.boneNameToIndex.get(tgtName);
|
|
788
|
+
if (boneIdx === undefined || !hasChanneledAncestor(srcName)) continue;
|
|
789
|
+
if (!unionTimes) {
|
|
790
|
+
const set = new Set<number>();
|
|
791
|
+
for (const ch of srcRotChannels.values()) ch.times.forEach((t) => { set.add(t);; });
|
|
792
|
+
unionTimes = new Float32Array([...set].sort((a, b) => a - b));
|
|
793
|
+
}
|
|
794
|
+
emitDesired(srcName, boneIdx, unionTimes, "LINEAR");
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// ── Target animated world sampler (channels → desired; else bind-local) ──
|
|
798
|
+
const tgtWorldMemo = new Map<number, Map<number, Float32Array>>();
|
|
799
|
+
const tgtWorldAnim = (i: number, t: number): Float32Array => {
|
|
800
|
+
const ch = boneChannels.get(i);
|
|
801
|
+
if (ch) {
|
|
802
|
+
const out = new Float32Array(4);
|
|
803
|
+
sampleQuatChannel(ch.times, ch.desired, t, out);
|
|
804
|
+
return out;
|
|
805
|
+
}
|
|
806
|
+
let memo = tgtWorldMemo.get(i);
|
|
807
|
+
if (!memo) { memo = new Map(); tgtWorldMemo.set(i, memo); }
|
|
808
|
+
const hit = memo.get(t);
|
|
809
|
+
if (hit) return hit;
|
|
810
|
+
const parentIdx = this.parentIndices[i];
|
|
811
|
+
const pw = parentIdx >= 0 ? tgtWorldAnim(parentIdx, t) : ID;
|
|
812
|
+
const tmp = new Float32Array(4);
|
|
813
|
+
const w = new Float32Array(4);
|
|
814
|
+
quatMul(pw, this.intermediateRot[i], tmp);
|
|
815
|
+
quatMul(tmp, this.restRot[i], w);
|
|
816
|
+
memo.set(t, w);
|
|
817
|
+
return w;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// ── Emit local rotation tracks ──
|
|
821
|
+
for (const [i, ch] of boneChannels.entries()) {
|
|
822
|
+
const times = ch.times;
|
|
823
|
+
const values = new Float32Array(times.length * 4);
|
|
824
|
+
const parentIdx = this.parentIndices[i];
|
|
825
|
+
const invIntermediate = new Float32Array(4);
|
|
826
|
+
quatInvert(this.intermediateRot[i], invIntermediate);
|
|
827
|
+
const tmpA = new Float32Array(4);
|
|
828
|
+
const tmpB = new Float32Array(4);
|
|
829
|
+
let prev: Float32Array | null = null;
|
|
830
|
+
for (let k = 0; k < times.length; k++) {
|
|
831
|
+
const pw = parentIdx >= 0 ? tgtWorldAnim(parentIdx, times[k]) : ID;
|
|
832
|
+
const o = k * 4;
|
|
833
|
+
const desired = new Float32Array([ch.desired[o], ch.desired[o + 1], ch.desired[o + 2], ch.desired[o + 3]]);
|
|
834
|
+
quatInvert(pw, tmpA);
|
|
835
|
+
quatMul(tmpA, desired, tmpB);
|
|
836
|
+
const local = new Float32Array(4);
|
|
837
|
+
quatMul(invIntermediate, tmpB, local);
|
|
838
|
+
if (prev && local[0] * prev[0] + local[1] * prev[1] + local[2] * prev[2] + local[3] * prev[3] < 0) {
|
|
839
|
+
local[0] = -local[0]; local[1] = -local[1]; local[2] = -local[2]; local[3] = -local[3];
|
|
840
|
+
}
|
|
841
|
+
prev = local;
|
|
842
|
+
values.set(local, o);
|
|
843
|
+
}
|
|
844
|
+
tracks.push({
|
|
845
|
+
boneName: this.boneNames[i],
|
|
846
|
+
boneIndex: i,
|
|
847
|
+
path: "rotation" as TrackPath,
|
|
848
|
+
times,
|
|
849
|
+
values,
|
|
850
|
+
interpolation: toInterp(ch.interpolation),
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// ── Hips translation → pelvis position track ──
|
|
855
|
+
// Applied as a scaled delta around the target's rest position. Forward
|
|
856
|
+
// motion is stripped (root motion is gameplay-driven); lateral sway and
|
|
857
|
+
// vertical bob are preserved.
|
|
858
|
+
const pelvisIdx = this.boneNameToIndex.get("pelvis");
|
|
859
|
+
if (hipsPosChannel && pelvisIdx !== undefined) {
|
|
860
|
+
const times = hipsPosChannel.keyframeTimes;
|
|
861
|
+
const v = hipsPosChannel.keyframeValues;
|
|
862
|
+
const rest = this.restPos[pelvisIdx];
|
|
863
|
+
// The pelvis' vertical rest component — Z for Z-up rigs, Y for Y-up.
|
|
864
|
+
const upAxis = this.targetZUp ? 2 : 1;
|
|
865
|
+
const srcY0 = v[1];
|
|
866
|
+
const scale = Math.abs(srcY0) > 1e-3 ? Math.abs(rest[upAxis]) / Math.abs(srcY0) : 0.01;
|
|
867
|
+
const values = new Float32Array(times.length * 3);
|
|
868
|
+
for (let k = 0; k < times.length; k++) {
|
|
869
|
+
const dx = (v[k * 3] - v[0]) * scale;
|
|
870
|
+
const dy = (v[k * 3 + 1] - v[1]) * scale;
|
|
871
|
+
values[k * 3] = rest[0] + dx;
|
|
872
|
+
values[k * 3 + 1] = rest[1] + (this.targetZUp ? 0 : dy);
|
|
873
|
+
values[k * 3 + 2] = rest[2] + (this.targetZUp ? dy : 0);
|
|
874
|
+
}
|
|
875
|
+
tracks.push({
|
|
876
|
+
boneName: this.boneNames[pelvisIdx],
|
|
877
|
+
boneIndex: pelvisIdx,
|
|
878
|
+
path: "position" as TrackPath,
|
|
879
|
+
times,
|
|
880
|
+
values,
|
|
881
|
+
interpolation: toInterp(hipsPosChannel.interpolation),
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// ── Non-Mixamo channels: applied directly as local-space tracks ──
|
|
886
|
+
for (let c = 0; c < directChannels.length; c++) {
|
|
887
|
+
const ch = directChannels[c];
|
|
888
|
+
const boneIdx = this.boneNameToIndex.get(ch.targetNode);
|
|
889
|
+
if (boneIdx === undefined) continue;
|
|
890
|
+
const path: TrackPath = ch.path === "translation" ? "position" : ch.path === "rotation" ? "rotation" : "scale";
|
|
891
|
+
const interp = toInterp(ch.interpolation);
|
|
892
|
+
const times = ch.keyframeTimes;
|
|
893
|
+
const srcValues = ch.keyframeValues;
|
|
894
|
+
if (path === "rotation") {
|
|
895
|
+
const values = new Float32Array(srcValues.length);
|
|
896
|
+
const srcPreRot = preRotMap?.get(ch.targetNode);
|
|
897
|
+
for (let k = 0; k < times.length; k++) {
|
|
898
|
+
const v0 = k * 4;
|
|
899
|
+
const q = new Float32Array([srcValues[v0], srcValues[v0 + 1], srcValues[v0 + 2], srcValues[v0 + 3]]);
|
|
900
|
+
if (srcPreRot) {
|
|
901
|
+
const result = new Float32Array(4);
|
|
902
|
+
quatMul(new Float32Array(srcPreRot), q, result);
|
|
903
|
+
values[v0] = result[0]; values[v0 + 1] = result[1]; values[v0 + 2] = result[2]; values[v0 + 3] = result[3];
|
|
904
|
+
} else {
|
|
905
|
+
values[v0] = q[0]; values[v0 + 1] = q[1]; values[v0 + 2] = q[2]; values[v0 + 3] = q[3];
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
tracks.push({ boneName: ch.targetNode, boneIndex: boneIdx, path, times, values, interpolation: interp });
|
|
909
|
+
} else {
|
|
910
|
+
tracks.push({ boneName: ch.targetNode, boneIndex: boneIdx, path, times, values: srcValues, interpolation: interp });
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
} else {
|
|
914
|
+
// Mixamo skeleton: simple pre-rotation application
|
|
915
|
+
for (let c = 0; c < anim.channels.length; c++) {
|
|
916
|
+
const ch = anim.channels[c];
|
|
917
|
+
const nodeName = ch.targetNode;
|
|
918
|
+
const boneIdx = this.boneNameToIndex.get(nodeName);
|
|
919
|
+
if (boneIdx === undefined) continue;
|
|
920
|
+
const path: TrackPath = ch.path === "translation" ? "position" : ch.path === "rotation" ? "rotation" : "scale";
|
|
921
|
+
const interp: "linear" | "step" | "cubicspline" = ch.interpolation === "LINEAR" ? "linear" : ch.interpolation === "STEP" ? "step" : "cubicspline";
|
|
922
|
+
const times = ch.keyframeTimes;
|
|
923
|
+
const srcValues = ch.keyframeValues;
|
|
924
|
+
|
|
925
|
+
if (path === "rotation") {
|
|
926
|
+
const values = new Float32Array(srcValues.length);
|
|
927
|
+
const srcPreRot = preRotMap?.get(nodeName);
|
|
928
|
+
for (let k = 0; k < times.length; k++) {
|
|
929
|
+
const v0 = k * 4;
|
|
930
|
+
const q = new Float32Array([srcValues[v0], srcValues[v0+1], srcValues[v0+2], srcValues[v0+3]]);
|
|
931
|
+
if (srcPreRot) {
|
|
932
|
+
const result = new Float32Array(4);
|
|
933
|
+
quatMul(new Float32Array(srcPreRot), q, result);
|
|
934
|
+
values[v0] = result[0]; values[v0+1] = result[1]; values[v0+2] = result[2]; values[v0+3] = result[3];
|
|
935
|
+
} else {
|
|
936
|
+
values[v0] = q[0]; values[v0+1] = q[1]; values[v0+2] = q[2]; values[v0+3] = q[3];
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
tracks.push({ boneName: nodeName, boneIndex: boneIdx, path, times, values, interpolation: interp });
|
|
940
|
+
} else {
|
|
941
|
+
tracks.push({ boneName: nodeName, boneIndex: boneIdx, path, times, values: srcValues, interpolation: interp });
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (tracks.length === 0) return null;
|
|
947
|
+
return new AnimationClip({ name: animName, duration: anim.duration, tracks });
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
private static MIXAMO_TO_UE: Record<string, string> = {
|
|
951
|
+
"mixamorig:Hips": "pelvis",
|
|
952
|
+
"mixamorig:Spine": "spine_01",
|
|
953
|
+
"mixamorig:Spine1": "spine_02",
|
|
954
|
+
"mixamorig:Spine2": "spine_03",
|
|
955
|
+
"mixamorig:Neck": "neck_01",
|
|
956
|
+
"mixamorig:Head": "head",
|
|
957
|
+
"mixamorig:LeftShoulder": "clavicle_l",
|
|
958
|
+
"mixamorig:LeftArm": "upperarm_l",
|
|
959
|
+
"mixamorig:LeftForeArm": "lowerarm_l",
|
|
960
|
+
"mixamorig:LeftHand": "hand_l",
|
|
961
|
+
"mixamorig:RightShoulder": "clavicle_r",
|
|
962
|
+
"mixamorig:RightArm": "upperarm_r",
|
|
963
|
+
"mixamorig:RightForeArm": "lowerarm_r",
|
|
964
|
+
"mixamorig:RightHand": "hand_r",
|
|
965
|
+
"mixamorig:LeftUpLeg": "thigh_l",
|
|
966
|
+
"mixamorig:LeftLeg": "calf_l",
|
|
967
|
+
"mixamorig:LeftFoot": "foot_l",
|
|
968
|
+
"mixamorig:LeftToeBase": "ball_l",
|
|
969
|
+
"mixamorig:RightUpLeg": "thigh_r",
|
|
970
|
+
"mixamorig:RightLeg": "calf_r",
|
|
971
|
+
"mixamorig:RightFoot": "foot_r",
|
|
972
|
+
"mixamorig:RightToeBase": "ball_r",
|
|
973
|
+
"mixamorig:LeftHandThumb1": "thumb_01_l",
|
|
974
|
+
"mixamorig:LeftHandThumb2": "thumb_02_l",
|
|
975
|
+
"mixamorig:LeftHandThumb3": "thumb_03_l",
|
|
976
|
+
"mixamorig:LeftHandIndex1": "index_01_l",
|
|
977
|
+
"mixamorig:LeftHandIndex2": "index_02_l",
|
|
978
|
+
"mixamorig:LeftHandIndex3": "index_03_l",
|
|
979
|
+
"mixamorig:LeftHandMiddle1": "middle_01_l",
|
|
980
|
+
"mixamorig:LeftHandMiddle2": "middle_02_l",
|
|
981
|
+
"mixamorig:LeftHandMiddle3": "middle_03_l",
|
|
982
|
+
"mixamorig:LeftHandRing1": "ring_01_l",
|
|
983
|
+
"mixamorig:LeftHandRing2": "ring_02_l",
|
|
984
|
+
"mixamorig:LeftHandRing3": "ring_03_l",
|
|
985
|
+
"mixamorig:LeftHandPinky1": "pinky_01_l",
|
|
986
|
+
"mixamorig:LeftHandPinky2": "pinky_02_l",
|
|
987
|
+
"mixamorig:LeftHandPinky3": "pinky_03_l",
|
|
988
|
+
"mixamorig:RightHandThumb1": "thumb_01_r",
|
|
989
|
+
"mixamorig:RightHandThumb2": "thumb_02_r",
|
|
990
|
+
"mixamorig:RightHandThumb3": "thumb_03_r",
|
|
991
|
+
"mixamorig:RightHandIndex1": "index_01_r",
|
|
992
|
+
"mixamorig:RightHandIndex2": "index_02_r",
|
|
993
|
+
"mixamorig:RightHandIndex3": "index_03_r",
|
|
994
|
+
"mixamorig:RightHandMiddle1": "middle_01_r",
|
|
995
|
+
"mixamorig:RightHandMiddle2": "middle_02_r",
|
|
996
|
+
"mixamorig:RightHandMiddle3": "middle_03_r",
|
|
997
|
+
"mixamorig:RightHandRing1": "ring_01_r",
|
|
998
|
+
"mixamorig:RightHandRing2": "ring_02_r",
|
|
999
|
+
"mixamorig:RightHandRing3": "ring_03_r",
|
|
1000
|
+
"mixamorig:RightHandPinky1": "pinky_01_r",
|
|
1001
|
+
"mixamorig:RightHandPinky2": "pinky_02_r",
|
|
1002
|
+
"mixamorig:RightHandPinky3": "pinky_03_r",
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
/** Parent of each bone in the standard Mixamo rig. Used to evaluate source
|
|
1006
|
+
* world rotations when retargeting mixamorig:* clips onto non-Mixamo
|
|
1007
|
+
* skeletons — the source hierarchy is independent of the target's. */
|
|
1008
|
+
private static MIXAMO_PARENT: Record<string, string | null> = {
|
|
1009
|
+
"mixamorig:Hips": null,
|
|
1010
|
+
"mixamorig:Spine": "mixamorig:Hips",
|
|
1011
|
+
"mixamorig:Spine1": "mixamorig:Spine",
|
|
1012
|
+
"mixamorig:Spine2": "mixamorig:Spine1",
|
|
1013
|
+
"mixamorig:Neck": "mixamorig:Spine2",
|
|
1014
|
+
"mixamorig:Head": "mixamorig:Neck",
|
|
1015
|
+
"mixamorig:HeadTop_End": "mixamorig:Head",
|
|
1016
|
+
"mixamorig:LeftShoulder": "mixamorig:Spine2",
|
|
1017
|
+
"mixamorig:LeftArm": "mixamorig:LeftShoulder",
|
|
1018
|
+
"mixamorig:LeftForeArm": "mixamorig:LeftArm",
|
|
1019
|
+
"mixamorig:LeftHand": "mixamorig:LeftForeArm",
|
|
1020
|
+
"mixamorig:RightShoulder": "mixamorig:Spine2",
|
|
1021
|
+
"mixamorig:RightArm": "mixamorig:RightShoulder",
|
|
1022
|
+
"mixamorig:RightForeArm": "mixamorig:RightArm",
|
|
1023
|
+
"mixamorig:RightHand": "mixamorig:RightForeArm",
|
|
1024
|
+
"mixamorig:LeftUpLeg": "mixamorig:Hips",
|
|
1025
|
+
"mixamorig:LeftLeg": "mixamorig:LeftUpLeg",
|
|
1026
|
+
"mixamorig:LeftFoot": "mixamorig:LeftLeg",
|
|
1027
|
+
"mixamorig:LeftToeBase": "mixamorig:LeftFoot",
|
|
1028
|
+
"mixamorig:LeftToe_End": "mixamorig:LeftToeBase",
|
|
1029
|
+
"mixamorig:RightUpLeg": "mixamorig:Hips",
|
|
1030
|
+
"mixamorig:RightLeg": "mixamorig:RightUpLeg",
|
|
1031
|
+
"mixamorig:RightFoot": "mixamorig:RightLeg",
|
|
1032
|
+
"mixamorig:RightToeBase": "mixamorig:RightFoot",
|
|
1033
|
+
"mixamorig:RightToe_End": "mixamorig:RightToeBase",
|
|
1034
|
+
"mixamorig:LeftHandThumb1": "mixamorig:LeftHand",
|
|
1035
|
+
"mixamorig:LeftHandThumb2": "mixamorig:LeftHandThumb1",
|
|
1036
|
+
"mixamorig:LeftHandThumb3": "mixamorig:LeftHandThumb2",
|
|
1037
|
+
"mixamorig:LeftHandThumb4": "mixamorig:LeftHandThumb3",
|
|
1038
|
+
"mixamorig:LeftHandIndex1": "mixamorig:LeftHand",
|
|
1039
|
+
"mixamorig:LeftHandIndex2": "mixamorig:LeftHandIndex1",
|
|
1040
|
+
"mixamorig:LeftHandIndex3": "mixamorig:LeftHandIndex2",
|
|
1041
|
+
"mixamorig:LeftHandIndex4": "mixamorig:LeftHandIndex3",
|
|
1042
|
+
"mixamorig:LeftHandMiddle1": "mixamorig:LeftHand",
|
|
1043
|
+
"mixamorig:LeftHandMiddle2": "mixamorig:LeftHandMiddle1",
|
|
1044
|
+
"mixamorig:LeftHandMiddle3": "mixamorig:LeftHandMiddle2",
|
|
1045
|
+
"mixamorig:LeftHandMiddle4": "mixamorig:LeftHandMiddle3",
|
|
1046
|
+
"mixamorig:LeftHandRing1": "mixamorig:LeftHand",
|
|
1047
|
+
"mixamorig:LeftHandRing2": "mixamorig:LeftHandRing1",
|
|
1048
|
+
"mixamorig:LeftHandRing3": "mixamorig:LeftHandRing2",
|
|
1049
|
+
"mixamorig:LeftHandRing4": "mixamorig:LeftHandRing3",
|
|
1050
|
+
"mixamorig:LeftHandPinky1": "mixamorig:LeftHand",
|
|
1051
|
+
"mixamorig:LeftHandPinky2": "mixamorig:LeftHandPinky1",
|
|
1052
|
+
"mixamorig:LeftHandPinky3": "mixamorig:LeftHandPinky2",
|
|
1053
|
+
"mixamorig:LeftHandPinky4": "mixamorig:LeftHandPinky3",
|
|
1054
|
+
"mixamorig:RightHandThumb1": "mixamorig:RightHand",
|
|
1055
|
+
"mixamorig:RightHandThumb2": "mixamorig:RightHandThumb1",
|
|
1056
|
+
"mixamorig:RightHandThumb3": "mixamorig:RightHandThumb2",
|
|
1057
|
+
"mixamorig:RightHandThumb4": "mixamorig:RightHandThumb3",
|
|
1058
|
+
"mixamorig:RightHandIndex1": "mixamorig:RightHand",
|
|
1059
|
+
"mixamorig:RightHandIndex2": "mixamorig:RightHandIndex1",
|
|
1060
|
+
"mixamorig:RightHandIndex3": "mixamorig:RightHandIndex2",
|
|
1061
|
+
"mixamorig:RightHandIndex4": "mixamorig:RightHandIndex3",
|
|
1062
|
+
"mixamorig:RightHandMiddle1": "mixamorig:RightHand",
|
|
1063
|
+
"mixamorig:RightHandMiddle2": "mixamorig:RightHandMiddle1",
|
|
1064
|
+
"mixamorig:RightHandMiddle3": "mixamorig:RightHandMiddle2",
|
|
1065
|
+
"mixamorig:RightHandMiddle4": "mixamorig:RightHandMiddle3",
|
|
1066
|
+
"mixamorig:RightHandRing1": "mixamorig:RightHand",
|
|
1067
|
+
"mixamorig:RightHandRing2": "mixamorig:RightHandRing1",
|
|
1068
|
+
"mixamorig:RightHandRing3": "mixamorig:RightHandRing2",
|
|
1069
|
+
"mixamorig:RightHandRing4": "mixamorig:RightHandRing3",
|
|
1070
|
+
"mixamorig:RightHandPinky1": "mixamorig:RightHand",
|
|
1071
|
+
"mixamorig:RightHandPinky2": "mixamorig:RightHandPinky1",
|
|
1072
|
+
"mixamorig:RightHandPinky3": "mixamorig:RightHandPinky2",
|
|
1073
|
+
"mixamorig:RightHandPinky4": "mixamorig:RightHandPinky3",
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
registerRetargetedAnimations(animations: AnimationData[], stateName: string): void {
|
|
1077
|
+
// Extract and store source rest/pre-rotations for pre-baking
|
|
1078
|
+
const sourceRests = animations[0]?.sourceRestRotations;
|
|
1079
|
+
const sourcePreRots = animations[0]?.sourcePreRotations;
|
|
1080
|
+
const sourceTrans = animations[0]?.sourceRestTranslations;
|
|
1081
|
+
|
|
1082
|
+
const restRotMap = new Map<string, [number, number, number, number]>();
|
|
1083
|
+
const preRotMap = new Map<string, [number, number, number, number]>();
|
|
1084
|
+
|
|
1085
|
+
// Keys are SOURCE node names — for Mixamo sources the full map is kept
|
|
1086
|
+
// (including bones with no target counterpart, since they still
|
|
1087
|
+
// participate in the source world-rotation chain).
|
|
1088
|
+
if (sourcePreRots) {
|
|
1089
|
+
for (const [boneName, quat] of sourcePreRots.entries()) {
|
|
1090
|
+
preRotMap.set(boneName, quat);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (sourceRests) {
|
|
1094
|
+
for (const [boneName, quat] of sourceRests.entries()) {
|
|
1095
|
+
restRotMap.set(boneName, quat);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if (restRotMap.size > 0) {
|
|
1099
|
+
this.sourceRestRotations.set(stateName, restRotMap);
|
|
1100
|
+
}
|
|
1101
|
+
if (preRotMap.size > 0) {
|
|
1102
|
+
this.sourcePreRotations.set(stateName, preRotMap);
|
|
1103
|
+
}
|
|
1104
|
+
if (sourceTrans && sourceTrans.size > 0) {
|
|
1105
|
+
this.sourceRestTranslations.set(stateName, new Map(sourceTrans));
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Merge all animation channels into one AnimationData, then pre-bake to AnimationClip
|
|
1109
|
+
const allChannels: AnimationChannel[] = [];
|
|
1110
|
+
let duration = 0;
|
|
1111
|
+
for (let i = 0; i < animations.length; i++) {
|
|
1112
|
+
const anim = animations[i];
|
|
1113
|
+
duration = Math.max(duration, anim.duration);
|
|
1114
|
+
for (let c = 0; c < anim.channels.length; c++) {
|
|
1115
|
+
const ch = anim.channels[c];
|
|
1116
|
+
const nodeName = ch.targetNode;
|
|
1117
|
+
if (this.isMixamoSkeleton) {
|
|
1118
|
+
if (this.boneNameToIndex.has(nodeName)) allChannels.push(ch);
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
// Non-Mixamo skeleton: keep mixamorig:* channels under their SOURCE
|
|
1122
|
+
// names (resolved by world-space retargeting, including unmapped bones
|
|
1123
|
+
// needed for the source chain); keep same-name channels that hit a bone.
|
|
1124
|
+
if (nodeName.startsWith("mixamorig:") || this.boneNameToIndex.has(nodeName)) {
|
|
1125
|
+
allChannels.push(ch);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
if (allChannels.length === 0) return;
|
|
1131
|
+
|
|
1132
|
+
const mergedAnim: AnimationData = {
|
|
1133
|
+
name: stateName,
|
|
1134
|
+
duration,
|
|
1135
|
+
channels: allChannels,
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
const clip = this.animationDataToClip(mergedAnim, stateName);
|
|
1139
|
+
if (clip) {
|
|
1140
|
+
this.clips.set(stateName, clip);
|
|
1141
|
+
log.info("Anim", `Registered: ${stateName} (${allChannels.length} channels, ${this.isMixamoSkeleton ? "direct Mixamo" : "retargeted-v2"})`);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
protected setAnimationState(state: AnimState): void {
|
|
1146
|
+
if (state === this.currentState && this.player.isPlaying(state)) return;
|
|
1147
|
+
if (this.timeSinceLastStateChange < 0.5 && this.player.isPlaying()) return;
|
|
1148
|
+
const clip = this.clips.get(state);
|
|
1149
|
+
if (!clip) return;
|
|
1150
|
+
|
|
1151
|
+
// Use AnimationPlayer's fade for crossfade blending
|
|
1152
|
+
this.player.play(state, clip, { fadeDuration: this.blendDuration });
|
|
1153
|
+
this.currentState = state;
|
|
1154
|
+
this.timeSinceLastStateChange = 0;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
setFootTargets(left: Float32Array | null, right: Float32Array | null): void {
|
|
1158
|
+
this.leftFootTarget = left;
|
|
1159
|
+
this.rightFootTarget = right;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
setIKEnabled(enabled: boolean): void {
|
|
1163
|
+
this.ikEnabled = enabled;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
hasAnimation(name: string): boolean {
|
|
1167
|
+
return this.clips.has(name);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Advance the animation clock without changing state — for callers that
|
|
1172
|
+
* hold a SkeletonAnimator by composition (e.g. CharacterAnimator/Preview)
|
|
1173
|
+
* rather than subclassing it.
|
|
1174
|
+
*/
|
|
1175
|
+
advance(dt: number): void {
|
|
1176
|
+
this.tick(dt);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
protected tick(dt: number): void {
|
|
1180
|
+
if (dt > 0.1) dt = 0.1;
|
|
1181
|
+
this.timeSinceLastStateChange += dt;
|
|
1182
|
+
|
|
1183
|
+
// AnimationPlayer handles time progression, blending, layering, and sampling
|
|
1184
|
+
this.player.update(dt);
|
|
1185
|
+
|
|
1186
|
+
// Copy output to local arrays
|
|
1187
|
+
const transforms = this.player.getBoneTransforms();
|
|
1188
|
+
const hasActiveAnim = this.player.isPlaying();
|
|
1189
|
+
|
|
1190
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
1191
|
+
this.localPos[i][0] = transforms.positions[i][0];
|
|
1192
|
+
this.localPos[i][1] = transforms.positions[i][1];
|
|
1193
|
+
this.localPos[i][2] = transforms.positions[i][2];
|
|
1194
|
+
this.localRot[i][0] = transforms.rotations[i][0];
|
|
1195
|
+
this.localRot[i][1] = transforms.rotations[i][1];
|
|
1196
|
+
this.localRot[i][2] = transforms.rotations[i][2];
|
|
1197
|
+
this.localRot[i][3] = transforms.rotations[i][3];
|
|
1198
|
+
this.localScale[i][0] = transforms.scales[i][0];
|
|
1199
|
+
this.localScale[i][1] = transforms.scales[i][1];
|
|
1200
|
+
this.localScale[i][2] = transforms.scales[i][2];
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// Apply procedural idle when no animation is playing
|
|
1204
|
+
if (!hasActiveAnim) {
|
|
1205
|
+
this.applyProceduralIdle(dt);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// Apply foot IK as post-processing
|
|
1209
|
+
if (this.ikEnabled) {
|
|
1210
|
+
this.applyFootIK();
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// Flatten for GPU upload
|
|
1214
|
+
for (let i = 0; i < this.boneCount; i++) {
|
|
1215
|
+
this.localPosFlat[i * 4] = this.localPos[i][0];
|
|
1216
|
+
this.localPosFlat[i * 4 + 1] = this.localPos[i][1];
|
|
1217
|
+
this.localPosFlat[i * 4 + 2] = this.localPos[i][2];
|
|
1218
|
+
this.localPosFlat[i * 4 + 3] = 0.0;
|
|
1219
|
+
this.localRotFlat[i * 4] = this.localRot[i][0];
|
|
1220
|
+
this.localRotFlat[i * 4 + 1] = this.localRot[i][1];
|
|
1221
|
+
this.localRotFlat[i * 4 + 2] = this.localRot[i][2];
|
|
1222
|
+
this.localRotFlat[i * 4 + 3] = this.localRot[i][3];
|
|
1223
|
+
this.localScaleFlat[i * 4] = this.localScale[i][0];
|
|
1224
|
+
this.localScaleFlat[i * 4 + 1] = this.localScale[i][1];
|
|
1225
|
+
this.localScaleFlat[i * 4 + 2] = this.localScale[i][2];
|
|
1226
|
+
this.localScaleFlat[i * 4 + 3] = 0.0;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
protected applyFootIK(): void {
|
|
1231
|
+
if (this.leftFootTarget && this.leftLegUpIdx >= 0 && this.leftLegDownIdx >= 0 && this.leftFootIdx >= 0) {
|
|
1232
|
+
this.applyTwoBoneIK(
|
|
1233
|
+
this.leftLegUpIdx, this.leftLegDownIdx, this.leftFootIdx,
|
|
1234
|
+
this.leftFootTarget,
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
if (this.rightFootTarget && this.rightLegUpIdx >= 0 && this.rightLegDownIdx >= 0 && this.rightFootIdx >= 0) {
|
|
1238
|
+
this.applyTwoBoneIK(
|
|
1239
|
+
this.rightLegUpIdx, this.rightLegDownIdx, this.rightFootIdx,
|
|
1240
|
+
this.rightFootTarget,
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
private applyTwoBoneIK(
|
|
1246
|
+
rootIdx: number,
|
|
1247
|
+
midIdx: number,
|
|
1248
|
+
endIdx: number,
|
|
1249
|
+
target: Float32Array,
|
|
1250
|
+
): void {
|
|
1251
|
+
const rootWorld = this.computeBoneWorldPos(rootIdx);
|
|
1252
|
+
const midWorld = this.computeBoneWorldPos(midIdx);
|
|
1253
|
+
const endWorld = this.computeBoneWorldPos(endIdx);
|
|
1254
|
+
|
|
1255
|
+
const targetY = target[1];
|
|
1256
|
+
|
|
1257
|
+
const dx = target[0] - rootWorld[0];
|
|
1258
|
+
const dy = targetY - rootWorld[1];
|
|
1259
|
+
const dz = target[2] - rootWorld[2];
|
|
1260
|
+
const targetDist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
1261
|
+
|
|
1262
|
+
let upperLen = 0;
|
|
1263
|
+
let lowerLen = 0;
|
|
1264
|
+
for (let i = 0; i < 3; i++) {
|
|
1265
|
+
const d = midWorld[i] - rootWorld[i];
|
|
1266
|
+
upperLen += d * d;
|
|
1267
|
+
}
|
|
1268
|
+
upperLen = Math.sqrt(upperLen);
|
|
1269
|
+
for (let i = 0; i < 3; i++) {
|
|
1270
|
+
const d = endWorld[i] - midWorld[i];
|
|
1271
|
+
lowerLen += d * d;
|
|
1272
|
+
}
|
|
1273
|
+
lowerLen = Math.sqrt(lowerLen);
|
|
1274
|
+
|
|
1275
|
+
if (upperLen < 0.001 || lowerLen < 0.001) return;
|
|
1276
|
+
|
|
1277
|
+
const maxReach = upperLen + lowerLen - 0.001;
|
|
1278
|
+
const minReach = Math.abs(upperLen - lowerLen) + 0.001;
|
|
1279
|
+
const clampedDist = Math.max(minReach, Math.min(maxReach, targetDist));
|
|
1280
|
+
|
|
1281
|
+
const cosKnee = (upperLen * upperLen + lowerLen * lowerLen - clampedDist * clampedDist) / (2 * upperLen * lowerLen);
|
|
1282
|
+
const kneeAngle = Math.acos(Math.max(-1, Math.min(1, cosKnee)));
|
|
1283
|
+
|
|
1284
|
+
const dirX = dx / targetDist;
|
|
1285
|
+
const dirY = dy / targetDist;
|
|
1286
|
+
const dirZ = dz / targetDist;
|
|
1287
|
+
|
|
1288
|
+
const restKneeAngle = Math.acos(Math.max(-1, Math.min(1,
|
|
1289
|
+
((midWorld[0] - rootWorld[0]) * (endWorld[0] - midWorld[0]) +
|
|
1290
|
+
(midWorld[1] - rootWorld[1]) * (endWorld[1] - midWorld[1]) +
|
|
1291
|
+
(midWorld[2] - rootWorld[2]) * (endWorld[2] - midWorld[2])) / (upperLen * lowerLen)
|
|
1292
|
+
)));
|
|
1293
|
+
|
|
1294
|
+
const kneeDelta = kneeAngle - restKneeAngle;
|
|
1295
|
+
if (Math.abs(kneeDelta) < 0.001) return;
|
|
1296
|
+
|
|
1297
|
+
const halfDelta = kneeDelta * 0.5;
|
|
1298
|
+
const rotQuat = new Float32Array([Math.sin(halfDelta), 0, 0, Math.cos(halfDelta)]);
|
|
1299
|
+
const newRot = new Float32Array(4);
|
|
1300
|
+
quatMul(this.localRot[midIdx], rotQuat, newRot);
|
|
1301
|
+
this.localRot[midIdx].set(newRot);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
private computeBoneWorldPos(boneIdx: number): Float32Array {
|
|
1305
|
+
const chain: number[] = [];
|
|
1306
|
+
let idx = boneIdx;
|
|
1307
|
+
while (idx >= 0) {
|
|
1308
|
+
chain.unshift(idx);
|
|
1309
|
+
idx = this.parentIndices[idx];
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const pos = new Float32Array(3);
|
|
1313
|
+
const tmpMat = new Float32Array(16);
|
|
1314
|
+
const accumMat = new Float32Array(16);
|
|
1315
|
+
accumMat[0] = 1; accumMat[5] = 1; accumMat[10] = 1; accumMat[15] = 1;
|
|
1316
|
+
|
|
1317
|
+
for (let c = 0; c < chain.length; c++) {
|
|
1318
|
+
const bi = chain[c];
|
|
1319
|
+
buildLocalMatrix(this.localPos[bi], this.localRot[bi], this.localScale[bi], tmpMat);
|
|
1320
|
+
const newAccum = new Float32Array(16);
|
|
1321
|
+
mat4Mul(accumMat, tmpMat, newAccum);
|
|
1322
|
+
accumMat.set(newAccum);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
pos[0] = accumMat[12];
|
|
1326
|
+
pos[1] = accumMat[13];
|
|
1327
|
+
pos[2] = accumMat[14];
|
|
1328
|
+
return pos;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
protected applyProceduralIdle(dt: number): void {
|
|
1332
|
+
this.proceduralTime += dt;
|
|
1333
|
+
const t = this.proceduralTime;
|
|
1334
|
+
|
|
1335
|
+
const breath = Math.sin(t * 1.5) * 0.5 + 0.5;
|
|
1336
|
+
if (this.spineIdx >= 0) {
|
|
1337
|
+
this.localScale[this.spineIdx][1] = 1.0 + breath * 0.02;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
const armSwing = Math.sin(t * 1.5) * 0.03;
|
|
1341
|
+
const tmpRot = new Float32Array(4);
|
|
1342
|
+
const deltaRot = new Float32Array(4);
|
|
1343
|
+
|
|
1344
|
+
if (this.leftUpperArmIdx >= 0) {
|
|
1345
|
+
const a = armSwing;
|
|
1346
|
+
deltaRot[0] = 0; deltaRot[1] = 0; deltaRot[2] = Math.sin(a * 0.5); deltaRot[3] = Math.cos(a * 0.5);
|
|
1347
|
+
quatMul(this.localRot[this.leftUpperArmIdx], deltaRot, tmpRot);
|
|
1348
|
+
this.localRot[this.leftUpperArmIdx].set(tmpRot);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
if (this.rightUpperArmIdx >= 0) {
|
|
1352
|
+
const a = -armSwing;
|
|
1353
|
+
deltaRot[0] = 0; deltaRot[1] = 0; deltaRot[2] = Math.sin(a * 0.5); deltaRot[3] = Math.cos(a * 0.5);
|
|
1354
|
+
quatMul(this.localRot[this.rightUpperArmIdx], deltaRot, tmpRot);
|
|
1355
|
+
this.localRot[this.rightUpperArmIdx].set(tmpRot);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if (this.headIdx >= 0) {
|
|
1359
|
+
const headSway = Math.sin(t * 0.8) * 0.03;
|
|
1360
|
+
deltaRot[0] = 0; deltaRot[1] = 0; deltaRot[2] = Math.sin(headSway * 0.5); deltaRot[3] = Math.cos(headSway * 0.5);
|
|
1361
|
+
quatMul(this.localRot[this.headIdx], deltaRot, tmpRot);
|
|
1362
|
+
this.localRot[this.headIdx].set(tmpRot);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
setNormalizationMatrix(mat: Float32Array, invMat: Float32Array): void {
|
|
1367
|
+
this.normalizationMatrix = mat;
|
|
1368
|
+
this.inverseNormalizationMatrix = invMat;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
getLocalPosFlat(): Float32Array { return this.localPosFlat; }
|
|
1372
|
+
getLocalRotFlat(): Float32Array { return this.localRotFlat; }
|
|
1373
|
+
getLocalScaleFlat(): Float32Array { return this.localScaleFlat; }
|
|
1374
|
+
getParentIndices(): Int32Array { return this.parentIndices; }
|
|
1375
|
+
getInverseBindMatricesFlat(): Float32Array { return this.inverseBindMatricesFlat; }
|
|
1376
|
+
getNormalizationMatrix(): Float32Array | null { return this.normalizationMatrix; }
|
|
1377
|
+
getInverseNormalizationMatrix(): Float32Array | null { return this.inverseNormalizationMatrix; }
|
|
1378
|
+
getBoneCount(): number { return this.boneCount; }
|
|
1379
|
+
getCurrentState(): AnimState { return this.currentState; }
|
|
1380
|
+
getRootMotionDelta(): [number, number, number] { return this.player.getRootMotionDelta(); }
|
|
1381
|
+
getPlayer(): AnimationPlayer { return this.player; }
|
|
1382
|
+
getSkeleton(): Skeleton { return this.skeleton; }
|
|
1383
|
+
|
|
1384
|
+
onAnimationEvent(type: string, handler: (event: AnimationEvent) => void): void {
|
|
1385
|
+
this.player.onEvent(type, handler);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
offAnimationEvent(type: string, handler?: (event: AnimationEvent) => void): void {
|
|
1389
|
+
this.player.offEvent(type, handler);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
getPendingAnimationEvents(): AnimationEvent[] {
|
|
1393
|
+
return this.player.getPendingEvents();
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
clearPendingAnimationEvents(): void {
|
|
1397
|
+
this.player.clearPendingEvents();
|
|
1398
|
+
}
|
|
1399
|
+
}
|