1use nalgebra_glm::{Quat, Vec3};
4use serde::{Deserialize, Serialize};
5
6use super::types::{ColliderHandle, InteractionGroups, LockedAxes, RigidBodyHandle, RigidBodyType};
7
8#[derive(Debug, Clone, Serialize, Deserialize, enum2schema::Schema)]
14pub struct RigidBodyComponent {
15 #[serde(skip)]
17 #[schema(skip)]
18 pub handle: Option<RigidBodyHandle>,
19 pub body_type: RigidBodyType,
21 pub translation: [f32; 3],
23 pub rotation: [f32; 4],
25 pub linvel: [f32; 3],
27 pub angvel: [f32; 3],
29 pub mass: f32,
31 #[serde(default)]
34 pub linear_damping: f32,
35 #[serde(default)]
38 pub angular_damping: f32,
39 #[serde(default = "default_gravity_scale")]
42 pub gravity_scale: f32,
43 pub locked_axes: LockedAxes,
45 #[serde(default)]
48 pub ccd_enabled: bool,
49}
50
51fn default_gravity_scale() -> f32 {
52 1.0
53}
54
55impl Default for RigidBodyComponent {
56 fn default() -> Self {
57 Self {
58 handle: None,
59 body_type: RigidBodyType::default(),
60 translation: [0.0, 0.0, 0.0],
61 rotation: [0.0, 0.0, 0.0, 1.0],
62 linvel: [0.0, 0.0, 0.0],
63 angvel: [0.0, 0.0, 0.0],
64 mass: 1.0,
65 linear_damping: 0.0,
66 angular_damping: 0.0,
67 gravity_scale: 1.0,
68 locked_axes: LockedAxes::default(),
69 ccd_enabled: false,
70 }
71 }
72}
73
74impl RigidBodyComponent {
75 pub fn new_dynamic() -> Self {
77 Self {
78 body_type: RigidBodyType::Dynamic,
79 ..Default::default()
80 }
81 }
82
83 pub fn new_kinematic() -> Self {
85 Self {
86 body_type: RigidBodyType::KinematicPositionBased,
87 ..Default::default()
88 }
89 }
90
91 pub fn new_kinematic_velocity() -> Self {
95 Self {
96 body_type: RigidBodyType::KinematicVelocityBased,
97 ..Default::default()
98 }
99 }
100
101 pub fn new_static() -> Self {
103 Self {
104 body_type: RigidBodyType::Fixed,
105 ..Default::default()
106 }
107 }
108
109 pub fn with_translation(mut self, x: f32, y: f32, z: f32) -> Self {
111 self.translation = [x, y, z];
112 self
113 }
114
115 pub fn with_mass(mut self, mass: f32) -> Self {
117 self.mass = mass;
118 self
119 }
120
121 pub fn with_linear_damping(mut self, damping: f32) -> Self {
123 self.linear_damping = damping;
124 self
125 }
126
127 pub fn with_angular_damping(mut self, damping: f32) -> Self {
129 self.angular_damping = damping;
130 self
131 }
132
133 pub fn with_gravity_scale(mut self, scale: f32) -> Self {
135 self.gravity_scale = scale;
136 self
137 }
138
139 pub fn with_rotation(mut self, x: f32, y: f32, z: f32, w: f32) -> Self {
141 self.rotation = [x, y, z, w];
142 self
143 }
144
145 pub fn with_ccd(mut self, enabled: bool) -> Self {
148 self.ccd_enabled = enabled;
149 self
150 }
151
152 #[cfg(feature = "physics")]
154 pub(crate) fn to_rapier_rigid_body(&self) -> rapier3d::prelude::RigidBody {
155 use rapier3d::prelude::*;
156
157 let translation = vector![
158 self.translation[0],
159 self.translation[1],
160 self.translation[2]
161 ];
162 let rotation =
163 rapier3d::na::UnitQuaternion::from_quaternion(rapier3d::na::Quaternion::new(
164 self.rotation[3],
165 self.rotation[0],
166 self.rotation[1],
167 self.rotation[2],
168 ));
169 let position = rapier3d::na::Isometry3::from_parts(translation.into(), rotation);
170
171 let rapier_body_type = super::types::rigid_body_type_to_rapier(self.body_type);
172 let rapier_locked_axes = super::types::locked_axes_to_rapier(self.locked_axes);
173
174 let mut rb = RigidBodyBuilder::new(rapier_body_type)
175 .pose(position)
176 .linvel(vector![self.linvel[0], self.linvel[1], self.linvel[2]])
177 .angvel(vector![self.angvel[0], self.angvel[1], self.angvel[2]])
178 .linear_damping(self.linear_damping)
179 .angular_damping(self.angular_damping)
180 .gravity_scale(self.gravity_scale)
181 .locked_axes(rapier_locked_axes)
182 .ccd_enabled(self.ccd_enabled)
183 .build();
184
185 if self.body_type == super::types::RigidBodyType::Dynamic {
186 rb.set_additional_mass(self.mass, true);
187 }
188
189 rb
190 }
191}
192
193use super::types::CharacterControllerConfig;
194
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, enum2schema::Schema)]
201pub struct CharacterControllerComponent {
202 pub config: CharacterControllerConfig,
204 pub grounded: bool,
206 pub velocity: Vec3,
208 pub shape: ColliderShape,
210 pub max_speed: f32,
212 pub acceleration: f32,
214 pub jump_impulse: f32,
216 pub can_jump: bool,
218 pub is_crouching: bool,
220 pub is_sprinting: bool,
222 pub crouch_enabled: bool,
224 pub crouch_speed_multiplier: f32,
226 pub sprint_speed_multiplier: f32,
228 pub standing_half_height: f32,
230 pub crouching_half_height: f32,
232 pub crouch_input_was_pressed: bool,
234 pub sprint_input_was_pressed: bool,
236 pub jump_input_was_pressed: bool,
238 pub scale: f32,
240 pub friction_rate: f32,
243 pub above_max_friction_rate: f32,
246 pub engine_input_enabled: bool,
250 pub disable_gravity: bool,
255 pub external_movement: nalgebra_glm::Vec2,
260 pub external_jump: bool,
263}
264
265impl Default for CharacterControllerComponent {
266 fn default() -> Self {
267 Self {
268 config: CharacterControllerConfig::default(),
269 grounded: false,
270 velocity: Vec3::zeros(),
271 shape: ColliderShape::Capsule {
272 half_height: 0.9,
273 radius: 0.3,
274 },
275 max_speed: 5.0,
276 acceleration: 50.0,
277 jump_impulse: 8.0,
278 can_jump: false,
279 is_crouching: false,
280 is_sprinting: false,
281 crouch_enabled: true,
282 crouch_speed_multiplier: 0.5,
283 sprint_speed_multiplier: 1.6,
284 standing_half_height: 0.9,
285 crouching_half_height: 0.45,
286 crouch_input_was_pressed: false,
287 sprint_input_was_pressed: false,
288 jump_input_was_pressed: false,
289 scale: 1.0,
290 friction_rate: 8.0,
291 above_max_friction_rate: 1.5,
292 engine_input_enabled: true,
293 disable_gravity: false,
294 external_movement: nalgebra_glm::Vec2::zeros(),
295 external_jump: false,
296 }
297 }
298}
299
300impl CharacterControllerComponent {
301 pub fn new_capsule(half_height: f32, radius: f32) -> Self {
303 let character_height = 2.0 * half_height + 2.0 * radius;
304 let scale = character_height / 2.4;
305
306 Self {
307 config: CharacterControllerConfig::default(),
308 grounded: false,
309 velocity: Vec3::zeros(),
310 shape: ColliderShape::Capsule {
311 half_height,
312 radius,
313 },
314 max_speed: 5.0,
315 acceleration: 50.0,
316 jump_impulse: 8.0,
317 can_jump: false,
318 is_crouching: false,
319 is_sprinting: false,
320 crouch_enabled: true,
321 crouch_speed_multiplier: 0.5,
322 sprint_speed_multiplier: 1.6,
323 standing_half_height: half_height,
324 crouching_half_height: half_height * 0.5,
325 crouch_input_was_pressed: false,
326 sprint_input_was_pressed: false,
327 jump_input_was_pressed: false,
328 scale,
329 friction_rate: 8.0,
330 above_max_friction_rate: 1.5,
331 engine_input_enabled: true,
332 disable_gravity: false,
333 external_movement: nalgebra_glm::Vec2::zeros(),
334 external_jump: false,
335 }
336 }
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize, enum2schema::Schema)]
345pub struct ColliderComponent {
346 #[serde(skip)]
348 #[schema(skip)]
349 pub handle: Option<ColliderHandle>,
350 pub shape: ColliderShape,
352 pub friction: f32,
354 pub restitution: f32,
356 pub density: f32,
358 pub is_sensor: bool,
360 pub collision_groups: InteractionGroups,
362 pub solver_groups: InteractionGroups,
364}
365
366impl Default for ColliderComponent {
367 fn default() -> Self {
368 Self {
369 handle: None,
370 shape: ColliderShape::Cuboid {
371 hx: 0.5,
372 hy: 0.5,
373 hz: 0.5,
374 },
375 friction: 0.5,
376 restitution: 0.0,
377 density: 1.0,
378 is_sensor: false,
379 collision_groups: InteractionGroups::all(),
380 solver_groups: InteractionGroups::all(),
381 }
382 }
383}
384
385#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, enum2schema::Schema)]
387pub enum ColliderShape {
388 Ball { radius: f32 },
390 Cuboid { hx: f32, hy: f32, hz: f32 },
392 Capsule { half_height: f32, radius: f32 },
394 Cylinder { half_height: f32, radius: f32 },
396 Cone { half_height: f32, radius: f32 },
398 ConvexMesh { vertices: Vec<[f32; 3]> },
400 TriMesh {
402 vertices: Vec<[f32; 3]>,
403 indices: Vec<[u32; 3]>,
404 },
405 HeightField {
407 nrows: usize,
408 ncols: usize,
409 heights: Vec<f32>,
410 scale: [f32; 3],
411 },
412}
413
414impl ColliderComponent {
415 pub fn new_ball(radius: f32) -> Self {
417 Self {
418 shape: ColliderShape::Ball { radius },
419 ..Default::default()
420 }
421 }
422
423 pub fn new_cuboid(hx: f32, hy: f32, hz: f32) -> Self {
425 Self {
426 shape: ColliderShape::Cuboid { hx, hy, hz },
427 ..Default::default()
428 }
429 }
430
431 pub fn new_capsule(half_height: f32, radius: f32) -> Self {
433 Self {
434 shape: ColliderShape::Capsule {
435 half_height,
436 radius,
437 },
438 ..Default::default()
439 }
440 }
441
442 pub fn new_cylinder(half_height: f32, radius: f32) -> Self {
444 Self {
445 shape: ColliderShape::Cylinder {
446 half_height,
447 radius,
448 },
449 ..Default::default()
450 }
451 }
452
453 pub fn new_cone(half_height: f32, radius: f32) -> Self {
455 Self {
456 shape: ColliderShape::Cone {
457 half_height,
458 radius,
459 },
460 ..Default::default()
461 }
462 }
463
464 pub fn with_friction(mut self, friction: f32) -> Self {
466 self.friction = friction;
467 self
468 }
469
470 pub fn with_restitution(mut self, restitution: f32) -> Self {
472 self.restitution = restitution;
473 self
474 }
475
476 pub fn with_density(mut self, density: f32) -> Self {
478 self.density = density;
479 self
480 }
481
482 pub fn as_sensor(mut self) -> Self {
484 self.is_sensor = true;
485 self
486 }
487
488 #[cfg(feature = "physics")]
490 pub(crate) fn to_rapier_collider(&self) -> rapier3d::prelude::Collider {
491 use rapier3d::prelude::{ColliderBuilder, DMatrix, Point, Real, SharedShape, vector};
492
493 let shape: SharedShape = match &self.shape {
494 ColliderShape::Ball { radius } => SharedShape::ball(*radius),
495 ColliderShape::Cuboid { hx, hy, hz } => SharedShape::cuboid(*hx, *hy, *hz),
496 ColliderShape::Capsule {
497 half_height,
498 radius,
499 } => SharedShape::capsule_y(*half_height, *radius),
500 ColliderShape::Cylinder {
501 half_height,
502 radius,
503 } => SharedShape::cylinder(*half_height, *radius),
504 ColliderShape::Cone {
505 half_height,
506 radius,
507 } => SharedShape::cone(*half_height, *radius),
508 ColliderShape::ConvexMesh { vertices } => {
509 let points: Vec<Point<Real>> = vertices
510 .iter()
511 .map(|v| Point::new(v[0], v[1], v[2]))
512 .collect();
513 SharedShape::convex_hull(&points).unwrap_or_else(|| SharedShape::ball(0.1))
514 }
515 ColliderShape::TriMesh { vertices, indices } => {
516 let points: Vec<Point<Real>> = vertices
517 .iter()
518 .map(|v| Point::new(v[0], v[1], v[2]))
519 .collect();
520 SharedShape::trimesh(points, indices.clone())
521 .unwrap_or_else(|_| SharedShape::ball(0.1))
522 }
523 ColliderShape::HeightField {
524 nrows,
525 ncols,
526 heights,
527 scale,
528 } => SharedShape::heightfield(
529 DMatrix::from_vec(*nrows, *ncols, heights.clone()),
530 vector![scale[0], scale[1], scale[2]],
531 ),
532 };
533
534 let rapier_collision_groups =
535 super::types::interaction_groups_to_rapier(self.collision_groups);
536 let rapier_solver_groups = super::types::interaction_groups_to_rapier(self.solver_groups);
537
538 ColliderBuilder::new(shape)
539 .friction(self.friction)
540 .restitution(self.restitution)
541 .density(self.density)
542 .sensor(self.is_sensor)
543 .collision_groups(rapier_collision_groups)
544 .solver_groups(rapier_solver_groups)
545 .build()
546 }
547}
548
549#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
550pub struct CollisionListener;
551
552#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
558pub struct PhysicsInterpolation {
559 pub previous_translation: Vec3,
561 pub previous_rotation: Quat,
563 pub current_translation: Vec3,
565 pub current_rotation: Quat,
567 pub enabled: bool,
569}
570
571impl PhysicsInterpolation {
572 pub fn new() -> Self {
574 Self {
575 previous_translation: Vec3::zeros(),
576 previous_rotation: Quat::identity(),
577 current_translation: Vec3::zeros(),
578 current_rotation: Quat::identity(),
579 enabled: true,
580 }
581 }
582
583 pub fn interpolate(&self, alpha: f32) -> (Vec3, Quat) {
585 if !self.enabled {
586 return (self.current_translation, self.current_rotation);
587 }
588
589 let translation =
590 nalgebra_glm::lerp(&self.previous_translation, &self.current_translation, alpha);
591 let rotation =
592 nalgebra_glm::quat_slerp(&self.previous_rotation, &self.current_rotation, alpha);
593 (translation, rotation)
594 }
595
596 pub fn update_previous(&mut self) {
598 self.previous_translation = self.current_translation;
599 self.previous_rotation = self.current_rotation;
600 }
601
602 pub fn update_current(&mut self, translation: Vec3, rotation: Quat) {
604 self.current_translation = translation;
605 self.current_rotation = rotation;
606 }
607}