ship: apply scale + gate approximate external parts behind a toggle
Two changes after user feedback that externals sit near-but-wrong (shield inset into the hull, thruster floating close): - Scale: the joint table's slots 5–7 are per-axis scale (a GN_ShieldG frame ships at 0.5, GN_Jet FX at 2.4–6.5) and were being ignored, rendering scaled parts at the wrong size. ScenePart now carries `s` and applies R·(S·v)+T. - External placement is fundamentally approximate: a GN_* frame is the mount PIVOT (f105's two GN_ShieldG frames are both on the centreline), while the part's outboard/rotational offset lives in a detail sub-rig / runtime code this static pass can't recover. So assemble_ship gains `include_external`: the hull tier (rou_ nodes) is exact and always drawn; the external tier (bridge / shield / engine at GN_ frames) is opt-in via a "Show external parts" checkbox in the Ships browser, off by default so the clean, correct hull is the default view. The module doc is corrected to the scene-graph mechanism (the old "draw parts untransformed" claim was wrong). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -624,6 +624,9 @@ 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.
|
||||
pub show_external: bool,
|
||||
}
|
||||
|
||||
/// Ask the loader to scan the stage containers and build the ship catalog.
|
||||
@@ -637,6 +640,8 @@ pub struct RequestShipRender {
|
||||
pub file: String,
|
||||
/// Ship family id (`e106`) — drives the scene-graph assembly.
|
||||
pub id: String,
|
||||
/// Also place the approximate external hardpoint parts.
|
||||
pub external: bool,
|
||||
/// Display label for the model info line.
|
||||
pub label: String,
|
||||
}
|
||||
@@ -3904,7 +3909,8 @@ fn handle_ship_render_request(
|
||||
let Some(req) = events.read().last() else {
|
||||
return;
|
||||
};
|
||||
let (file, id, label) = (req.file.clone(), req.id.clone(), req.label.clone());
|
||||
let (file, id, external, label) =
|
||||
(req.file.clone(), req.id.clone(), req.external, req.label.clone());
|
||||
|
||||
// Supersede any in-flight XPR/stage decode, exactly like a file selection.
|
||||
xpr_load.generation = xpr_load.generation.wrapping_add(1);
|
||||
@@ -3919,7 +3925,7 @@ fn handle_ship_render_request(
|
||||
let source = iso_state.source_kind.clone();
|
||||
let sender = channels.sender.clone();
|
||||
std::thread::spawn(move || {
|
||||
let prepared = build_ship_model(&source, &file, &id, &label, &cancel);
|
||||
let prepared = build_ship_model(&source, &file, &id, external, &label, &cancel);
|
||||
let _ = sender.send(IsoLoaderMsg::XprPrepared {
|
||||
generation,
|
||||
prepared: Box::new(prepared),
|
||||
@@ -3935,6 +3941,7 @@ fn build_ship_model(
|
||||
source: &SourceKind,
|
||||
file: &str,
|
||||
id: &str,
|
||||
external: bool,
|
||||
label: &str,
|
||||
cancel: &Arc<AtomicBool>,
|
||||
) -> PreparedXpr {
|
||||
@@ -3947,7 +3954,7 @@ fn build_ship_model(
|
||||
|
||||
// Scene-graph placement: which resources to draw and where (bridge fore,
|
||||
// engines aft, hardpoints on their frames).
|
||||
let placed = sylpheed_formats::ship::assemble_ship(&bytes, id);
|
||||
let placed = sylpheed_formats::ship::assemble_ship(&bytes, id, external);
|
||||
if placed.is_empty() {
|
||||
return PreparedXpr::Info(format!("No parts found for {label}."));
|
||||
}
|
||||
|
||||
@@ -1589,9 +1589,18 @@ fn draw_ships_ui(
|
||||
ships.filter.clear();
|
||||
}
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut ships.show_external, "Show external parts");
|
||||
ui.label(
|
||||
egui::RichText::new("(bridge / shield gens / engines — approximate placement)")
|
||||
.weak()
|
||||
.small(),
|
||||
);
|
||||
});
|
||||
ui.separator();
|
||||
|
||||
let ShipBrowser { rows, filter, faction, selected, .. } = &mut *ships;
|
||||
let ShipBrowser { rows, filter, faction, selected, show_external, .. } = &mut *ships;
|
||||
let show_external = *show_external;
|
||||
let needle = filter.to_lowercase();
|
||||
let mut to_render: Option<(String, String, String)> = None;
|
||||
|
||||
@@ -1677,7 +1686,7 @@ fn draw_ships_ui(
|
||||
if let Some((id, file, label)) = to_render {
|
||||
let id2 = id.clone();
|
||||
*selected = Some(id);
|
||||
render.send(RequestShipRender { file, id: id2, label });
|
||||
render.send(RequestShipRender { file, id: id2, external: show_external, label });
|
||||
}
|
||||
});
|
||||
ships.open &= open;
|
||||
|
||||
Reference in New Issue
Block a user