1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use alga::general::Real;
use na;
#[cfg(feature = "dim3")]
use na::Point2;
use query::{Ray, RayCast, RayIntersection};
use shape::Ball;
use math::{Isometry, Point};
#[cfg(feature = "dim3")]
use math::Vector;
#[cfg(feature = "dim3")]
#[inline]
fn ball_uv<N: Real>(normal: &Vector<N>) -> Point2<N> {
let two_pi: N = Real::two_pi();
let pi: N = Real::pi();
let _0_5: N = na::convert(0.5f64);
let uvx = _0_5 + normal[2].atan2(normal[0]) / two_pi;
let uvy = _0_5 - normal[1].asin() / pi;
Point2::new(uvx, uvy)
}
impl<N: Real> RayCast<N> for Ball<N> {
#[inline]
fn toi_with_ray(&self, m: &Isometry<N>, ray: &Ray<N>, solid: bool) -> Option<N> {
ball_toi_with_ray(&Point::from_coordinates(m.translation.vector), self.radius(), ray, solid).1
}
#[inline]
fn toi_and_normal_with_ray(
&self,
m: &Isometry<N>,
ray: &Ray<N>,
solid: bool,
) -> Option<RayIntersection<N>> {
let center = Point::from_coordinates(m.translation.vector);
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 })
})
}
#[cfg(feature = "dim3")]
#[inline]
fn toi_and_normal_and_uv_with_ray(
&self,
m: &Isometry<N>,
ray: &Ray<N>,
solid: bool,
) -> Option<RayIntersection<N>> {
let center = Point::from_coordinates(m.translation.vector);
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 }, Some(uv))
})
}
}
#[inline]
pub fn ball_toi_with_ray<N: Real>(
center: &Point<N>,
radius: N,
ray: &Ray<N>,
solid: bool,
) -> (bool, Option<N>) {
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() {
(false, None)
} else {
let t = (-b - delta.sqrt()) / a;
if t <= na::zero() {
if solid {
(true, Some(na::zero()))
} else {
(true, Some((-b + delta.sqrt()) / a))
}
} else {
(false, Some(t))
}
}
}
}