Files
benilla-octo/crates/benilla-formats/tests/ribbons.rs
T
SamandClaude Fable 5 6b980b9efb the player build: one binary that carries its content
- a player build ships beside the dev one — dev chords and probe surfaces go dark, and a gate enforces what a player can still reach, by medium not by module
- the binary carries its own shaders and its shipped UI, finds the WoW folder by itself, and keeps its local state in one benilla-config folder
- the glue screens get a capture shutter, and the engine stops depending on the protocol crate

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:18:02 +07:00

65 lines
2.3 KiB
Rust

//! Difftest the vanilla M2 ribbon-emitter parser against real trail-carrying models (wow-5875-re
//! `ribbon-emitter-spec.md` field map). Skips (passes) when the client isn't present.
use benilla_formats::{open_chain, parse_m2_ribbon_emitters};
#[test]
fn ribbon_records_match_real_bytes() {
let data = benilla_formats::wow_data_or_skip!();
let mut chain = open_chain(&data).expect("open vanilla patch chain");
// The red wisp — three streamers trailing an animated creature.
let wisp =
parse_m2_ribbon_emitters(&chain.read_file("Creature\\WISP\\WispRed.m2").unwrap()).unwrap();
assert_eq!(wisp.len(), 3, "WispRed authors three ribbons");
for (i, r) in wisp.iter().enumerate() {
assert!(
r.edges_per_second > 0.0 && r.edges_per_second < 1000.0,
"ribbon {i} sane edge rate, got {}",
r.edges_per_second
);
assert!(
r.edge_lifetime >= 0.25,
"ribbon {i} lifetime respects the reference's 0.25 s load clamp"
);
assert!(
r.height_above.peak() + r.height_below.peak() > 0.0,
"ribbon {i} has a nonzero peak cross-section"
);
assert!(
r.texture.is_some(),
"ribbon {i} resolves a trail texture from the M2 textures table"
);
for &(_, a) in &r.alpha.keys {
assert!(
(0.0..=1.0).contains(&a),
"ribbon {i} fixed16 alpha decodes into 0..1, got {a}"
);
}
for &(_, c3) in &r.color.keys {
for c in c3 {
assert!((0.0..=1.5).contains(&c), "ribbon {i} color channel {c}");
}
}
}
// The Thunderblade — a weapon whose enchant trail rides the item root.
let blade = parse_m2_ribbon_emitters(
&chain
.read_file("ITEM\\ObjectComponents\\WEAPON\\Sword_1H_Thunderblade_A_01.m2")
.unwrap(),
)
.unwrap();
assert_eq!(blade.len(), 3, "Thunderblade authors three ribbons");
assert!(blade.iter().all(|r| r.texture.is_some()));
// A ribbon-less model parses to empty (the overwhelming majority).
let torch = parse_m2_ribbon_emitters(
&chain
.read_file("World\\Generic\\PassiveDoodads\\Lights\\Torch.m2")
.unwrap(),
)
.unwrap();
assert!(torch.is_empty(), "the torch has no ribbons");
}