Skip to main content

nightshade/ecs/scene/
physics.rs

1use serde::{Deserialize, Serialize};
2
3use crate::plugins::physics::components::{ColliderComponent, ColliderShape, RigidBodyComponent};
4use crate::plugins::physics::types::{InteractionGroups, LockedAxes, RigidBodyType};
5
6use super::asset_uuid::AssetUuid;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
9pub enum SceneBodyType {
10    #[default]
11    Static,
12    Dynamic,
13    KinematicPositionBased,
14    KinematicVelocityBased,
15}
16
17impl SceneBodyType {
18    pub fn to_body_type(self) -> RigidBodyType {
19        match self {
20            SceneBodyType::Static => RigidBodyType::Fixed,
21            SceneBodyType::Dynamic => RigidBodyType::Dynamic,
22            SceneBodyType::KinematicPositionBased => RigidBodyType::KinematicPositionBased,
23            SceneBodyType::KinematicVelocityBased => RigidBodyType::KinematicVelocityBased,
24        }
25    }
26}
27
28impl From<RigidBodyType> for SceneBodyType {
29    fn from(body_type: RigidBodyType) -> Self {
30        match body_type {
31            RigidBodyType::Fixed => SceneBodyType::Static,
32            RigidBodyType::Dynamic => SceneBodyType::Dynamic,
33            RigidBodyType::KinematicPositionBased => SceneBodyType::KinematicPositionBased,
34            RigidBodyType::KinematicVelocityBased => SceneBodyType::KinematicVelocityBased,
35        }
36    }
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ScenePhysics {
41    pub body_type: SceneBodyType,
42    pub collider: SceneCollider,
43    #[serde(default = "default_friction")]
44    pub friction: f32,
45    #[serde(default = "default_restitution")]
46    pub restitution: f32,
47    #[serde(default)]
48    pub mass: Option<f32>,
49    #[serde(default)]
50    pub is_sensor: bool,
51    #[serde(default = "default_collision_membership")]
52    pub collision_membership: u32,
53    #[serde(default = "default_collision_filter")]
54    pub collision_filter: u32,
55    #[serde(default = "default_solver_membership")]
56    pub solver_membership: u32,
57    #[serde(default = "default_solver_filter")]
58    pub solver_filter: u32,
59    #[serde(default)]
60    pub locked_axes: LockedAxes,
61    #[serde(default)]
62    pub linvel: [f32; 3],
63    #[serde(default)]
64    pub angvel: [f32; 3],
65}
66
67fn default_collision_membership() -> u32 {
68    0xFFFFFFFF
69}
70
71fn default_collision_filter() -> u32 {
72    0xFFFFFFFF
73}
74
75fn default_solver_membership() -> u32 {
76    0xFFFFFFFF
77}
78
79fn default_solver_filter() -> u32 {
80    0xFFFFFFFF
81}
82
83fn default_friction() -> f32 {
84    0.8
85}
86
87fn default_restitution() -> f32 {
88    0.1
89}
90
91impl Default for ScenePhysics {
92    fn default() -> Self {
93        Self {
94            body_type: SceneBodyType::Static,
95            collider: SceneCollider::Cuboid {
96                half_extents: [0.5, 0.5, 0.5],
97            },
98            friction: default_friction(),
99            restitution: default_restitution(),
100            mass: None,
101            is_sensor: false,
102            collision_membership: default_collision_membership(),
103            collision_filter: default_collision_filter(),
104            solver_membership: default_solver_membership(),
105            solver_filter: default_solver_filter(),
106            locked_axes: LockedAxes::default(),
107            linvel: [0.0, 0.0, 0.0],
108            angvel: [0.0, 0.0, 0.0],
109        }
110    }
111}
112
113impl ScenePhysics {
114    pub fn static_cuboid(half_extents: [f32; 3]) -> Self {
115        Self {
116            collider: SceneCollider::Cuboid { half_extents },
117            ..Default::default()
118        }
119    }
120
121    pub fn dynamic_ball(radius: f32, mass: f32) -> Self {
122        Self {
123            body_type: SceneBodyType::Dynamic,
124            collider: SceneCollider::Ball { radius },
125            friction: 0.7,
126            restitution: 0.3,
127            mass: Some(mass),
128            ..Default::default()
129        }
130    }
131
132    pub fn dynamic_cuboid(half_extents: [f32; 3], mass: f32) -> Self {
133        Self {
134            body_type: SceneBodyType::Dynamic,
135            collider: SceneCollider::Cuboid { half_extents },
136            friction: 0.7,
137            restitution: 0.2,
138            mass: Some(mass),
139            ..Default::default()
140        }
141    }
142
143    pub fn to_rigid_body(&self) -> RigidBodyComponent {
144        let mut body = match self.body_type {
145            SceneBodyType::Static => RigidBodyComponent::new_static(),
146            SceneBodyType::Dynamic => RigidBodyComponent::new_dynamic(),
147            SceneBodyType::KinematicPositionBased => RigidBodyComponent::new_kinematic(),
148            SceneBodyType::KinematicVelocityBased => RigidBodyComponent {
149                body_type: RigidBodyType::KinematicVelocityBased,
150                ..Default::default()
151            },
152        };
153        if let Some(mass) = self.mass {
154            body = body.with_mass(mass);
155        }
156        body.locked_axes = self.locked_axes;
157        body.linvel = self.linvel;
158        body.angvel = self.angvel;
159        body
160    }
161
162    pub fn to_collider(&self) -> ColliderComponent {
163        let mut collider = match &self.collider {
164            SceneCollider::Cuboid { half_extents } => {
165                ColliderComponent::new_cuboid(half_extents[0], half_extents[1], half_extents[2])
166            }
167            SceneCollider::Ball { radius } => ColliderComponent::new_ball(*radius),
168            SceneCollider::Cylinder {
169                half_height,
170                radius,
171            } => ColliderComponent::new_cylinder(*half_height, *radius),
172            SceneCollider::Capsule {
173                half_height,
174                radius,
175            } => ColliderComponent::new_capsule(*half_height, *radius),
176            SceneCollider::TriMesh { vertices, indices } => ColliderComponent {
177                shape: ColliderShape::TriMesh {
178                    vertices: vertices.clone(),
179                    indices: indices.clone(),
180                },
181                ..Default::default()
182            },
183            SceneCollider::ConvexHull { points } => ColliderComponent {
184                shape: ColliderShape::ConvexMesh {
185                    vertices: points.clone(),
186                },
187                ..Default::default()
188            },
189            SceneCollider::Cone {
190                half_height,
191                radius,
192            } => ColliderComponent {
193                shape: ColliderShape::Cone {
194                    half_height: *half_height,
195                    radius: *radius,
196                },
197                ..Default::default()
198            },
199            SceneCollider::HeightField {
200                nrows,
201                ncols,
202                heights,
203                scale,
204            } => ColliderComponent {
205                shape: ColliderShape::HeightField {
206                    nrows: *nrows,
207                    ncols: *ncols,
208                    heights: heights.clone(),
209                    scale: *scale,
210                },
211                ..Default::default()
212            },
213        };
214        collider = collider
215            .with_friction(self.friction)
216            .with_restitution(self.restitution);
217        collider.is_sensor = self.is_sensor;
218        collider.collision_groups =
219            InteractionGroups::new(self.collision_membership, self.collision_filter);
220        collider.solver_groups = InteractionGroups::new(self.solver_membership, self.solver_filter);
221        collider
222    }
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub enum SceneJoint {
227    Fixed {
228        parent_anchor: [f32; 3],
229        child_anchor: [f32; 3],
230    },
231    Revolute {
232        parent_anchor: [f32; 3],
233        child_anchor: [f32; 3],
234        axis: [f32; 3],
235        #[serde(default)]
236        limits: Option<[f32; 2]>,
237    },
238    Prismatic {
239        parent_anchor: [f32; 3],
240        child_anchor: [f32; 3],
241        axis: [f32; 3],
242        #[serde(default)]
243        limits: Option<[f32; 2]>,
244    },
245    Spherical {
246        parent_anchor: [f32; 3],
247        child_anchor: [f32; 3],
248    },
249    Rope {
250        parent_anchor: [f32; 3],
251        child_anchor: [f32; 3],
252        max_distance: f32,
253    },
254    Spring {
255        parent_anchor: [f32; 3],
256        child_anchor: [f32; 3],
257        rest_length: f32,
258        stiffness: f32,
259        damping: f32,
260    },
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct SceneJointConnection {
265    pub parent_entity: AssetUuid,
266    pub child_entity: AssetUuid,
267    pub joint: SceneJoint,
268    #[serde(default)]
269    pub collisions_enabled: bool,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
273pub enum SceneCollider {
274    Cuboid {
275        half_extents: [f32; 3],
276    },
277    Ball {
278        radius: f32,
279    },
280    Cylinder {
281        half_height: f32,
282        radius: f32,
283    },
284    Capsule {
285        half_height: f32,
286        radius: f32,
287    },
288    TriMesh {
289        vertices: Vec<[f32; 3]>,
290        indices: Vec<[u32; 3]>,
291    },
292    ConvexHull {
293        points: Vec<[f32; 3]>,
294    },
295    Cone {
296        half_height: f32,
297        radius: f32,
298    },
299    HeightField {
300        nrows: usize,
301        ncols: usize,
302        heights: Vec<f32>,
303        scale: [f32; 3],
304    },
305}