feat: initial implementation with build_rig + build_animation
Validates and cooks rig + animation data into internal representations: - build_rig: parses bones (with parent-resolution to object refs) and tracks (with bone-uniqueness validation). Converts rest angles from JSON degrees to radians. - build_animation: validates each keyframe references only bones in the animation's declared track. Converts keyframe angles from degrees to radians. Remaining functions (sample_animation, spawn, update, look-at, procedural, render, lifecycle) follow in subsequent commits.
This commit is contained in:
109
README.md
Normal file
109
README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# lib-core.puppet
|
||||
|
||||
Skeletal animation primitive for top-down 2D characters: bones,
|
||||
tracks, rest pose, keyframe animations, procedural animations
|
||||
(Lua-side), and look-at constraint. No footplant IK in v0.1
|
||||
(deferred).
|
||||
|
||||
**Version:** 0.1.0
|
||||
**Lib-ID:** lib-core.puppet
|
||||
**Requires:** (none — pure Lua + engine.render.*)
|
||||
**Tags:** animation, skeleton, puppet, character
|
||||
|
||||
## Topology
|
||||
|
||||
<!-- topology:start (auto-generated; do not edit) -->
|
||||
<!-- topology:end -->
|
||||
|
||||
## API
|
||||
|
||||
### `puppet.build_rig(rig_table)`
|
||||
|
||||
Validates a rig table (parsed from `*.rig.json`) and returns an
|
||||
internal representation with `bones_by_id`, parent-resolved object
|
||||
references, and track-membership maps. Raises on invalid data
|
||||
(missing parents, bones in 2 tracks, etc.).
|
||||
|
||||
### `puppet.build_animation(anim_table, rig)`
|
||||
|
||||
Validates an animation table against a built rig (bones referenced
|
||||
in keyframes must exist and be in the animation's declared track).
|
||||
|
||||
### `puppet.sample_animation(animation, t)`
|
||||
|
||||
Returns a frame `{ <bone_id> = { angle = <number>, ... }, ... }`
|
||||
sampling the animation at time `t` with linear interpolation. Wraps
|
||||
on `t > duration` if `animation.loop` is true.
|
||||
|
||||
### `puppet.spawn(rig, { x, y })`, `puppet.despawn(handle)`
|
||||
|
||||
Spawn a new puppet instance at world position (x, y). Returns an
|
||||
opaque handle. `despawn` removes the instance.
|
||||
|
||||
### `puppet.update(dt)`, `puppet.render(handle)`
|
||||
|
||||
`update(dt)` runs the pipeline for ALL spawned puppets (rest →
|
||||
look-at → keyframes → procedural). `render(handle)` draws ONE
|
||||
puppet via `engine.render.draw_rect_rotated` per bone.
|
||||
|
||||
### `puppet.set_look_target(p, world_x, world_y)`, `puppet.clear_look_target(p)`
|
||||
|
||||
Set or clear the look-at target. Bones marked `look_at: true` in the
|
||||
rig rotate to face the target.
|
||||
|
||||
### `puppet.bone_angle(p, bone_id)`
|
||||
|
||||
Returns the current world-space angle of `bone_id` (after the
|
||||
full pipeline tick). Used by tests + by callers needing bone-state.
|
||||
|
||||
### `puppet.set_procedural(p, name, callback)`, `puppet.clear_procedural(p, name)`
|
||||
|
||||
Register or unregister a per-frame procedural callback. Callback
|
||||
signature: `function(handle, dt)`. Inside the callback, mutate
|
||||
bones via `puppet.write_bone(handle, bone_id, { angle = <number> })`.
|
||||
|
||||
### `puppet.play(p, animation_name, { loop, speed })`, `puppet.stop(p, animation_name)`, `puppet.stop_all(p)`, `puppet.is_playing(p, animation_name)`
|
||||
|
||||
Keyframe-layer controls. Animation must be registered via
|
||||
`puppet.register_animation(p, anim)` first.
|
||||
|
||||
### `puppet.register_animation(p, animation)`
|
||||
|
||||
Binds a built animation (from `build_animation`) to a puppet
|
||||
instance, making it available to `play`.
|
||||
|
||||
### `puppet.load_rig(path)`, `puppet.load_animation(path)`
|
||||
|
||||
Convenience wrappers: read JSON file via `engine.asset.read_file` +
|
||||
parse + call `build_rig` / `build_animation`.
|
||||
|
||||
### `puppet.position(p)`, `puppet.facing(p)`, `puppet.move_to(p, x, y)`
|
||||
|
||||
Puppet owns its world position. `move_to` sets it instantly (caller
|
||||
applies speed). `facing` returns the last-movement-direction angle.
|
||||
|
||||
### `puppet.write_bone(handle, bone_id, { angle })`
|
||||
|
||||
Procedural-callback-only API to mutate a bone. Validates track
|
||||
ownership at write time.
|
||||
|
||||
## Conventions
|
||||
|
||||
- World-coords + pixel-units throughout (per ADR-0031).
|
||||
- Bone angles in radians (internal). JSON rest-angles and animation
|
||||
keyframe angles are in degrees, converted on load.
|
||||
- Update pipeline runs once per frame (`puppet.update(dt)`) for ALL
|
||||
spawned puppets. `render` is per-puppet so callers can interleave
|
||||
with other rendering.
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
### v0.1.0
|
||||
- Initial release: skeleton + bones + tracks + rest + keyframe +
|
||||
procedural + look-at constraint. Footplant IK deferred.
|
||||
|
||||
## References
|
||||
|
||||
- `architecture/puppet.md` (Reference)
|
||||
- ADR-0037 (Tests-as-Libs)
|
||||
- ADR-0038 (API-Doc-Convention)
|
||||
Reference in New Issue
Block a user