Skip to content

[FEATURE] Animation graph, IK, root motion, and animation events #1683

Description

@Noisemaker111

Problem: skeletal animation was one hardcoded three-state machine. Done in #1694: packages/core/src/anim/animGraph.ts (AnimGraph, createAnimGraphRuntime), packages/core/src/anim/locomotionGraph.ts, ModelAnimationConfig.graph, the graph path in packages/shell/src/render/useModelAnimation.ts, and the animation.event event. Read those before starting.

Tasks, one PR each, in order:

  1. Graph becomes the only path. In useModelAnimation.ts, when animation.states is set and animation.graph is not, build the graph with locomotionGraph({ idle, walk, run, walkSpeed, runSpeed, fadeSec, oneShots }) (for string[] one-shot variants pick with resolveOneShotClip at trigger time and set the state clip through a per-variant state) and delete the old stateActionsRef machine and one-shot code. Keep the single-clip and paused/time paths. Verify with bun run shoot claudecraft --mode play before and after (same rig, same pose) plus a bun run pr-video clip of walk to run to attack. CHANGELOG Changed.

  2. IK solver. New packages/core/src/anim/ikSolver.ts: solveTwoBone({ root, mid, tip, target, pole }, out), solveFabrik(chain, target, { iterations, tolerance }), lookAt({ from, target, up, maxYaw, maxPitch }); pure vector math over [x,y,z] tuples, no three. Tests against known geometry. bun run gen.

  3. Foot IK in the shell. New packages/shell/src/render/useFootIk.ts: after mixer.update, for bones named by ModelConfig.ik?: { feet: { root: string; mid: string; tip: string }[] ; lookAt?: { bone: string } }, raycast down from each foot with ctx.scene.raycast, run solveTwoBone, write bone quaternions, and blend by a weight that fades out while airborne. Wire it from SceneModels.tsx. Clip on a KayKit character standing on a terrain() slope.

  4. Root motion. On load, read each clip's root-bone position track into AnimGraphClipInfo (widen it to { duration: number; rootTrack?: { times: Float32Array; values: Float32Array } }; keep plain numbers accepted for compatibility). Add rootMotion?: boolean to AnimState; when set, advance returns rootDelta: [x, y, z] for the time slice, and the shell zeroes the root bone translation and applies rootDelta via ctx.scene.entity.setPose. Test in animGraph.test.ts with a synthetic root track.

  5. Instanced skinned crowds. New packages/shell/src/render/SkinnedInstances.tsx: bake each clip of a rig into a vertex-animation texture (bone matrices per frame) and render N instances with a custom ShaderMaterial sampling it (per-instance clip index, time offset). Prove with a scene of 200 animated humanoids in a clip and a frame-time number in the PR.

  6. Editor Animation workspace. Flip supported: false in packages/editor/src/shell/WorkspaceRail.tsx; new packages/editor/src/AnimationPanel.tsx listing layers, states, and transitions of the selected marker's model graph (stored through packages/editor/src/modelAnimationAuthoring.ts meta), a scrub slider driving runtime.advance, and a trigger button per transition. Shots of the panel. Closes #1683.

Rules: wait for bun run agent:bootstrap --check; branch claude/<slug> off origin/main; claim comment first; one task per PR with Refs #1683; bun run gen after export changes (JSDoc on every export); CHANGELOG bullet; check-types and test on core, shell, editor as touched plus check-stateful-ratchet, check-doc-symbols, check-orphan-ratchet; merge origin/main before pushing; shots via bun run games:clone, bun run shoot daemon start, bun run shoot <game> --mode play --size half, clips via bun run pr-video, publish with bun run pr-shots; squash auto-merge; fix CI on the same branch.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions