diff --git a/crates/sylpheed-formats/src/ship.rs b/crates/sylpheed-formats/src/ship.rs index ce9de2a..e66cf92 100644 --- a/crates/sylpheed-formats/src/ship.rs +++ b/crates/sylpheed-formats/src/ship.rs @@ -230,27 +230,43 @@ fn composites_for<'a>(names: &'a [String], id: &str) -> Vec<&'a String> { pub fn assemble_ship(bytes: &[u8], id: &str) -> Vec { let names = xbg7_resource_names(bytes); let resources: HashSet = names.iter().cloned().collect(); - let comps = composites_for(&names, id); + + // The PRIMARY composite carries the real ship-space layout (hull bodies + + // every `GN_*` hardpoint frame). Sibling composites (`e_rou__eng`, + // `_wep_*`) are LOCAL detail rigs — their nodes are in their own frame, not + // ship-space, so folding them in floats parts mid-ship. Pick the composite + // with the most nodes; that is the assembled hull. + let scenes: Vec<(String, Vec)> = composites_for(&names, id) + .into_iter() + .map(|c| (c.clone(), scene_world_nodes(bytes, c))) + .collect(); + let Some((_, nodes)) = scenes.iter().max_by_key(|(_, n)| n.len()) else { + // No composite at all → raw fallback below. + let mut placed = Vec::new(); + const ID: [[f32; 3]; 3] = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]; + for part in names.iter().filter(|n| is_base_part(n) && ship_id_of(n) == Some(id)) { + placed.push(ScenePart { resource: part.clone(), m: ID, t: [0.0; 3] }); + } + return placed; + }; let mut placed: Vec = Vec::new(); let mut placed_res: HashSet = HashSet::new(); - // GN hardpoint frames, world-space, from every composite. + // GN hardpoint frames, world-space, from the primary composite. let mut frames: Vec = Vec::new(); - for c in comps { - for node in scene_world_nodes(bytes, c) { - if node.resource.starts_with("GN_") { - frames.push(node); - } else if let Some(res) = resolve_hull_resource(&node.resource, &resources) { - // Only the ship's own parts. A composite also mounts shared, - // cross-id turret models (`e303_wep_*` on an `e106` hull); those - // need the per-mount Vessel recipe and are placed separately. - if ship_id_of(&res) != Some(id) { - continue; - } - if placed_res.insert(res.clone()) { - placed.push(ScenePart { resource: res, m: node.m, t: node.t }); - } + for node in nodes { + if node.resource.starts_with("GN_") { + frames.push(node.clone()); + } else if let Some(res) = resolve_hull_resource(&node.resource, &resources) { + // Only the ship's own parts. A composite also mounts shared, cross-id + // turret models (`e303_wep_*` on an `e106` hull); those need the + // per-mount Vessel recipe and are placed separately. + if ship_id_of(&res) != Some(id) { + continue; + } + if placed_res.insert(res.clone()) { + placed.push(ScenePart { resource: res, m: node.m, t: node.t }); } } }