use num_traits::{AsPrimitive, PrimInt};
pub struct TriMeshWithBvh<'a, Index> {
pub tri2vtx: &'a [Index],
pub vtx2xyz: &'a [f32],
pub bvhnodes: &'a [Index],
pub bvhnode2aabb: &'a [f32],
}
pub fn intersections_ray<Index>(
hits: &mut Vec<(f32, usize)>,
ray_org: &[f32; 3],
ray_dir: &[f32; 3],
trimesh3: &TriMeshWithBvh<Index>,
i_bvhnode: usize,
) where
Index: PrimInt + AsPrimitive<usize>,
{
if del_geo_core::aabb3::from_aabbs(trimesh3.bvhnode2aabb, i_bvhnode)
.intersections_against_ray(ray_org, ray_dir)
.is_none()
{
return;
}
assert_eq!(trimesh3.bvhnodes.len() / 3, trimesh3.bvhnode2aabb.len() / 6);
if trimesh3.bvhnodes[i_bvhnode * 3 + 2] == Index::max_value() {
let i_tri: usize = trimesh3.bvhnodes[i_bvhnode * 3 + 1].as_();
let Some((t, _bc)) = crate::trimesh3::to_tri3(trimesh3.tri2vtx, trimesh3.vtx2xyz, i_tri)
.intersection_against_ray(ray_org, ray_dir)
else {
return;
};
hits.push((t, i_tri));
return;
}
intersections_ray(
hits,
ray_org,
ray_dir,
trimesh3,
trimesh3.bvhnodes[i_bvhnode * 3 + 1].as_(),
);
intersections_ray(
hits,
ray_org,
ray_dir,
trimesh3,
trimesh3.bvhnodes[i_bvhnode * 3 + 2].as_(),
);
}
pub fn intersections_line<Index>(
hits: &mut Vec<(f32, usize)>,
line_org: &[f32; 3],
line_dir: &[f32; 3],
trimesh3: &TriMeshWithBvh<Index>,
i_bvhnode: usize,
) where
Index: PrimInt + AsPrimitive<usize>,
{
if del_geo_core::aabb3::from_aabbs(trimesh3.bvhnode2aabb, i_bvhnode)
.intersections_against_line(line_org, line_dir)
.is_none()
{
return;
}
assert_eq!(trimesh3.bvhnodes.len() / 3, trimesh3.bvhnode2aabb.len() / 6);
if trimesh3.bvhnodes[i_bvhnode * 3 + 2] == Index::max_value() {
let i_tri: usize = trimesh3.bvhnodes[i_bvhnode * 3 + 1].as_();
let Some((t, _bc)) = crate::trimesh3::to_tri3(trimesh3.tri2vtx, trimesh3.vtx2xyz, i_tri)
.intersection_against_line(line_org, line_dir)
else {
return;
};
hits.push((t, i_tri));
return;
}
crate::search_bvh3::intersections_line(
hits,
line_org,
line_dir,
trimesh3,
trimesh3.bvhnodes[i_bvhnode * 3 + 1].as_(),
);
crate::search_bvh3::intersections_line(
hits,
line_org,
line_dir,
trimesh3,
trimesh3.bvhnodes[i_bvhnode * 3 + 2].as_(),
);
}
pub fn first_intersection_ray<Index>(
ray_org: &[f32; 3],
ray_dir: &[f32; 3],
trimesh3: &TriMeshWithBvh<Index>,
i_bvhnode: usize,
dis: f32,
) -> Option<(f32, usize, [f32; 3])>
where
Index: PrimInt + AsPrimitive<usize>,
{
assert_eq!(
trimesh3.bvhnodes.len() / 3,
trimesh3.tri2vtx.len() / 3 * 2 - 1
);
assert_eq!(trimesh3.bvhnodes.len() / 3, trimesh3.bvhnode2aabb.len() / 6);
del_geo_core::aabb3::from_aabbs(trimesh3.bvhnode2aabb, i_bvhnode)
.intersections_against_ray(ray_org, ray_dir)?;
if trimesh3.bvhnodes[i_bvhnode * 3 + 2] == Index::max_value() {
let i_tri: usize = trimesh3.bvhnodes[i_bvhnode * 3 + 1].as_();
return crate::trimesh3::to_tri3(trimesh3.tri2vtx, trimesh3.vtx2xyz, i_tri)
.intersection_against_ray(ray_org, ray_dir)
.filter(|(t, _bc)| *t < dis)
.map(|(t, bc)| (t, i_tri, bc));
}
let near_is_left = {
let t_aabb_left = del_geo_core::aabb3::from_aabbs(
trimesh3.bvhnode2aabb,
trimesh3.bvhnodes[i_bvhnode * 3 + 1].as_(),
)
.intersections_against_ray(ray_org, ray_dir);
let t_aabb_right = del_geo_core::aabb3::from_aabbs(
trimesh3.bvhnode2aabb,
trimesh3.bvhnodes[i_bvhnode * 3 + 2].as_(),
)
.intersections_against_ray(ray_org, ray_dir);
match (t_aabb_left, t_aabb_right) {
(Some(tl), Some(tr)) => tl < tr,
(None, None) => {
return None;
}
(Some(_), None) => true,
(None, Some(_)) => false,
}
};
let res_near = {
let idx_bvhnode_near = if near_is_left {
i_bvhnode * 3 + 1
} else {
i_bvhnode * 3 + 2
};
first_intersection_ray(
ray_org,
ray_dir,
trimesh3,
trimesh3.bvhnodes[idx_bvhnode_near].as_(),
dis,
)
};
if let Some((t_near, _i_tri_near, _bc_near)) = res_near {
let idx_bvhnode_far = if near_is_left {
i_bvhnode * 3 + 2
} else {
i_bvhnode * 3 + 1
};
let i_bvhnode_far: usize = trimesh3.bvhnodes[idx_bvhnode_far].as_(); let aabb_far = arrayref::array_ref!(trimesh3.bvhnode2aabb, i_bvhnode_far * 6, 6);
if let Some((t_aabb_far, _)) =
del_geo_core::aabb::intersections_against_ray(aabb_far, ray_org, ray_dir)
{
if t_aabb_far > t_near {
return res_near;
}
} else {
return res_near;
}
}
let res_far = {
let idx_bvhnode_far = if near_is_left {
i_bvhnode * 3 + 2
} else {
i_bvhnode * 3 + 1
};
first_intersection_ray(
ray_org,
ray_dir,
trimesh3,
trimesh3.bvhnodes[idx_bvhnode_far].as_(),
dis,
)
};
if let Some((t_near, i_tri_near, bc_near)) = res_near {
if let Some((t_far, i_tri_far, bc_far)) = res_far {
if t_near < t_far {
Some((t_near, i_tri_near, bc_near))
} else {
Some((t_far, i_tri_far, bc_far))
}
} else {
Some((t_near, i_tri_near, bc_near))
}
} else if let Some((t_far, i_tri_far, bc_far)) = res_far {
Some((t_far, i_tri_far, bc_far))
} else {
None
}
}
#[test]
fn test_first_intersection_ray() {
let (tri2vtx, vtx2xyz) = crate::trimesh3_primitive::sphere_yup::<usize, f32>(1.0, 128, 128);
let bvhnodes = crate::bvhnodes_morton::from_triangle_mesh(&tri2vtx, &vtx2xyz, 3);
let bvhnode2aabb =
crate::bvhnode2aabb3::from_uniform_mesh_with_bvh(0, &bvhnodes, &tri2vtx, 3, &vtx2xyz, None);
use rand::RngExt;
use rand::SeedableRng;
let mut reng = rand_chacha::ChaChaRng::seed_from_u64(0u64);
for _iter in 0..1000 {
let x = reng.random::<f32>() * 2.0 - 1.;
let y = reng.random::<f32>() * 2.0 - 1.;
let ray_dir = [0., 0., -1.];
let ray_org = [x, y, 1.];
let res_first = first_intersection_ray(
&ray_org,
&ray_dir,
&TriMeshWithBvh {
tri2vtx: &tri2vtx,
vtx2xyz: &vtx2xyz,
bvhnodes: &bvhnodes,
bvhnode2aabb: &bvhnode2aabb,
},
0,
f32::INFINITY,
);
if let Some((t, _i_tri, _bc)) = res_first {
let p = [
ray_org[0] + t * ray_dir[0],
ray_org[1] + t * ray_dir[1],
ray_org[2] + t * ray_dir[2],
];
assert!(del_geo_core::vec3::norm(&p) < 1.0);
}
{
let mut hits = Vec::<(f32, usize)>::new();
intersections_ray::<usize>(
&mut hits,
&ray_org,
&ray_dir,
&TriMeshWithBvh {
tri2vtx: &tri2vtx,
vtx2xyz: &vtx2xyz,
bvhnodes: &bvhnodes,
bvhnode2aabb: &bvhnode2aabb,
},
0,
);
if let Some((t_first, _i_tri_first, _bc_first)) = res_first {
assert_eq!(hits.len(), 2);
assert!(hits[0].0 == t_first || hits[1].0 == t_first);
} else {
assert!(hits.is_empty());
}
}
}
}