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}