1use std::f32;
17use std::vec::Vec;
18use std::ops::{Add, AddAssign, Sub, SubAssign};
19use cgmath::prelude::*;
20use cgmath::{EuclideanSpace, Rotation, Rotation3, Vector3, Point3, Quaternion, One, Zero};
21
22use smallvec::SmallVec;
23
24use crate::bvh::*;
25use crate::bounds::*;
26use crate::collision::*;
27use crate::geom::*;
28
29#[derive(Copy, Clone, Debug)]
33pub enum Component {
34 Sphere(Sphere),
35 Capsule(Capsule),
36 }
38
39impl Component {
40 pub fn deconstruct(&self) -> (Point3<f32>, Quaternion<f32>, ComponentConstructor) {
43 match self {
44 &Component::Sphere(Sphere{ r, c }) =>
45 (c, Quaternion::one(), ComponentConstructor::Sphere{ r }),
46 &Component::Capsule(Capsule{ r, a, d }) => {
47 let h = d.magnitude();
48 let rot = Quaternion::from_arc(Vector3::new(0.0, 1.0, 0.0) * h, d, None);
49 (a + d * 0.5, rot, ComponentConstructor::Capsule{ r, half_h: h * 0.5 })
50 },
51 }
52 }
53}
54
55impl Volumetric for Component {
56 fn rotate<R: Rotation3<f32>>(self, r: R) -> Self {
57 match self {
58 Component::Sphere(s) => Component::Sphere(s.rotate(r)),
59 Component::Capsule(c) => Component::Capsule(c.rotate(r)),
60 }
61 }
62}
63
64impl From<Sphere> for Component {
65 fn from(s: Sphere) -> Self {
66 Component::Sphere(s)
67 }
68}
69
70impl From<Capsule> for Component {
71 fn from(c: Capsule) -> Self {
72 Component::Capsule(c)
73 }
74}
75
76impl Add<Vector3<f32>> for Component {
77 type Output = Self;
78
79 fn add(self, v: Vector3<f32>) -> Self {
80 match self {
81 Component::Sphere(s) => Component::Sphere(s + v),
82 Component::Capsule(c) => Component::Capsule(c + v),
83 }
84 }
85}
86
87impl Sub<Vector3<f32>> for Component {
88 type Output = Self;
89
90 fn sub(self, v: Vector3<f32>) -> Self {
91 match self {
92 Component::Sphere(s) => Component::Sphere(s - v),
93 Component::Capsule(c) => Component::Capsule(c - v),
94 }
95 }
96}
97
98impl AddAssign<Vector3<f32>> for Component {
99 fn add_assign(&mut self, v: Vector3<f32>) {
100 match self {
101 &mut Component::Sphere(ref mut s) => *s += v,
102 &mut Component::Capsule(ref mut c) => *c += v,
103 }
104 }
105}
106
107impl SubAssign<Vector3<f32>> for Component {
108 fn sub_assign(&mut self, v: Vector3<f32>) {
109 match self {
110 &mut Component::Sphere(ref mut s) => *s -= v,
111 &mut Component::Capsule(ref mut c) => *c -= v,
112 }
113 }
114}
115
116impl Shape for Component {
117 fn center(&self) -> Point3<f32> {
118 match self {
119 &Component::Sphere(s) => s.center(),
120 &Component::Capsule(c) => c.center(),
121 }
122 }
123
124 fn closest_point(&self, to: Point3<f32>) -> Point3<f32> {
125 match self {
126 &Component::Sphere(s) => s.closest_point(to),
127 &Component::Capsule(c) => c.closest_point(to),
128 }
129 }
130}
131
132impl BoundedBy<AABB> for Component {
133 fn bounds(&self) -> AABB {
134 match self {
135 &Component::Sphere(s) => s.bounds(),
136 &Component::Capsule(c) => c.bounds(),
137 }
138 }
139}
140
141impl BoundedBy<Sphere> for Component {
142 fn bounds(&self) -> Sphere {
143 match self {
144 &Component::Sphere(s) => s.bounds(),
145 &Component::Capsule(c) => c.bounds(),
146 }
147 }
148}
149
150impl<P: Particle> Intersects<Component> for P {
151 fn intersection(&self, rhs: &Component) -> Option<Intersection> {
152 match rhs {
153 &Component::Sphere(ref s) => self.intersection(s),
154 &Component::Capsule(ref c) => self.intersection(c),
155 }
156 }
157}
158
159macro_rules! impl_component_collision {
160 (
161 $recv:ty
162 ) => {
163 impl Contacts<Moving<Component>> for $recv {
164 fn contacts<F: FnMut(Contact)>(&self, rhs: &Moving<Component>, callback: F) -> bool {
165 match rhs.0 {
166 Component::Sphere(s) => self.contacts(&Moving::sweep(s, rhs.1), callback),
167 Component::Capsule(c) => self.contacts(&Moving::sweep(c, rhs.1), callback),
168 }
169 }
170 }
171 };
172}
173
174impl_component_collision!{ Plane }
175impl_component_collision!{ Triangle }
176impl_component_collision!{ Rectangle }
177impl_component_collision!{ Sphere }
178impl_component_collision!{ Capsule }
179
180impl<RHS> Contacts<RHS> for Moving<Component>
181where
182 RHS: Contacts<Moving<Sphere>> + Contacts<Moving<Capsule>>
183{
184 fn contacts<F: FnMut(Contact)>(&self, rhs: &RHS, mut callback: F) -> bool {
185 match self.0 {
186 Component::Sphere(s) => rhs.contacts(&Moving::sweep(s, self.1), |c|callback(-c)),
187 Component::Capsule(c) => rhs.contacts(&Moving::sweep(c, self.1), |c|callback(-c)),
188 }
189 }
190}
191
192impl LocalContacts<Moving<Component>> for Moving<Component> {
193 fn local_contacts<F: FnMut(LocalContact)>(&self, rhs: &Moving<Component>, mut callback: F) -> bool {
194 self.contacts(
195 rhs,
196 | c | {
197 callback(
198 LocalContact {
199 local_a: c.a + -(self.0.center() + self.1 * c.t).to_vec(),
200 local_b: c.b + -(rhs.0.center() + rhs.1 * c.t).to_vec(),
201 global: c,
202 }
203 );
204 }
205 )
206 }
207}
208
209#[derive(Copy, Clone, Debug)]
211pub enum ComponentConstructor {
212 Sphere{ r: f32 },
213 Capsule{ r: f32, half_h: f32 },
214 }
216
217impl ComponentConstructor {
218 pub fn construct<R: Rotation3<f32>>(&self, p: Point3<f32>, rot: R) -> Component {
220 match self {
221 &ComponentConstructor::Sphere{ r } => Component::Sphere(Sphere{ r, c: p }),
222 &ComponentConstructor::Capsule{ r, half_h } => {
223 let d = rot.rotate_vector(Vector3::new(0.0, 1.0, 0.0) * half_h);
224 Component::Capsule(Capsule{ r: r, a: p + -d, d: d * 2.0 })
225 },
226 }
227 }
228}
229
230#[derive(Clone)]
232pub struct Compound {
233 pub disp: Vector3<f32>,
235 pub rot: Quaternion<f32>,
237 pub shapes: SmallVec<[usize; 1]>,
240 pub bvh: BVH<AABB, Component>,
242}
243
244impl Compound {
245 pub fn new(components: Vec<Component>) -> Self {
246 let mut bvh: BVH<AABB, Component> = BVH::new();
247 let mut shapes: SmallVec<[usize; 1]> = SmallVec::with_capacity(components.len());
248 for component in components.iter() {
249 shapes.push(bvh.insert(component, *component));
250 }
251 Compound {
252 disp: Vector3::zero(),
253 rot: Quaternion::one(),
254 shapes,
255 bvh,
256 }
257 }
258}
259
260impl AddAssign<Vector3<f32>> for Compound {
261 fn add_assign(&mut self, v: Vector3<f32>) {
262 self.disp += v
263 }
264}
265
266impl SubAssign<Vector3<f32>> for Compound {
267 fn sub_assign(&mut self, v: Vector3<f32>) {
268 self.disp -= v
269 }
270}
271
272impl BoundedBy<AABB> for Compound {
273 fn bounds(&self) -> AABB {
274 self.bvh[self.bvh.root()].rotate(self.rot) + self.disp
275 }
276}
277
278impl BoundedBy<Sphere> for Compound {
279 fn bounds(&self) -> Sphere {
280 let s: Sphere = self.bvh[self.bvh.root()].bounds();
281 s + self.disp
282 }
283}
284
285impl Shape for Compound {
286 fn center(&self) -> Point3<f32> {
291 Point3::from_vec(self.disp)
292 }
293
294 fn closest_point(&self, to: Point3<f32>) -> Point3<f32> {
295 let mut best_p = Point3::new(0.0, 0.0, 0.0);
296 let mut best_dist: f32 = f32::INFINITY;
297 for shape in self.shapes.iter() {
298 let new_p = self.bvh.get_leaf(*shape).closest_point(to);
299 let new_dist = (to - new_p).magnitude2();
300 if new_dist < best_dist {
301 best_p = new_p;
302 best_dist = new_dist;
303 }
304 }
305 best_p
306 }
307}
308
309impl<P: Particle> Intersects<Compound> for P {
310 fn intersection(&self, rhs: &Compound) -> Option<Intersection> {
311 let conj_rot = rhs.rot.conjugate();
312 let p = conj_rot.rotate_point(self.pos() + -rhs.disp) + rhs.disp;
313 let d = conj_rot.rotate_vector(self.dir());
314 let r = Ray{ p, d };
315 let mut result: Option<Intersection> = None;
316 rhs.bvh.raytrace(&r, |&comp, inter| {
317 if inter.t > P::DT {
318 return;
319 }
320 let shape = comp.rotate(rhs.rot) + rhs.disp;
321 if let Some(inter) = self.intersection(&shape) {
322 if let Some(res) = result {
323 if inter.t > res.t {
324 return;
325 }
326 }
327 result = Some(inter)
328 }
329 });
330 result
331 }
332}
333
334impl<RHS> Contacts<RHS> for Compound
335where
336 RHS: Contacts<Component> + BoundedBy<AABB>
337{
338 fn contacts<F: FnMut(Contact)>(&self, rhs: &RHS, mut callback: F) -> bool {
339 let conj_rot = self.rot.conjugate();
341 let mut rhs_bounds = rhs.bounds().rotate(conj_rot);
342 let rhs_center = rhs_bounds.center();
343 let bounds_disp = conj_rot.rotate_point(rhs_center + -self.disp) + self.disp;
344 rhs_bounds.set_pos(bounds_disp);
345 let mut collided = false;
346 self.bvh.query(&rhs_bounds, |&comp| {
347 let shape = comp.rotate_about(self.rot, Point3::new(0.0, 0.0, 0.0)) + self.disp;
348 rhs.contacts(&shape, |c| { collided = true; callback(-c) });
349 });
350 collided
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 mod compound {
357 use cgmath::InnerSpace;
358 use crate::compound::*;
359 use crate::collision::Contacts;
360
361 #[test]
362 fn test_compound() {
363 let components = vec![
364 Component::Sphere(Sphere{ c: Point3::new(-5.0, 0.0, 0.0), r: 1.0 }),
365 Component::Sphere(Sphere{ c: Point3::new(5.0, 0.0, 0.0), r: 1.0 }),
366 ];
367 let mut compound = Compound::new(components);
368 let test_sphere = Moving::sweep(Sphere{ c: Point3::new(0.0, 8.0, 0.0), r: 1.0 },
369 Vector3::new(0.0, -1.5, 0.0));
370 assert!(!compound.contacts(&test_sphere, |c: Contact| { panic!("c = {:?}", c); }));
371 compound.rot = Quaternion::from_arc(Vector3::new(1.0, 0.0, 0.0),
373 Vector3::new(0.0, 1.0, 0.0),
374 None).normalize();
375 let contact: Contact = compound.last_contact(&test_sphere).unwrap();
376 assert_relative_eq!(contact.t, 0.6666663, epsilon = COLLISION_EPSILON);
377 assert_relative_eq!(contact.a, Point3::new(0.0, 6.0, 0.0), epsilon = COLLISION_EPSILON);
378
379 let static_rect = Rect {
380 c: Point3::new(0.0, -2.0, 0.0),
381 u: [ Vector3::new(1.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0) ],
382 e: [ 6.0, 6.0 ],
383 };
384
385 compound.rot = Quaternion::one();
386
387 let _contact: Contact = compound.last_contact(&Moving::sweep(static_rect, Vector3::new(0.0, 3.0, 0.0))).unwrap();
388 }
389 }
390}