Files
2026-09-28 18:35:44 +03:00

55 lines
2.9 KiB
Markdown

# Animation: what lives where
Three floors, deliberately different: the **curve facts** (pure mathematics),
the **runtime tree** (pose state) and **who picked which clip** (the game).
## `anim_iw4` — facts only, `#![no_std]`, no clock and no allocator
* `XAnimParts` — curve storage, unpacking of `half_quat` / `full_quat` and
quantized translations (`XAnimCalcParts`); translations are **deltas**;
* span/sample/`time_to_frame` — a pure function of (clip, time);
* the blend rule `bind + scale * Δ`, rotation replaced (`DObjCompose`);
* `DObj` — how model, skeleton and attachments compose (0x9c, prefix mapped out);
* `XBoneInfo` — the OBBs for bone hits (`DObjGeom_RayBones`);
* `PartBits` — the six-word requested/ignore mask (`DObjCalcAnimInternal`);
* `PlayerAnimValue` — 10-bit `legsAnim` / `torsoAnim`, a 9-bit index and the
`0x200` "restart" bit: a restart changes **only** that bit, not the index;
* the leaf time step and the goal-weight ramp are measured IW4 constants;
* script name tables: `ANIM_MT_NAMES`, `ANIM_BODY_PART_NAMES`, `ANIM_COND_NAMES`.
No `Vec`, no `AnimationClip`, no glam matrices and no playback clock here.
## `xmodel_runtime` — a runtime without Bevy and without ECS
A pure evaluator, used by **both** the authority **and** the client:
* `XAnimTreeDefinition` (the immutable hierarchy) + `XAnimTreeRuntime` (state;
leaf time normalized into `[0,1]`, `update` is the per-leaf advance);
* `DObjAnimRuntime` — `update` / `pose_request` / `compose`; DObj reuse is
equality of `(eType, model*)`, not "always a fresh one";
* `RetainedModelCapability` — pose, `XBoneInfo`, collSurf/collTris for hitscan;
* `build_body_head_weapon_dobj` — body + head on `j_spine4` + weapon on its tag.
A missing clip **never** turns into the bind pose; additive nodes are a typed
gap, not "ordinary blending".
## Who picks the clip
* `sim` — `player_anim_script.rs`, `mantle_xanim.rs`, `anim_script_gap.rs`:
what animation the player is in, and it travels into the snapshot as
semantics, not as a tree;
* `assets` — `XAnimParts` → owning clips;
* `render_frontend/adapters/anim/` + `render_anim` — the viewmodel (`fpv*.rs`,
`viewmodel_controller.rs`), third person (`third_person.rs`, `remote_body.rs`),
kick and sway (`view_kick.rs`, `view_sway.rs`), items and projectiles.
FPV actions come from the presented `weapAnim`, **never** from the
keyboard; the identity of the FPV mesh is
`weapon_iw4::get_viewmodel_weapon_index`.
The FPV consumer reads `PreparedFpv` only, never the weapon registry: views,
rigs and material bindings are built before Ready; an equip picks one, and a
mandatory material that failed is a refusal naming model, surface and
material. Remote kits (`PreparedRemoteKits`: models, hide bits, shared DObj)
and dropped items are composed at install, not on first sight.
To watch it live: `IW4L_PERF=1` / [`PERF.md`](PERF.md).