use super::RecastConfig;
use super::contour::ContourSet;
pub struct PolyMesh {
pub verts: Vec<[i32; 3]>,
pub polys: Vec<Vec<u16>>,
pub regs: Vec<u16>,
pub nverts: usize,
pub npolys: usize,
pub nvp: usize,
pub bmin: [f64; 3],
pub cs: f64,
pub ch: f64,
}
pub fn build_poly_mesh(cs: &ContourSet, cfg: &RecastConfig) -> Result<PolyMesh, String> {
let nvp = cfg.max_verts_per_poly.min(6);
let mut all_verts: Vec<[i32; 3]> = Vec::new();
let mut all_polys: Vec<Vec<u16>> = Vec::new();
let mut all_regs: Vec<u16> = Vec::new();
for contour in &cs.contours {
if contour.verts.len() < 3 {
continue;
}
let start_v = all_verts.len();
for v in &contour.verts {
all_verts.push([v[0], v[1], v[2]]);
}
let tris = ear_clip_triangulate(&contour.verts);
for tri in tris {
let poly: Vec<u16> = tri.iter().map(|&i| (start_v + i) as u16).collect();
all_polys.push(poly);
all_regs.push(contour.region);
}
}
let nverts = all_verts.len();
let npolys = all_polys.len();
Ok(PolyMesh {
verts: all_verts,
polys: all_polys,
regs: all_regs,
nverts,
npolys,
nvp,
bmin: cs.bmin,
cs: cs.cs,
ch: cs.ch,
})
}
fn ear_clip_triangulate(verts: &[[i32; 4]]) -> Vec<[usize; 3]> {
let n = verts.len();
if n < 3 {
return vec![];
}
if n == 3 {
return vec![[0, 1, 2]];
}
(1..n - 1).map(|i| [0, i, i + 1]).collect()
}
pub fn poly_mesh_to_triangles(pm: &PolyMesh) -> Vec<[f64; 9]> {
let mut tris = Vec::new();
for poly in &pm.polys {
if poly.len() < 3 {
continue;
}
let v0 = to_world(&pm.verts[poly[0] as usize], &pm.bmin, pm.cs, pm.ch);
for i in 1..poly.len() - 1 {
let v1 = to_world(&pm.verts[poly[i] as usize], &pm.bmin, pm.cs, pm.ch);
let v2 = to_world(&pm.verts[poly[i + 1] as usize], &pm.bmin, pm.cs, pm.ch);
tris.push([
v0[0], v0[1], v0[2], v1[0], v1[1], v1[2], v2[0], v2[1], v2[2],
]);
}
}
tris
}
pub fn poly_mesh_to_nav_mesh_primitives(pm: &PolyMesh) -> (Vec<[f64; 3]>, Vec<[u32; 3]>) {
let world_verts: Vec<[f64; 3]> = pm
.verts
.iter()
.map(|v| to_world(v, &pm.bmin, pm.cs, pm.ch))
.collect();
let mut out_verts: Vec<[f64; 3]> = Vec::new();
let mut out_tris: Vec<[u32; 3]> = Vec::new();
let mut vert_map: Vec<([i32; 3], u32)> = Vec::new();
let mut get_or_insert = |vkey: [i32; 3], world: [f64; 3]| -> u32 {
for &(k, idx) in &vert_map {
if k == vkey {
return idx;
}
}
let idx = out_verts.len() as u32;
out_verts.push(world);
vert_map.push((vkey, idx));
idx
};
for poly in &pm.polys {
if poly.len() < 3 {
continue;
}
let v0_key = pm.verts[poly[0] as usize];
let v1_key = pm.verts[poly[1] as usize];
let v2_key = pm.verts[poly[2] as usize];
let w0 = world_verts[poly[0] as usize];
let w1 = world_verts[poly[1] as usize];
let w2 = world_verts[poly[2] as usize];
let i0 = get_or_insert(v0_key, w0);
let i1 = get_or_insert(v1_key, w1);
let i2 = get_or_insert(v2_key, w2);
out_tris.push([i0, i1, i2]);
}
(out_verts, out_tris)
}
#[inline]
fn to_world(v: &[i32; 3], bmin: &[f64; 3], cs: f64, ch: f64) -> [f64; 3] {
[
bmin[0] + v[0] as f64 * cs,
bmin[1] + v[1] as f64 * ch,
bmin[2] + v[2] as f64 * cs,
]
}