use super::collate::{
compose_world, mat4_to_row_major_f32, to_post_rtc, Collated, InstanceMeshRef,
InstanceOccurrence, InstanceTemplate,
};
use super::verify::verify_pairing;
use nalgebra::Matrix4;
use rustc_hash::FxHashMap;
pub fn collate_refs(meshes: &[InstanceMeshRef], min_group: usize, rtc: [f64; 3]) -> Collated {
collate_refs_verified_in(meshes, min_group, rtc, None)
}
pub fn collate_refs_verified_in(
meshes: &[InstanceMeshRef],
min_group: usize,
rtc: [f64; 3],
verify_basis: Option<&Matrix4<f64>>,
) -> Collated {
let verify_conjugate = match verify_basis {
None => None,
Some(s) => match s.try_inverse() {
Some(s_inv) => Some((*s, s_inv)),
None => {
crate::diag::diag_warn!(
{ "instancing: verify_basis is singular; refusing the whole collation (nothing instanced, every drawable mesh still drawn flat)" }
else {
#[cfg(any(debug_assertions, test))]
eprintln!(
"[instancing] verify_basis is singular; refusing the whole \
collation (nothing instanced, every drawable mesh drawn flat)"
);
}
);
return all_drawable_flat(meshes);
}
},
};
let mut order: Vec<u128> = Vec::new();
let mut groups: FxHashMap<u128, Vec<usize>> = FxHashMap::default();
let mut flat: Vec<usize> = Vec::new();
for (i, m) in meshes.iter().enumerate() {
match m.instance_meta {
Some(im) if im.instanceable => {
groups
.entry(im.rep_identity)
.or_insert_with(|| {
order.push(im.rep_identity);
Vec::new()
})
.push(i);
}
_ if !m.positions.is_empty() => flat.push(i),
_ => {}
}
}
let mut out = Collated {
flat_indices: flat,
..Collated::default()
};
for rep in order {
let members = &groups[&rep];
let Some(t_idx) = members
.iter()
.copied()
.find(|&i| !meshes[i].positions.is_empty())
else {
out.dropped_placeholders += members.len();
continue;
};
let template = &meshes[t_idx];
let m_ref = to_post_rtc(compose_world(template.instance_meta.unwrap()), rtc);
let m_ref_inv = m_ref.try_inverse();
if !template.positions.iter().all(|p| p.is_finite()) {
if members.len() >= min_group.max(1)
|| members.iter().any(|&i| meshes[i].positions.is_empty())
{
out.verification_rejections += 1;
}
fall_back(&mut out, meshes, rep, members, t_idx, None, rtc);
continue;
}
if members.len() < min_group.max(1) {
fall_back(&mut out, meshes, rep, members, t_idx, m_ref_inv.as_ref(), rtc);
continue;
}
let Some(m_ref_inv) = m_ref_inv else {
fall_back(&mut out, meshes, rep, members, t_idx, None, rtc);
continue;
};
let vlen = template.positions.len();
let mut occurrences = Vec::with_capacity(members.len());
let mut shapes_match = true;
for &i in members {
let mesh = &meshes[i];
if i == t_idx {
occurrences.push(InstanceOccurrence {
mesh_index: i,
transform: mat4_to_row_major_f32(&(m_ref * m_ref_inv)),
});
continue;
}
let member_is_rigid = mesh
.instance_meta
.and_then(|m| m.canonical_transform)
.is_some();
let pose_only = mesh.positions.is_empty();
if !pose_only
&& !member_is_rigid
&& (mesh.positions.len() != vlen || mesh.indices != template.indices)
{
shapes_match = false;
break;
}
let m_k = to_post_rtc(compose_world(mesh.instance_meta.unwrap()), rtc);
let rel = m_k * m_ref_inv;
if !pose_only && !member_is_rigid {
let verify_rel =
verify_conjugate.as_ref().map_or(rel, |(s, s_inv)| s * rel * s_inv);
if !verify_pairing(
template.origin,
template.positions,
mesh.origin,
mesh.positions,
&verify_rel,
) {
shapes_match = false;
break;
}
}
occurrences.push(InstanceOccurrence {
mesh_index: i,
transform: mat4_to_row_major_f32(&rel),
});
}
if shapes_match {
out.templates.push(InstanceTemplate {
rep_identity: rep,
template_index: t_idx,
occurrences,
});
continue;
}
out.verification_rejections += 1;
fall_back(&mut out, meshes, rep, members, t_idx, Some(&m_ref_inv), rtc);
}
out
}
fn fall_back(
out: &mut Collated,
meshes: &[InstanceMeshRef],
rep: u128,
members: &[usize],
t_idx: usize,
place_with: Option<&Matrix4<f64>>,
rtc: [f64; 3],
) {
let pose_only: Vec<usize> = members
.iter()
.copied()
.filter(|&i| meshes[i].positions.is_empty())
.collect();
let materialized = || members.iter().copied().filter(|&i| !meshes[i].positions.is_empty());
let Some(m_ref_inv) = place_with.filter(|_| !pose_only.is_empty()) else {
out.dropped_placeholders += pose_only.len();
out.flat_indices.extend(materialized());
return;
};
let kept: Vec<InstanceOccurrence> = std::iter::once(t_idx)
.chain(pose_only)
.map(|i| InstanceOccurrence {
mesh_index: i,
transform: mat4_to_row_major_f32(
&(to_post_rtc(compose_world(meshes[i].instance_meta.unwrap()), rtc) * m_ref_inv),
),
})
.collect();
out.templates.push(InstanceTemplate {
rep_identity: rep,
template_index: t_idx,
occurrences: kept,
});
out.flat_indices.extend(materialized().filter(|&i| i != t_idx));
}
fn all_drawable_flat(meshes: &[InstanceMeshRef]) -> Collated {
Collated {
flat_indices: (0..meshes.len())
.filter(|&i| !meshes[i].positions.is_empty())
.collect(),
dropped_placeholders: meshes
.iter()
.filter(|m| {
m.positions.is_empty() && m.instance_meta.is_some_and(|im| im.instanceable)
})
.count(),
..Collated::default()
}
}