Files
2026-05-27 23:29:53 +01:00

172 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenSpec Change: enterprise-gaming-animation-events
- **Change ID**: enterprise-gaming-animation-events
- **Package**: `@suaveplan/animation-events`
- **Category**: gaming/rigs
- **Target path**: `gaming/rigs/animation-events`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/animation-events.md`
---
## Summary
Deliver `@suaveplan/animation-events` on the genesis branch from scratch at enterprise quality. The package provides animation event dispatching, foot contact detection, and procedural animation modifiers. An `AnimationEventTrack` associates named events with normalized animation times (0–1); an `AnimationEventDispatcher` fires `@suaveplan/events` messages when playback crosses event timestamps. `FootContactDetector` analyses Y-velocity of ankle bones to determine ground contact frames. Procedural modifiers (head bob, breathing sway, aim offset) compose additively. Genesis adds Zod schemas, structured errors, OTel event-count counter, and 100% coverage.
---
## Current-state audit (genesis)
Package does not exist on genesis yet — fresh build.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | `AnimationEventsError`; `ANIM_EVENT.*` codes |
| `@suaveplan/types` (`workspace:^`) | `Clock` |
| `@suaveplan/events` (`workspace:^`) | Event bus for dispatching animation triggers |
| `@suaveplan/ik-solver` (`workspace:^`) | `solveLookAt` for aim-offset modifier |
| `zod` (`catalog:forms`) | Track and modifier schemas |
| `@opentelemetry/api` (`catalog:observability`) | Event dispatch counter |
---
## Public API surface
```ts
// Event track
interface AnimationEvent {
name: string
normalizedTime: number // 0–1
data?: unknown
}
function createEventTrack(events: AnimationEvent[]): AnimationEventTrack
interface AnimationEventTrack {
getEvents(fromNorm: number, toNorm: number): AnimationEvent[]
readonly events: readonly AnimationEvent[]
}
// Dispatcher
function createAnimationEventDispatcher(options: DispatcherOptions): AnimationEventDispatcher
interface DispatcherOptions {
eventBus: EventBusLike
namespace?: string // event name prefix, default 'anim'
}
interface AnimationEventDispatcher {
update(clipName: string, track: AnimationEventTrack, normalizedTime: number, prevNormalizedTime: number): void
}
// Foot contact detection
function createFootContactDetector(options: FootContactOptions): FootContactDetector
interface FootContactOptions {
ankleThresholdY?: number // velocity below which foot is considered planted, default 0.05
sampleWindow?: number // frames to average, default 3
}
interface FootContactDetector {
update(leftAnkleY: number, rightAnkleY: number, dt: number): FootContactState
}
interface FootContactState {
leftGrounded: boolean
rightGrounded: boolean
leftPlantedAt?: Vec3
rightPlantedAt?: Vec3
}
// Procedural modifiers
interface ProceduralModifier {
update(dt: number, input: ModifierInput): BonePose[]
}
function createHeadBobModifier(options: HeadBobOptions): ProceduralModifier
function createBreathingModifier(options: BreathingOptions): ProceduralModifier
function createAimOffsetModifier(options: AimOffsetOptions): ProceduralModifier
```
### Error codes
```ts
type AnimEventErrorCode =
| 'ANIM_EVENT_TRACK_EMPTY'
| 'ANIM_EVENT_INVALID_TIME'
| 'ANIM_EVENT_DISPATCH_FAILED'
```
---
## New additions for genesis
1. **Looping event detection** — dispatcher handles wrap-around (prevNorm > normTime) correctly, firing events in the wrapped range.
2. **Foot IK plant correction** — `FootContactDetector` outputs planted world positions; caller feeds into `@suaveplan/ik-solver` for foot placement.
3. **Aim offset modifier** — uses `solveLookAt` to rotate spine/neck toward aim target additively.
---
## File layout
```text
gaming/rigs/animation-events/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/index.ts
types/common.ts
types/common.md
core/
event-track/
event-track.ts
event-track.md
event-track.types.ts
event-track.test.ts
index.ts
dispatcher/
dispatcher.ts
dispatcher.md
dispatcher.types.ts
dispatcher.test.ts
index.ts
foot-contact/
foot-contact.ts
foot-contact.md
foot-contact.types.ts
foot-contact.test.ts
index.ts
modifiers/
head-bob/
head-bob.ts
head-bob.md
head-bob.test.ts
index.ts
breathing/
breathing.ts
breathing.md
breathing.test.ts
index.ts
aim-offset/
aim-offset.ts
aim-offset.md
aim-offset.test.ts
index.ts
tests/
integration.test.ts
```
---
## Impact
**Downstream**: `@suaveplan/r3f-animation` hooks these modifiers into Three.js AnimationMixer. Prerequisites: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/ik-solver`.