use num::Float; use na::{Point2, Rotate, Translate, BaseFloat}; use na; use ray::{Ray, RayCast, RayIntersection}; use entities::shape::Ball; use math::{Point, Vector}; #[inline] fn ball_uv(normal: &V) -> Option> where V: Vector { if na::dimension::() == 3 { let two_pi: V::Scalar = BaseFloat::two_pi(); let pi: V::Scalar = BaseFloat::pi(); let _0_5: V::Scalar = na::cast(0.5f64); let uvx = _0_5 + normal[2].atan2(normal[0]) / two_pi; let uvy = _0_5 - normal[1].asin() / pi; Some(Point2::new(uvx, uvy)) } else { None } } impl RayCast for Ball<::Scalar> where P: Point, M: Translate

+ Rotate { #[inline] fn toi_with_ray(&self, m: &M, ray: &Ray

, solid: bool) -> Option<::Scalar> { ball_toi_with_ray(&m.translate(&na::origin()), self.radius(), ray, solid).1 } #[inline] fn toi_and_normal_with_ray(&self, m: &M, ray: &Ray

, solid: bool) -> Option> { let center = m.translate(&na::origin()); let (inside, inter) = ball_toi_with_ray(¢er, self.radius(), ray, solid); inter.map(|n| { let pos = ray.origin + ray.dir * n - center; let normal = na::normalize(&pos); RayIntersection::new(n, if inside { -normal } else { normal }) }) } #[inline] fn toi_and_normal_and_uv_with_ray(&self, m: &M, ray: &Ray

, solid: bool) -> Option> { let center = m.translate(&na::origin()); let (inside, inter) = ball_toi_with_ray(¢er, self.radius(), ray, solid); inter.map(|n| { let pos = ray.origin + ray.dir * n - center; let normal = na::normalize(&pos); let uv = ball_uv(&normal); RayIntersection::new_with_uvs(n, if inside { -normal } else { normal }, uv) }) } } /// Computes the time of impact of a ray on a ball. #[inline] pub fn ball_toi_with_ray

(center: &P, radius: ::Scalar, ray: &Ray

, solid: bool) -> (bool, Option<::Scalar>) where P: Point { let dcenter = ray.origin - *center; let a = na::norm_squared(&ray.dir); let b = na::dot(&dcenter, &ray.dir); let c = na::norm_squared(&dcenter) - radius * radius; if c > na::zero() && b > na::zero() { (false, None) } else { let delta = b * b - a * c; if delta < na::zero() { // no solution (false, None) } else { let t = (-b - delta.sqrt()) / a; if t <= na::zero() { // origin inside of the ball if solid { (true, Some(na::zero())) } else { (true, Some((-b + delta.sqrt()) / a)) } } else { (false, Some(t)) } } } }