rg3d_physics/
static_geometry.rs

1use rg3d_core::{
2    math::{
3        vec3::Vec3,
4        plane::Plane
5    },
6    visitor::{Visit, VisitResult, Visitor, VisitError},
7    octree::Octree
8};
9
10#[derive(Default, Clone, Debug)]
11pub struct StaticGeometry {
12    pub(in crate) triangles: Vec<StaticTriangle>,
13    pub(in crate) octree: Octree,
14    save_triangles: bool
15}
16
17impl StaticGeometry {
18    pub const OCTREE_THRESHOLD: usize = 64;
19
20    pub fn new(triangles: Vec<StaticTriangle>, save_triangles: bool) -> Self {
21        let raw_triangles: Vec<[Vec3; 3]> = triangles.iter().map(|st| st.points).collect();
22
23        Self {
24            octree: Octree::new(&raw_triangles, Self::OCTREE_THRESHOLD),
25            triangles,
26            save_triangles
27        }
28    }
29
30    pub fn set_save_triangles(&mut self, save_triangles: bool) {
31        self.save_triangles = save_triangles;
32    }
33
34    pub fn save_triangles(&self) -> bool {
35        self.save_triangles
36    }
37}
38
39impl Visit for StaticGeometry {
40    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
41        visitor.enter_region(name)?;
42
43        if !visitor.is_reading() {
44            if self.save_triangles {
45                self.triangles.visit("Triangles", visitor)?;
46            } else {
47                let mut empty: Vec<StaticTriangle> = Vec::new();
48                empty.visit("Triangles", visitor)?;
49            }
50        } else {
51            self.triangles.visit("Triangles", visitor)?;
52        }
53
54        if visitor.is_reading() {
55            let raw_triangles: Vec<[Vec3; 3]> = self.triangles.iter().map(|st| st.points).collect();
56            self.octree = Octree::new(&raw_triangles, Self::OCTREE_THRESHOLD);
57        }
58
59        let _ = self.save_triangles.visit("SaveTriangles", visitor);
60
61        visitor.leave_region()
62    }
63}
64
65#[derive(Clone, Debug)]
66pub struct StaticTriangle {
67    pub points: [Vec3; 3],
68    pub ca: Vec3,
69    pub ba: Vec3,
70    pub ca_dot_ca: f32,
71    pub ca_dot_ba: f32,
72    pub ba_dot_ba: f32,
73    pub inv_denom: f32,
74    pub plane: Plane,
75}
76
77impl Default for StaticTriangle {
78    fn default() -> Self {
79        Self {
80            points: Default::default(),
81            ca: Default::default(),
82            ba: Default::default(),
83            ca_dot_ca: 0.0,
84            ca_dot_ba: 0.0,
85            ba_dot_ba: 0.0,
86            inv_denom: 0.0,
87            plane: Default::default(),
88        }
89    }
90}
91
92impl Visit for StaticTriangle {
93    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
94        visitor.enter_region(name)?;
95
96        let mut a = self.points[0];
97        a.visit("A", visitor)?;
98
99        let mut b = self.points[1];
100        b.visit("B", visitor)?;
101
102        let mut c = self.points[2];
103        c.visit("C", visitor)?;
104
105        *self = match Self::from_points(&a, &b, &c) {
106            None => return Err(VisitError::User(String::from("invalid triangle"))),
107            Some(triangle) => triangle,
108        };
109
110        visitor.leave_region()
111    }
112}
113
114impl StaticTriangle {
115    ///
116    /// Creates static triangle from tree points and precomputes some data
117    /// to speedup collision detection in runtime. This function may fail
118    /// if degenerated triangle was passed into.
119    ///
120    pub fn from_points(a: &Vec3, b: &Vec3, c: &Vec3) -> Option<StaticTriangle> {
121        let ca = *c - *a;
122        let ba = *b - *a;
123        let ca_dot_ca = ca.dot(&ca);
124        let ca_dot_ba = ca.dot(&ba);
125        let ba_dot_ba = ba.dot(&ba);
126        if let Ok(plane) = Plane::from_normal_and_point(&ba.cross(&ca), a) {
127            return Some(StaticTriangle {
128                points: [*a, *b, *c],
129                ba,
130                ca: *c - *a,
131                ca_dot_ca,
132                ca_dot_ba,
133                ba_dot_ba,
134                inv_denom: 1.0 / (ca_dot_ca * ba_dot_ba - ca_dot_ba * ca_dot_ba),
135                plane,
136            });
137        }
138
139        None
140    }
141
142    /// Checks if point lies inside or at edge of triangle. Uses a lot of precomputed data.
143    pub fn contains_point(&self, p: Vec3) -> bool {
144        let vp = p - self.points[0];
145        let dot02 = self.ca.dot(&vp);
146        let dot12 = self.ba.dot(&vp);
147        let u = (self.ba_dot_ba * dot02 - self.ca_dot_ba * dot12) * self.inv_denom;
148        let v = (self.ca_dot_ca * dot12 - self.ca_dot_ba * dot02) * self.inv_denom;
149        u >= 0.0 && v >= 0.0 && u + v < 1.0
150    }
151}