viewer,ship: external parts ON by default + Ships catalog = composite ships only

Fixes the two things the user saw in the Ships browser:
- "Can't find the thrusters": show_external defaulted OFF, hiding the engine
  rig / bridge / turrets entirely. Externals are EXACT since the node-TRS fix,
  so the toggle now defaults ON (relabelled; off = bare hull bodies).
- "Some parts very far away": families without a composite scene graph (fighter
  morph sets like f002 whose detached parts are authored far from the origin,
  shared turret/prop part families) fell into the stack-at-origin fallback and
  rendered as piles with stray pieces. ShipModel now carries has_composite and
  the catalog lists only real assembled ships.

Offline audit across ALL stages (ship_audit example): the only far-away
outliers were composite-less f002 morph parts (now filtered); every capital
ship assembles coherently (e108 places its 7 turret instances, f105 its
mirrored shield generators). All primary composites are single-key tracks —
multikey exists only in animation composites (e901 attacks, missile-open),
which assemble_ship does not select.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-26 19:15:34 +02:00
parent 737b61242e
commit 31f2637e6c
4 changed files with 177 additions and 7 deletions

View File

@@ -613,7 +613,7 @@ pub struct ShipRow {
}
/// The Ships browser state — capital ships assembled from XBG7 part families.
#[derive(Resource, Default)]
#[derive(Resource)]
pub struct ShipBrowser {
pub open: bool,
pub loading: bool,
@@ -624,11 +624,27 @@ pub struct ShipBrowser {
pub rows: Vec<ShipRow>,
/// Family id of the ship currently rendered (for row highlight).
pub selected: Option<String>,
/// Also place the approximate external parts (bridge / shield gen / engines at
/// their `GN_*` hardpoint frames). Off by default → the exact hull only.
/// Also place the external parts: bridge / shield generators / the engine
/// cluster rig / cross-mounted turrets. EXACT (capture-validated) since the
/// node-TRS fix, so ON by default; off = bare hull bodies only.
pub show_external: bool,
}
impl Default for ShipBrowser {
fn default() -> Self {
Self {
open: false,
loading: false,
loaded: false,
filter: String::new(),
faction: String::new(),
rows: Vec::new(),
selected: None,
show_external: true,
}
}
}
/// Ask the loader to scan the stage containers and build the ship catalog.
#[derive(Event, Default)]
pub struct RequestShipCatalog;
@@ -3860,8 +3876,11 @@ fn build_ship_catalog(source: &SourceKind) -> Vec<ShipRow> {
};
let label = format!("S{n:02}");
for sm in ships_in_container(&bytes) {
// Skip single-piece props / debris — not "whole ships".
if sm.parts.len() < 2 {
// Skip single-piece props / debris — not "whole ships" — and any
// family with no composite scene graph (fighter morph sets, shared
// turret parts): those have no placement data and would render as a
// pile at the origin with stray far-away pieces.
if sm.parts.len() < 2 || !sm.has_composite {
continue;
}
let v = vmap.get(&sm.id);

View File

@@ -1590,7 +1590,7 @@ fn draw_ships_ui(
}
});
ui.horizontal(|ui| {
ui.checkbox(&mut ships.show_external, "Show external parts");
ui.checkbox(&mut ships.show_external, "External parts (bridge/engines/turrets)");
ui.label(
egui::RichText::new("(bridge / shield gens / engines — approximate placement)")
.weak()