use crate::aabb::Aabb;
use crate::bvh::Bvh;
use crate::vec3::{cross, dot, sub, Vec3};
const RAY_DIR: Vec3 = [0.3333333333333333, 0.5773502691896257, 0.7453559924999299];
const RAY_EPS: f64 = 1e-9;
pub struct TriMesh {
positions: Vec<f64>,
indices: Vec<u32>,
pub count: usize,
bvh: Bvh,
}
impl TriMesh {
pub fn new(positions: Vec<f64>, indices: Vec<u32>) -> Self {
let vertex_count = positions.len() / 3;
let mut indices = indices;
let tri_total = indices.len() / 3;
let all_valid = (0..tri_total).all(|t| {
let o = t * 3;
(indices[o] as usize) < vertex_count
&& (indices[o + 1] as usize) < vertex_count
&& (indices[o + 2] as usize) < vertex_count
});
if !all_valid {
let mut clean: Vec<u32> = Vec::with_capacity(indices.len());
for t in 0..tri_total {
let o = t * 3;
let i0 = indices[o] as usize;
let i1 = indices[o + 1] as usize;
let i2 = indices[o + 2] as usize;
if i0 < vertex_count && i1 < vertex_count && i2 < vertex_count {
clean.extend_from_slice(&[indices[o], indices[o + 1], indices[o + 2]]);
}
}
indices = clean;
}
let count = indices.len() / 3;
let mut items: Vec<(u32, Aabb)> = Vec::with_capacity(count);
for t in 0..count {
let bounds = tri_bounds(&positions, &indices, t);
items.push((t as u32, bounds));
}
let bvh = Bvh::build(&items);
Self {
positions,
indices,
count,
bvh,
}
}
#[inline]
pub fn vertex(&self, i: u32) -> Vec3 {
let o = (i as usize) * 3;
[self.positions[o], self.positions[o + 1], self.positions[o + 2]]
}
#[inline]
pub fn tri(&self, t: usize) -> [Vec3; 3] {
let o = t * 3;
[
self.vertex(self.indices[o]),
self.vertex(self.indices[o + 1]),
self.vertex(self.indices[o + 2]),
]
}
#[inline]
pub fn tri_bounds(&self, t: usize) -> Aabb {
tri_bounds(&self.positions, &self.indices, t)
}
pub fn query_tris(&self, bounds: &Aabb) -> Vec<u32> {
if self.count == 0 {
return Vec::new();
}
self.bvh.query_aabb(bounds)
}
#[allow(clippy::manual_range_contains)]
pub fn contains_point(&self, p: Vec3) -> bool {
let mut crossings: u32 = 0;
for t in 0..self.count {
let [v0, v1, v2] = self.tri(t);
let e1 = sub(v1, v0);
let e2 = sub(v2, v0);
let pv = cross(RAY_DIR, e2);
let det = dot(e1, pv);
if det > -RAY_EPS && det < RAY_EPS {
continue; }
let inv = 1.0 / det;
let tv = sub(p, v0);
let u = dot(tv, pv) * inv;
if u < 0.0 || u > 1.0 {
continue;
}
let qv = cross(tv, e1);
let v = dot(RAY_DIR, qv) * inv;
if v < 0.0 || u + v > 1.0 {
continue;
}
let t_hit = dot(e2, qv) * inv;
if t_hit > RAY_EPS {
crossings += 1; }
}
crossings & 1 == 1
}
}
fn tri_bounds(positions: &[f64], indices: &[u32], t: usize) -> Aabb {
let o = t * 3;
let va = vertex(positions, indices[o]);
let vb = vertex(positions, indices[o + 1]);
let vc = vertex(positions, indices[o + 2]);
Aabb::new(
[
va[0].min(vb[0]).min(vc[0]),
va[1].min(vb[1]).min(vc[1]),
va[2].min(vb[2]).min(vc[2]),
],
[
va[0].max(vb[0]).max(vc[0]),
va[1].max(vb[1]).max(vc[1]),
va[2].max(vb[2]).max(vc[2]),
],
)
}
#[inline]
fn vertex(positions: &[f64], i: u32) -> Vec3 {
let o = (i as usize) * 3;
[positions[o], positions[o + 1], positions[o + 2]]
}