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
107
108
109
110
111
112
113
114
115
116
117
118
use de_objects::ObjectCollider;
use parry3d::{
    bounding_volume::{Aabb, BoundingVolume},
    math::Isometry,
    query::{Ray, RayCast},
};

pub trait ColliderWithCache {
    /// World-space AABB of the collider.
    fn world_aabb(&self) -> &Aabb;

    /// World-to-collider mapping.
    fn position(&self) -> &Isometry<f32>;

    fn inner(&self) -> &ObjectCollider;
}

/// Entity collider with cached entity-space and world-space AABBs for fast
/// query pre-filtering.
pub struct LocalCollider {
    object_collider: ObjectCollider,
    /// World-space position of the collider.
    position: Isometry<f32>,
    /// Collider-space AABB.
    local_aabb: Aabb,
    /// World-space AABB. It is kept for fast geometric pre-filtering.
    world_aabb: Aabb,
}

impl LocalCollider {
    /// Creates a new entity collider from entity shape and position.
    // Needs to be public because it is used in a benchmark.
    pub fn new(object_collider: ObjectCollider, position: Isometry<f32>) -> Self {
        let local_aabb = object_collider.aabb();
        let world_aabb = local_aabb.transform_by(&position);

        Self {
            object_collider,
            position,
            local_aabb,
            world_aabb,
        }
    }

    /// Updates position of cached world-space AABB of the collider.
    pub(super) fn update_position(&mut self, position: Isometry<f32>) {
        self.world_aabb = self.local_aabb.transform_by(&position);
        self.position = position;
    }

    pub(super) fn cast_ray(&self, ray: &Ray, max_toi: f32) -> Option<f32> {
        if self.world_aabb.intersects_local_ray(ray, max_toi) {
            self.object_collider.cast_ray(&self.position, ray, max_toi)
        } else {
            None
        }
    }

    pub(super) fn intersects(&self, rhs: &impl ColliderWithCache) -> bool {
        if self.query_aabb(rhs.world_aabb()) {
            self.object_collider
                .intersects(&self.position, rhs.inner(), rhs.position())
        } else {
            false
        }
    }

    /// Returns true if world-space axis-aligned bounding boxes of the two
    /// colliders intersect.
    pub(super) fn query_aabb(&self, aabb: &Aabb) -> bool {
        self.world_aabb.intersects(aabb)
    }
}

impl ColliderWithCache for LocalCollider {
    fn world_aabb(&self) -> &Aabb {
        &self.world_aabb
    }

    fn position(&self) -> &Isometry<f32> {
        &self.position
    }

    fn inner(&self) -> &ObjectCollider {
        &self.object_collider
    }
}

pub struct QueryCollider<'a> {
    inner: &'a ObjectCollider,
    position: Isometry<f32>,
    world_aabb: Aabb,
}

impl<'a> QueryCollider<'a> {
    pub fn new(inner: &'a ObjectCollider, position: Isometry<f32>) -> Self {
        let world_aabb = inner.aabb().transform_by(&position);
        Self {
            inner,
            position,
            world_aabb,
        }
    }
}

impl<'a> ColliderWithCache for QueryCollider<'a> {
    fn world_aabb(&self) -> &Aabb {
        &self.world_aabb
    }

    fn position(&self) -> &Isometry<f32> {
        &self.position
    }

    fn inner(&self) -> &ObjectCollider {
        self.inner
    }
}