use crate::types::mesh::{InstanceRecord, MeshData, RawInstanceOccurrence};
use rustc_hash::FxHashMap;
pub(super) fn finalize_instances(
raw: Vec<RawInstanceOccurrence>,
meshes: &mut Vec<MeshData>,
mapped_item_cache: &ifc_lite_geometry::SharedMappedItemCache,
rtc: [f64; 3],
) -> Vec<InstanceRecord> {
if raw.is_empty() {
return Vec::new();
}
let mut by_source: FxHashMap<u128, Vec<RawInstanceOccurrence>> = FxHashMap::default();
for occ in raw {
by_source.entry(occ.rep_identity).or_default().push(occ);
}
let mut template_by_rep: FxHashMap<u128, usize> = FxHashMap::default();
for (i, m) in meshes.iter().enumerate() {
if m.positions.is_empty() {
continue;
}
if let Some(im) = m.instance.as_ref() {
if im.instanceable {
template_by_rep.entry(im.rep_identity).or_insert(i);
}
}
}
let mut records: Vec<InstanceRecord> = Vec::new();
let mut orphan_flats: Vec<MeshData> = Vec::new();
let mut groups: Vec<(u128, Vec<RawInstanceOccurrence>)> = by_source.into_iter().collect();
groups.sort_by_key(|(rep, _)| *rep);
for (rep, occs) in groups {
if let Some(&t_idx) = template_by_rep.get(&rep) {
if let Some(im) = meshes[t_idx].instance.as_ref() {
let m_ref = ifc_lite_geometry::compose_instance_world_row_major(im);
let template_express_id = meshes[t_idx].express_id;
let mut batch = Vec::with_capacity(occs.len());
let mut all_ok = true;
for occ in &occs {
match ifc_lite_geometry::instance_rel_row_major_f32(
&occ.world_transform,
&m_ref,
rtc,
) {
Some(transform) => batch.push(InstanceRecord {
express_id: occ.express_id,
ifc_type: occ.ifc_type.clone(),
global_id: occ.global_id.clone(),
name: occ.name.clone(),
presentation_layer: occ.presentation_layer.clone(),
color: occ.color,
template_express_id,
rep_identity: rep,
transform,
}),
None => {
all_ok = false;
break;
}
}
}
if all_ok {
records.extend(batch);
continue;
}
}
}
recover_orphan_occurrences(rep, &occs, mapped_item_cache, rtc, &mut orphan_flats);
}
records.sort_by_key(|r| (r.express_id, r.rep_identity));
orphan_flats.sort_by_key(|m| m.express_id);
meshes.append(&mut orphan_flats);
records
}
fn recover_orphan_occurrences(
rep: u128,
occs: &[RawInstanceOccurrence],
mapped_item_cache: &ifc_lite_geometry::SharedMappedItemCache,
rtc: [f64; 3],
out: &mut Vec<MeshData>,
) {
let source_id = rep as u32;
let source = mapped_item_cache
.lock()
.unwrap_or_else(|e| e.into_inner())
.get(&source_id)
.cloned();
let Some(source) = source else {
tracing::warn!(
source_id,
occurrences = occs.len(),
"instancing: orphan mapped source missing from registry; occurrences dropped"
);
return;
};
for occ in occs {
let (positions, normals, indices) =
ifc_lite_geometry::bake_source_at_world(&source, &occ.world_transform, rtc);
if positions.is_empty() || indices.is_empty() {
continue;
}
out.push(
MeshData::new(
occ.express_id,
occ.ifc_type.clone(),
positions,
normals,
indices,
occ.color,
)
.with_element_metadata(
occ.global_id.clone(),
occ.name.clone(),
occ.presentation_layer.clone(),
),
);
}
}