mirror of
https://github.com/vladtrc/iw4L
synced 2026-10-28 02:56:40 -04:00
38aa9cf741
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.9 KiB
Markdown
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).
|