parry3d/query/ray/
ray_voxels.rsuse crate::math::{Real, Vector};
use crate::query::{Ray, RayCast, RayIntersection};
use crate::shape::{FeatureId, Voxels};
impl RayCast for Voxels {
#[inline]
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_time_of_impact: Real,
solid: bool,
) -> Option<RayIntersection> {
use num_traits::Bounded;
let aabb = self.local_aabb();
let (min_t, mut max_t) = aabb.clip_ray_parameters(ray)?;
#[cfg(feature = "dim2")]
let ii = [0, 1];
#[cfg(feature = "dim3")]
let ii = [0, 1, 2];
if min_t > max_time_of_impact {
return None;
}
max_t = max_t.min(max_time_of_impact);
let clip_ray_a = ray.point_at(min_t);
let voxel_key_signed = self.voxel_at_point_unchecked(clip_ray_a);
let mut voxel_key = self.clamp_voxel(voxel_key_signed);
let [domain_mins, domain_maxs] = self.domain();
loop {
let voxel = self.voxel_state(voxel_key);
let aabb = self.voxel_aabb(voxel_key);
if !voxel.is_empty() {
let hit = aabb.cast_local_ray_and_get_normal(ray, max_t, solid);
if let Some(mut hit) = hit {
hit.feature = FeatureId::Face(self.linear_index(voxel_key));
return Some(hit);
}
}
let toi = ii.map(|i| {
if ray.dir[i] > 0.0 {
let t = (aabb.maxs[i] - ray.origin[i]) / ray.dir[i];
if t < 0.0 {
(Real::max_value(), true)
} else {
(t, true)
}
} else if ray.dir[i] < 0.0 {
let t = (aabb.mins[i] - ray.origin[i]) / ray.dir[i];
if t < 0.0 {
(Real::max_value(), false)
} else {
(t, false)
}
} else {
(Real::max_value(), false)
}
});
#[cfg(feature = "dim2")]
if toi[0].0 > max_t && toi[1].0 > max_t {
break;
}
#[cfg(feature = "dim3")]
if toi[0].0 > max_t && toi[1].0 > max_t && toi[2].0 > max_t {
break;
}
let imin = Vector::from(toi.map(|t| t.0)).imin();
if toi[imin].1 {
if voxel_key[imin] < domain_maxs[imin] - 1 {
voxel_key[imin] += 1;
} else {
break;
}
} else if voxel_key[imin] > domain_mins[imin] {
voxel_key[imin] -= 1;
} else {
break;
}
}
None
}
}