Skip to main content

pfx_physics/rigid/
gravity.rs

1use super::Pose;
2use super::convert::{Vec3, iso, vec};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum Falloff {
6    Constant,
7    Inverse,
8    InverseSquare,
9}
10
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub enum Region {
13    Sphere { center: [f32; 3], radius: f32 },
14    Box { pose: Pose, half: [f32; 3] },
15}
16
17impl Region {
18    pub fn contains(&self, point: [f32; 3]) -> bool {
19        match *self {
20            Self::Sphere { center, radius } => {
21                vec(point).distance_squared(vec(center)) <= radius * radius
22            }
23            Self::Box { pose, half } => {
24                let local = iso(pose).inverse_transform_point(vec(point));
25                local.x.abs() <= half[0] && local.y.abs() <= half[1] && local.z.abs() <= half[2]
26            }
27        }
28    }
29}
30
31#[derive(Clone, Copy, Debug, PartialEq)]
32pub enum Source {
33    Point {
34        center: [f32; 3],
35        strength: f32,
36        falloff: Falloff,
37        range: f32,
38        softening: f32,
39    },
40    Zone {
41        region: Region,
42        gravity: [f32; 3],
43        replace: bool,
44    },
45}
46
47impl Source {
48    pub fn attractor(center: [f32; 3], strength: f32) -> Self {
49        Self::Point {
50            center,
51            strength,
52            falloff: Falloff::InverseSquare,
53            range: f32::INFINITY,
54            softening: 0.01,
55        }
56    }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
60pub struct SourceId(pub(crate) u32);
61
62pub(crate) fn acceleration(sources: &[(SourceId, Source)], world: Vec3, point: Vec3) -> Vec3 {
63    let mut base = world;
64    let mut extra = Vec3::ZERO;
65    for (_, source) in sources {
66        match *source {
67            Source::Point {
68                center,
69                strength,
70                falloff,
71                range,
72                softening,
73            } => {
74                let towards = vec(center) - point;
75                let distance = towards.length();
76                if distance > range || distance <= f32::EPSILON {
77                    continue;
78                }
79                let d = distance.max(softening);
80                let scale = match falloff {
81                    Falloff::Constant => 1.0,
82                    Falloff::Inverse => 1.0 / d,
83                    Falloff::InverseSquare => 1.0 / (d * d),
84                };
85                extra += towards / distance * (strength * scale);
86            }
87            Source::Zone {
88                region,
89                gravity,
90                replace,
91            } => {
92                if !region.contains(point.to_array()) {
93                    continue;
94                }
95                if replace {
96                    base = vec(gravity);
97                } else {
98                    extra += vec(gravity);
99                }
100            }
101        }
102    }
103    base - world + extra
104}