use std::ops::Index;
use num::Zero;
use alga::general::Id;
use alga::linear::NormedSpace;
use na::{self, Point2, Vector3};
use query::{ray_internal, Ray, RayCast, RayIntersection};
use shape::{BaseMesh, BaseMeshElement, Polyline, TriMesh};
use bounding_volume::AABB;
use partitioning::BVTCostFn;
use math::{Isometry, Point};
impl
RayCast
for BaseMesh
where
P: Point,
M: Isometry
,
I: Index,
E: BaseMeshElement + RayCast,
{
#[inline]
fn toi_with_ray(&self, m: &M, ray: &Ray
, _: bool) -> Option {
let ls_ray = ray.inverse_transform_by(m);
let mut cost_fn = BaseMeshRayToiCostFn {
mesh: self,
ray: &ls_ray,
};
self.bvt()
.best_first_search(&mut cost_fn)
.map(|(_, res)| res)
}
#[inline]
fn toi_and_normal_with_ray(
&self,
m: &M,
ray: &Ray,
_: bool,
) -> Option> {
let ls_ray = ray.inverse_transform_by(m);
let mut cost_fn = BaseMeshRayToiAndNormalCostFn {
mesh: self,
ray: &ls_ray,
};
self.bvt()
.best_first_search(&mut cost_fn)
.map(|(_, mut res)| {
res.normal = m.rotate_vector(&res.normal);
res
})
}
fn toi_and_normal_and_uv_with_ray(
&self,
m: &M,
ray: &Ray,
solid: bool,
) -> Option> {
if self.uvs().is_none() || na::dimension::() != 3 {
return self.toi_and_normal_with_ray(m, ray, solid);
}
let ls_ray = ray.inverse_transform_by(m);
let mut cost_fn = BaseMeshRayToiAndNormalAndUVsCostFn {
mesh: self,
ray: &ls_ray,
};
let cast = self.bvt().best_first_search(&mut cost_fn);
match cast {
None => None,
Some((best, inter)) => {
let toi = inter.0.toi;
let n = inter.0.normal;
let uv = inter.1; // barycentric coordinates to compute the exact uvs.
let idx = &self.indices()[*best];
let uvs = self.uvs().as_ref().unwrap();
let uv1 = uvs[idx[0]];
let uv2 = uvs[idx[1]];
let uv3 = uvs[idx[2]];
let uvx = uv1.x * uv.x + uv2.x * uv.y + uv3.x * uv.z;
let uvy = uv1.y * uv.x + uv2.y * uv.y + uv3.y * uv.z;
// XXX: this interpolation should be done on the two other ray cast too!
match *self.normals() {
None => Some(RayIntersection::new_with_uvs(
toi,
m.rotate_vector(&n),
Some(Point2::new(uvx, uvy)),
)),
Some(ref ns) => {
let n1 = &ns[idx[0]];
let n2 = &ns[idx[1]];
let n3 = &ns[idx[2]];
let mut n123 = *n1 * uv.x + *n2 * uv.y + *n3 * uv.z;
if n123.normalize_mut().is_zero() {
Some(RayIntersection::new_with_uvs(
toi,
m.rotate_vector(&n),
Some(Point2::new(uvx, uvy)),
))
} else {
if na::dot(&n123, &ls_ray.dir) > na::zero() {
Some(RayIntersection::new_with_uvs(
toi,
-m.rotate_vector(&n123),
Some(Point2::new(uvx, uvy)),
))
} else {
Some(RayIntersection::new_with_uvs(
toi,
m.rotate_vector(&n123),
Some(Point2::new(uvx, uvy)),
))
}
}
}
}
}
}
}
}
/*
* Costs functions.
*/
struct BaseMeshRayToiCostFn<'a, P: 'a + Point, I: 'a, E: 'a> {
mesh: &'a BaseMesh,
ray: &'a Ray
,
}
impl<'a, P, I, E> BVTCostFn> for BaseMeshRayToiCostFn<'a, P, I, E>
where
P: Point,
E: BaseMeshElement + RayCast,
{
type UserData = P::Real;
#[inline]
fn compute_bv_cost(&mut self, aabb: &AABB
) -> Option {
aabb.toi_with_ray(&Id::new(), self.ray, true)
}
#[inline]
fn compute_b_cost(&mut self, b: &usize) -> Option<(P::Real, P::Real)> {
self.mesh
.element_at(*b)
.toi_with_ray(&Id::new(), self.ray, true)
.map(|toi| (toi, toi))
}
}
struct BaseMeshRayToiAndNormalCostFn<'a, P: 'a + Point, I: 'a, E: 'a> {
mesh: &'a BaseMesh,
ray: &'a Ray
,
}
impl<'a, P, I, E> BVTCostFn> for BaseMeshRayToiAndNormalCostFn<'a, P, I, E>
where
P: Point,
E: BaseMeshElement + RayCast,
{
type UserData = RayIntersection;
#[inline]
fn compute_bv_cost(&mut self, aabb: &AABB) -> Option {
aabb.toi_with_ray(&Id::new(), self.ray, true)
}
#[inline]
fn compute_b_cost(&mut self, b: &usize) -> Option<(P::Real, RayIntersection)> {
self.mesh
.element_at(*b)
.toi_and_normal_with_ray(&Id::new(), self.ray, true)
.map(|inter| (inter.toi, inter))
}
}
struct BaseMeshRayToiAndNormalAndUVsCostFn<'a, P: 'a + Point, I: 'a, E: 'a> {
mesh: &'a BaseMesh,
ray: &'a Ray
,
}
impl<'a, P, I, E> BVTCostFn>
for BaseMeshRayToiAndNormalAndUVsCostFn<'a, P, I, E>
where
P: Point,
I: Index,
E: BaseMeshElement + RayCast,
{
type UserData = (RayIntersection, Vector3);
#[inline]
fn compute_bv_cost(&mut self, aabb: &AABB) -> Option {
aabb.toi_with_ray(&Id::new(), self.ray, true)
}
#[inline]
fn compute_b_cost(
&mut self,
b: &usize,
) -> Option<(P::Real, (RayIntersection, Vector3))> {
let vs = &self.mesh.vertices()[..];
let idx = &self.mesh.indices()[*b];
let a = &vs[idx[0]];
let b = &vs[idx[1]];
let c = &vs[idx[2]];
ray_internal::triangle_ray_intersection(a, b, c, self.ray).map(|inter| (inter.0.toi, inter))
}
}
/*
* fwd impls. to the exact shapes.
*/
impl> RayCast for TriMesh
{
#[inline]
fn toi_with_ray(&self, m: &M, ray: &Ray
, solid: bool) -> Option {
self.base_mesh().toi_with_ray(m, ray, solid)
}
#[inline]
fn toi_and_normal_with_ray(
&self,
m: &M,
ray: &Ray,
solid: bool,
) -> Option> {
self.base_mesh().toi_and_normal_with_ray(m, ray, solid)
}
#[inline]
fn toi_and_normal_and_uv_with_ray(
&self,
m: &M,
ray: &Ray,
solid: bool,
) -> Option> {
self.base_mesh()
.toi_and_normal_and_uv_with_ray(m, ray, solid)
}
}
impl> RayCast for Polyline
{
#[inline]
fn toi_with_ray(&self, m: &M, ray: &Ray
, solid: bool) -> Option {
self.base_mesh().toi_with_ray(m, ray, solid)
}
#[inline]
fn toi_and_normal_with_ray(
&self,
m: &M,
ray: &Ray,
solid: bool,
) -> Option> {
self.base_mesh().toi_and_normal_with_ray(m, ray, solid)
}
#[inline]
fn toi_and_normal_and_uv_with_ray(
&self,
m: &M,
ray: &Ray,
solid: bool,
) -> Option> {
self.base_mesh()
.toi_and_normal_and_uv_with_ray(m, ray, solid)
}
}