use crate::mesh::{Indices, Mesh, Primitive, Topology};
const FILL_NORMAL: [f32; 3] = [0.0, 0.0, 1.0];
const FILL_TANGENT: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
const FILL_UV: [f32; 2] = [0.0, 0.0];
const FILL_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const FILL_JOINTS: [u16; 4] = [0, 0, 0, 0];
const FILL_WEIGHTS: [f32; 4] = [0.0, 0.0, 0.0, 0.0];
impl Primitive {
pub fn merge(&self, other: &Primitive) -> Primitive {
merge_all(&[other], self)
}
}
fn merge_all(rest: &[&Primitive], base: &Primitive) -> Primitive {
let all: Vec<&Primitive> = std::iter::once(base).chain(rest.iter().copied()).collect();
let want_normals = all.iter().any(|p| p.normals.is_some());
let want_tangents = all.iter().any(|p| p.tangents.is_some());
let want_joints = all.iter().any(|p| p.joints.is_some());
let want_weights = all.iter().any(|p| p.weights.is_some());
let uv_sets = all.iter().map(|p| p.uvs.len()).max().unwrap_or(0);
let color_sets = all.iter().map(|p| p.colors.len()).max().unwrap_or(0);
let mut out = Primitive::new(Topology::Triangles);
out.material = base.material;
out.variant_mappings = base.variant_mappings.clone();
out.extras = base.extras.clone();
if want_normals {
out.normals = Some(Vec::new());
}
if want_tangents {
out.tangents = Some(Vec::new());
}
if want_joints {
out.joints = Some(Vec::new());
}
if want_weights {
out.weights = Some(Vec::new());
}
out.uvs = vec![Vec::new(); uv_sets];
out.colors = vec![Vec::new(); color_sets];
let mut flat: Vec<u32> = Vec::new();
for p in &all {
let base_index = out.positions.len() as u32;
let vcount = p.positions.len();
out.positions.extend_from_slice(&p.positions);
if want_normals {
let dst = out.normals.as_mut().unwrap();
match &p.normals {
Some(src) => append_or_fill(dst, src, vcount, FILL_NORMAL),
None => dst.extend(std::iter::repeat(FILL_NORMAL).take(vcount)),
}
}
if want_tangents {
let dst = out.tangents.as_mut().unwrap();
match &p.tangents {
Some(src) => append_or_fill(dst, src, vcount, FILL_TANGENT),
None => dst.extend(std::iter::repeat(FILL_TANGENT).take(vcount)),
}
}
if want_joints {
let dst = out.joints.as_mut().unwrap();
match &p.joints {
Some(src) => append_or_fill(dst, src, vcount, FILL_JOINTS),
None => dst.extend(std::iter::repeat(FILL_JOINTS).take(vcount)),
}
}
if want_weights {
let dst = out.weights.as_mut().unwrap();
match &p.weights {
Some(src) => append_or_fill(dst, src, vcount, FILL_WEIGHTS),
None => dst.extend(std::iter::repeat(FILL_WEIGHTS).take(vcount)),
}
}
for s in 0..uv_sets {
match p.uvs.get(s) {
Some(src) => append_or_fill(&mut out.uvs[s], src, vcount, FILL_UV),
None => out.uvs[s].extend(std::iter::repeat(FILL_UV).take(vcount)),
}
}
for s in 0..color_sets {
match p.colors.get(s) {
Some(src) => append_or_fill(&mut out.colors[s], src, vcount, FILL_COLOR),
None => out.colors[s].extend(std::iter::repeat(FILL_COLOR).take(vcount)),
}
}
for tri in p.triangle_indices() {
if tri.iter().all(|&c| (c as usize) < vcount) {
flat.push(base_index + tri[0]);
flat.push(base_index + tri[1]);
flat.push(base_index + tri[2]);
}
}
}
out.indices = Some(Indices::U32(flat));
out
}
fn append_or_fill<T: Copy>(dst: &mut Vec<T>, src: &[T], vcount: usize, fill: T) {
let take = src.len().min(vcount);
dst.extend_from_slice(&src[..take]);
if take < vcount {
dst.extend(std::iter::repeat(fill).take(vcount - take));
}
}
impl Mesh {
pub fn merge_primitives_by_material(&self) -> Mesh {
let mut groups: Vec<Vec<&Primitive>> = Vec::new();
for p in &self.primitives {
match groups.iter().position(|g| {
g[0].material == p.material && g[0].variant_mappings == p.variant_mappings
}) {
Some(idx) => groups[idx].push(p),
None => groups.push(vec![p]),
}
}
let mut out = self.clone();
out.primitives = groups
.into_iter()
.map(|g| {
let (base, rest) = g.split_first().expect("group is non-empty");
merge_all(rest, base)
})
.collect();
out
}
}