ship: engine placement CONFIRMED by 43-instance capture; exhaust markers added

capture_verify example: clusters every part's ship-relative transform across
the new multi-snapshot, multi-instance F10 captures (5 snapshots, 43 e106
instances, all angles). Verdict: the dominant clusters match the static
assembly to ~1 unit on EVERY part — including BOTH engine nacelles at
(+-131, -133, -131) — so the "engines inside the hull" appearance is the
game's own placement: the engine geometry sits recessed in the aft hull, and
the visible "thrusters" in-game are exhaust FX drawn at the GN_Jet/GN_SJet
frames (Z ~ -570, past the stern).

To close that perception gap the viewer now draws simple exhaust cones at the
game's own jet frames (ship::exhaust_frames; part of the external-parts
toggle). The jet frames flip Z, so the cone apex is authored at +Z and lands
trailing aft — verified in the offline render.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-26 19:44:12 +02:00
parent 31f2637e6c
commit ea32e77e45
4 changed files with 285 additions and 0 deletions

View File

@@ -45,6 +45,22 @@ fn main() {
p.resource, lo[0], hi[0], lo[1], hi[1], lo[2], hi[2]
);
}
// Exhaust cones at the GN_Jet/GN_SJet frames (the game's visible thrusters).
for f in sylpheed_formats::ship::exhaust_frames(&bytes, id) {
const SEG: usize = 12;
let (r, len) = (22.0f32, 140.0f32);
let ring: Vec<[f32; 3]> = (0..SEG)
.map(|s| {
let a = s as f32 / SEG as f32 * std::f32::consts::TAU;
f.apply([a.cos() * r, a.sin() * r, 0.0])
})
.collect();
let apex = f.apply([0.0, 0.0, len]);
for s in 0..SEG {
tris.push([apex, ring[(s + 1) % SEG], ring[s]]);
}
println!(" exhaust frame {:16} T=[{:8.1}{:8.1}{:8.1}]", f.resource, f.t[0], f.t[1], f.t[2]);
}
println!("total {} tris from {} placements", tris.len(), placed.len());
// Orthographic z-buffered flat renders: top (X/Z, look down Y) and side (Z/Y, look down X).