Files
benilla-octo/crates/benilla-formats/tests/selection_ring_radius.rs
T
SamandClaude Fable 5 2ad0043523 benilla: initial public release
A from-scratch World of Warcraft 1.12.1 client in Rust + Bevy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:27:01 +07:00

59 lines
2.9 KiB
Rust
Raw 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.
//! Pins the selection-ring radius against the **pixel-measured reference** on real build-5875 assets.
//!
//! The reference client sizes a **living unit's** ground selection ring (draw `0x608e00`, sizer
//! `0x60aee0`) as:
//!
//! ```text
//! ring_radius = OBJECT_FIELD_SCALE_X × sqrt( 0.5 · sqrt(dx² + dy²) )
//! ```
//!
//! where `dx,dy` are the horizontal (X,Y) extents of the unit's **Stand** animation bounding box (the M2
//! `M2Sequence` CAaBox), Stand being animation **id 0** via `animationLookup[0]`. This was byte-traced
//! *and* Unicorn-emulated in wow-re (`system/object-layer/scratch/selection-ring-scale.md`) and
//! reproduces the reference apitrace's measured ring radii to ~1 mm. It is NOT the render bounding sphere
//! (`0xCC`) — that (`0x5d6fe0`, `[unit+0x2b0]`) is the *corpse* decal; the render sphere over-sized tall
//! humans and under-sized the squat chicken because it folds in height, whereas the nested-sqrt footprint
//! compresses range.
//!
//! [`M2Bounds::ring_footprint`] is the model-local (pre-scale) part; the world radius is it ×
//! `OBJECT_FIELD_SCALE_X`. The four creatures below were the ones captured in the reference trace, drawn
//! life-size (scale ≈ 1.0), so `ring_footprint` ≈ the measured world radius. Skips when the gitignored
//! client data isn't present.
use benilla_formats::{load_m2_bounds, open_chain};
#[test]
fn ring_footprint_matches_reference_pixels() {
let data = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../WoW/Data");
if !data.is_dir() {
eprintln!("skipping: vanilla client not present at {}", data.display());
return;
}
let mut chain = open_chain(&data).expect("open chain");
// (model, ring radius measured from the reference apitrace at scale 1.0). The Stand-box footprint
// must reproduce these; a header-render-box or render-sphere source does not.
let cases = [
("Creature\\Chicken\\Chicken.mdx", 0.572_f32),
("Character\\Human\\Female\\HumanFemale.mdx", 0.731),
("Character\\Human\\Male\\HumanMale.mdx", 0.841),
("Creature\\Horse\\Horse.mdx", 1.295),
];
for (path, measured) in cases {
let b = load_m2_bounds(&mut chain, path).unwrap_or_else(|e| panic!("bounds {path}: {e}"));
assert!(
(b.ring_footprint - measured).abs() < 0.01,
"{path}: ring_footprint {:.4} should reproduce the reference-measured {measured} (Stand-box \
sqrt(0.5·sqrt(dx²+dy²)))",
b.ring_footprint
);
// Guard against a regression to the (wrong) corpse-path render-sphere source: for all four it is
// far off (0.5×sphere = 0.33 / 1.19 / 1.39 / 1.96), so it can't sneak past the bound above.
assert!(
(b.ring_footprint - 0.5 * b.sphere_radius).abs() > 0.05,
"{path}: ring_footprint must not coincide with the corpse-path 0.5×renderSphere"
);
}
}