ship: use only the primary composite — fixes parts floating mid-ship
The sub-composites (`e_rou_<id>_eng`, `_wep_*`) are LOCAL detail rigs: their nodes are posed in their own frame, not ship-space, so folding them in placed the engines at ~mid-ship (Z≈99) instead of aft — the "floating in air" parts. The PRIMARY composite (`e_rou_<id>`, the one with the most nodes) already carries the full ship-space layout: every hull body AND every `GN_*` hardpoint frame (GN_Bridge at the stern, GN_Engine aft, GN_ShieldG amidships…). Assemble from that one composite only; engines/bridge/shield now resolve to their real aft/mid frames. Robust to the odd single-composite case (e108's `e_rou_e108_Missile_open` is still picked as its own primary). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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<ScenePart> {
|
pub fn assemble_ship(bytes: &[u8], id: &str) -> Vec<ScenePart> {
|
||||||
let names = xbg7_resource_names(bytes);
|
let names = xbg7_resource_names(bytes);
|
||||||
let resources: HashSet<String> = names.iter().cloned().collect();
|
let resources: HashSet<String> = 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_<id>_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<ScenePart>)> = 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<ScenePart> = Vec::new();
|
let mut placed: Vec<ScenePart> = Vec::new();
|
||||||
let mut placed_res: HashSet<String> = HashSet::new();
|
let mut placed_res: HashSet<String> = HashSet::new();
|
||||||
// GN hardpoint frames, world-space, from every composite.
|
// GN hardpoint frames, world-space, from the primary composite.
|
||||||
let mut frames: Vec<ScenePart> = Vec::new();
|
let mut frames: Vec<ScenePart> = Vec::new();
|
||||||
|
|
||||||
for c in comps {
|
for node in nodes {
|
||||||
for node in scene_world_nodes(bytes, c) {
|
if node.resource.starts_with("GN_") {
|
||||||
if node.resource.starts_with("GN_") {
|
frames.push(node.clone());
|
||||||
frames.push(node);
|
} else if let Some(res) = resolve_hull_resource(&node.resource, &resources) {
|
||||||
} else if let Some(res) = resolve_hull_resource(&node.resource, &resources) {
|
// Only the ship's own parts. A composite also mounts shared, cross-id
|
||||||
// Only the ship's own parts. A composite also mounts shared,
|
// turret models (`e303_wep_*` on an `e106` hull); those need the
|
||||||
// cross-id turret models (`e303_wep_*` on an `e106` hull); those
|
// per-mount Vessel recipe and are placed separately.
|
||||||
// need the per-mount Vessel recipe and are placed separately.
|
if ship_id_of(&res) != Some(id) {
|
||||||
if ship_id_of(&res) != Some(id) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
if placed_res.insert(res.clone()) {
|
||||||
if placed_res.insert(res.clone()) {
|
placed.push(ScenePart { resource: res, m: node.m, t: node.t });
|
||||||
placed.push(ScenePart { resource: res, m: node.m, t: node.t });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user