use std::mem; use num::Float; use na::{Transform, Rotate, Point2, Bounded}; use na; use ray::{Ray, RayCast, RayIntersection}; use entities::bounding_volume::AABB; use math::{Point, Vector}; impl RayCast for AABB

where P: Point, M: Transform

+ Rotate { fn toi_with_ray(&self, m: &M, ray: &Ray

, solid: bool) -> Option<::Scalar> { let ls_ray = Ray::new(m.inverse_transform(&ray.origin), m.inverse_rotate(&ray.dir)); let mut tmin: ::Scalar = na::zero(); let mut tmax: ::Scalar = Bounded::max_value(); for i in 0usize .. na::dimension::

() { if na::is_zero(&ls_ray.dir[i]) { if ls_ray.origin[i] < (*self.mins())[i] || ls_ray.origin[i] > (*self.maxs())[i] { return None } } else { let _1: ::Scalar = na::one(); let denom = _1 / ls_ray.dir[i]; let mut inter_with_near_plane = ((*self.mins())[i] - ls_ray.origin[i]) * denom; let mut inter_with_far_plane = ((*self.maxs())[i] - ls_ray.origin[i]) * denom; if inter_with_near_plane > inter_with_far_plane { mem::swap(&mut inter_with_near_plane, &mut inter_with_far_plane) } tmin = tmin.max(inter_with_near_plane); tmax = tmax.min(inter_with_far_plane); if tmin > tmax { return None; } } } if na::is_zero(&tmin) && !solid { Some(tmax) } else { Some(tmin) } } #[inline] fn toi_and_normal_with_ray(&self, m: &M, ray: &Ray

, solid: bool) -> Option> { let ls_ray = Ray::new(m.inverse_transform(&ray.origin), m.inverse_rotate(&ray.dir)); ray_aabb(self, &ls_ray, solid).map(|(t, n, _)| RayIntersection::new(t, m.rotate(&n))) } fn toi_and_normal_and_uv_with_ray(&self, m: &M, ray: &Ray

, solid: bool) -> Option> { do_toi_and_normal_and_uv_with_ray(m, self, ray, solid) } } fn do_toi_and_normal_and_uv_with_ray(m: &M, aabb: &AABB

, ray: &Ray

, solid: bool) -> Option> where P: Point, M: Transform

+ Rotate { if na::dimension::

() != 3 { aabb.toi_and_normal_with_ray(m, ray, solid) } else { let ls_ray = Ray::new(m.inverse_transform(&ray.origin), m.inverse_rotate(&ray.dir)); ray_aabb(aabb, &ls_ray, solid).map(|(t, n, s)| { let pt = ls_ray.origin + ls_ray.dir * t; let dpt = pt - *aabb.mins(); let scale = *aabb.maxs() - *aabb.mins(); let id = na::abs(&s); let gs_n = m.rotate(&n); if id == 1 { RayIntersection::new_with_uvs(t, gs_n, Some(Point2::new(dpt[1] / scale[1], dpt[2] / scale[2]))) } else if id == 2 { RayIntersection::new_with_uvs(t, gs_n, Some(Point2::new(dpt[2] / scale[2], dpt[0] / scale[0]))) } else { RayIntersection::new_with_uvs(t, gs_n, Some(Point2::new(dpt[0] / scale[0], dpt[1] / scale[1]))) } }) } } fn ray_aabb

(aabb: &AABB

, ray: &Ray

, solid: bool) -> Option<(::Scalar, P::Vect, isize)> where P: Point { let mut tmax: ::Scalar = Bounded::max_value(); let mut tmin: ::Scalar = -tmax; let mut near_side = 0; let mut far_side = 0; let mut near_diag = false; let mut far_diag = false; for i in 0usize .. na::dimension::

() { if na::is_zero(&ray.dir[i]) { if ray.origin[i] < (*aabb.mins())[i] || ray.origin[i] > (*aabb.maxs())[i] { return None } } else { let _1: ::Scalar = na::one(); let denom = _1 / ray.dir[i]; let flip_sides; let mut inter_with_near_plane = ((*aabb.mins())[i] - ray.origin[i]) * denom; let mut inter_with_far_plane = ((*aabb.maxs())[i] - ray.origin[i]) * denom; if inter_with_near_plane > inter_with_far_plane { flip_sides = true; mem::swap(&mut inter_with_near_plane, &mut inter_with_far_plane) } else { flip_sides = false; } if inter_with_near_plane > tmin { tmin = inter_with_near_plane; near_side = if flip_sides { -(i as isize + 1) } else { i as isize + 1 }; near_diag = false; } else if inter_with_near_plane == tmin { near_diag = true; } if inter_with_far_plane < tmax { tmax = inter_with_far_plane; far_side = if !flip_sides { -(i as isize + 1) } else { i as isize + 1 }; far_diag = false; } else if inter_with_far_plane == tmax { far_diag = true; } if tmin > tmax { return None; } } } if tmin < na::cast(0.0f64) { // the ray starts inside of the box if solid { Some((na::zero(), na::zero(), far_side)) } else { if far_diag { Some((tmax, -na::normalize(&ray.dir), far_side)) } else { let mut normal = na::zero::(); if far_side < 0 { normal[(-far_side - 1) as usize] = -na::one::<::Scalar>(); } else { normal[(far_side - 1) as usize] = na::one::<::Scalar>(); } Some((tmax, normal, far_side)) } } } else { if near_diag { Some((tmin, -na::normalize(&ray.dir), near_side)) } else { let mut normal = na::zero::(); if near_side < 0 { normal[(-near_side - 1) as usize] = na::one::<::Scalar>(); } else { normal[(near_side - 1) as usize] = -na::one::<::Scalar>(); } Some((tmin, normal, near_side)) } } }