use alloc::vec::Vec;
use crate::{
Geometry, Ops,
math::Vec3,
mikktspace::{
face_vertex::FaceVertex, get_normal_from_index, get_position_from_index,
get_texture_coordinate_from_index,
},
};
pub(super) fn weld_vertices<I: Geometry<O>, O: Ops>(
context: &I,
triangle_vertices: &mut [FaceVertex],
) {
let Some((min, max)) = triangle_vertices
.iter()
.map(|&i| get_position_from_index(context, i))
.fold(None, |state, v| {
let (mut min, mut max) = state.unwrap_or((v, v));
for c in 0..3 {
min[c] = min[c].min(v[c]);
max[c] = max[c].max(v[c]);
}
Some((min, max))
})
else {
return;
};
let d = max - min;
let c_max = if d.y > d.x && d.y > d.z {
1
} else if d.z > d.x {
2
} else {
0
};
let mut temporary_vertices = triangle_vertices
.iter()
.map(|&v| get_position_from_index(context, v))
.enumerate()
.map(|(index, position)| TemporaryVertex {
bucket: {
const GROUPS: u16 = 2048;
let t = (position[c_max] - min[c_max]) / d[c_max];
let group = (GROUPS as f32 * t.clamp(0., 1.)) as u16;
group.clamp(0, GROUPS - 1)
},
position,
original_index: index,
})
.collect::<Vec<_>>();
temporary_vertices.sort_by_key(|v| v.bucket);
for chunk in temporary_vertices.chunk_by_mut(|a, b| a.bucket == b.bucket) {
merge_verts_fast(context, triangle_vertices, chunk);
}
}
fn merge_verts_fast<I: Geometry<O>, O: Ops>(
context: &I,
vertices: &mut [FaceVertex],
buffer: &mut [TemporaryVertex<O>],
) {
if buffer.len() < 2 {
return;
}
let (min, max) = buffer
.iter()
.map(|t| t.position)
.fold(None, |state, v| {
let (mut min, mut max) = state.unwrap_or((v, v));
for c in 0..3 {
min[c] = min[c].min(v[c]);
max[c] = max[c].max(v[c]);
}
Some((min, max))
})
.unwrap();
let d = max - min;
let c = if d.y > d.x && d.y > d.z {
1
} else if d.z > d.x {
2
} else {
0
};
let sep = 0.5f32 * (max[c] + min[c]);
if !sep.is_finite() {
return;
}
if !(min[c] < sep && sep < max[c]) {
for (l, v_a) in buffer.iter().enumerate() {
let i = v_a.original_index;
let index = vertices[i];
let a = (
v_a.position,
get_normal_from_index(context, index),
get_texture_coordinate_from_index(context, index),
);
let j = buffer.iter().take(l).find_map(|v_b| {
let j = v_b.original_index;
let index = vertices[j];
let b = (
v_b.position,
get_normal_from_index(context, index),
get_texture_coordinate_from_index(context, index),
);
(a == b).then_some(j)
});
if let Some(j) = j {
vertices[i] = vertices[j];
}
}
return;
}
let mut unsorted = 0..buffer.len();
while unsorted.len() >= 2 {
let a = unsorted.find(|&i| buffer[i].position[c] >= sep);
let b = (&mut unsorted).rev().find(|&i| buffer[i].position[c] < sep);
unsorted = match (a, b) {
(Some(a), Some(b)) => {
buffer.swap(a, b);
(a + 1)..b
}
(None, Some(b)) => unsorted.start..(b + 1),
(Some(a), None) => a..(a + 1),
(None, None) => unsorted,
};
}
let partition = if !unsorted.is_empty() && buffer[unsorted.start].position[c] < sep {
unsorted.start + 1
} else {
unsorted.start
};
let (lesser, greater) = buffer.split_at_mut(partition);
merge_verts_fast(context, vertices, lesser);
merge_verts_fast(context, vertices, greater);
}
struct TemporaryVertex<O: Ops> {
position: Vec3<O>,
original_index: usize,
bucket: u16,
}
impl<O: Ops> Copy for TemporaryVertex<O> {}
impl<O: Ops> Clone for TemporaryVertex<O> {
fn clone(&self) -> Self {
*self
}
}