khora_infra/physics/rapier/
mod.rs1mod conversions;
18mod debug;
19mod events;
20
21use khora_core::math::{Quat, Vec3};
22use khora_core::physics::{
23 BodyType, CharacterControllerOptions, ColliderDesc, ColliderHandle, ColliderShape,
24 CollisionEvent, PhysicsProvider, Ray, RaycastHit, RigidBodyDesc, RigidBodyHandle,
25};
26use rapier3d::control::*;
27use rapier3d::prelude::*;
28use std::sync::{Arc, Mutex};
29
30use conversions::*;
31use debug::*;
32use events::*;
33
34pub struct RapierPhysicsWorld {
36 rigid_body_set: RigidBodySet,
37 collider_set: ColliderSet,
38 gravity: Vector,
39 integration_parameters: IntegrationParameters,
40 physics_pipeline: PhysicsPipeline,
41 island_manager: IslandManager,
42 broad_phase: BroadPhaseBvh,
43 narrow_phase: NarrowPhase,
44 impulse_joint_set: ImpulseJointSet,
45 multibody_joint_set: MultibodyJointSet,
46 ccd_solver: CCDSolver,
47 events: Arc<Mutex<Vec<CollisionEvent>>>,
48}
49
50impl Default for RapierPhysicsWorld {
51 fn default() -> Self {
52 Self {
53 rigid_body_set: RigidBodySet::new(),
54 collider_set: ColliderSet::new(),
55 gravity: Vector::new(0.0, -9.81, 0.0),
56 integration_parameters: IntegrationParameters::default(),
57 physics_pipeline: PhysicsPipeline::new(),
58 island_manager: IslandManager::new(),
59 broad_phase: BroadPhaseBvh::new(),
60 narrow_phase: NarrowPhase::new(),
61 impulse_joint_set: ImpulseJointSet::new(),
62 multibody_joint_set: MultibodyJointSet::new(),
63 ccd_solver: CCDSolver::new(),
64 events: Arc::new(Mutex::new(Vec::new())),
65 }
66 }
67}
68
69impl PhysicsProvider for RapierPhysicsWorld {
70 fn step(&mut self, dt: f32) {
71 self.integration_parameters.dt = dt;
72 let event_handler = RapierEventHandler {
73 events: self.events.clone(),
74 };
75
76 self.physics_pipeline.step(
77 self.gravity,
78 &self.integration_parameters,
79 &mut self.island_manager,
80 &mut self.broad_phase,
81 &mut self.narrow_phase,
82 &mut self.rigid_body_set,
83 &mut self.collider_set,
84 &mut self.impulse_joint_set,
85 &mut self.multibody_joint_set,
86 &mut self.ccd_solver,
87 &(),
88 &event_handler,
89 );
90 }
91
92 fn set_gravity(&mut self, gravity: Vec3) {
93 self.gravity = to_rapier_vec(gravity);
94 }
95
96 fn add_body(&mut self, desc: RigidBodyDesc) -> RigidBodyHandle {
97 let rb_type = match desc.body_type {
98 BodyType::Dynamic => RigidBodyType::Dynamic,
99 BodyType::Static => RigidBodyType::Fixed,
100 BodyType::Kinematic => RigidBodyType::KinematicVelocityBased,
101 };
102
103 let rigid_body = RigidBodyBuilder::new(rb_type)
104 .translation(to_rapier_vec(desc.position))
105 .rotation(to_rapier_quat(desc.rotation).to_scaled_axis())
106 .linvel(to_rapier_vec(desc.linear_velocity))
107 .angvel(to_rapier_vec(desc.angular_velocity))
108 .additional_mass(desc.mass)
109 .ccd_enabled(desc.ccd_enabled)
110 .build();
111
112 let handle = self.rigid_body_set.insert(rigid_body);
113 RigidBodyHandle(handle.into_raw_parts().0 as u64)
114 }
115
116 fn remove_body(&mut self, handle: RigidBodyHandle) {
117 let rb_handle = to_rapier_rb_handle(handle);
118 self.rigid_body_set.remove(
119 rb_handle,
120 &mut self.island_manager,
121 &mut self.collider_set,
122 &mut self.impulse_joint_set,
123 &mut self.multibody_joint_set,
124 true,
125 );
126 }
127
128 fn add_collider(&mut self, desc: ColliderDesc) -> ColliderHandle {
129 let shape = match desc.shape {
130 ColliderShape::Box(half) => SharedShape::cuboid(half.x, half.y, half.z),
131 ColliderShape::Sphere(r) => SharedShape::ball(r),
132 ColliderShape::Capsule(h, r) => SharedShape::capsule_y(h, r),
133 };
134
135 let collider = ColliderBuilder::new(shape)
136 .translation(to_rapier_vec(desc.position))
137 .rotation(to_rapier_quat(desc.rotation).to_scaled_axis())
138 .active_events(if desc.active_events {
139 ActiveEvents::COLLISION_EVENTS
140 } else {
141 ActiveEvents::empty()
142 })
143 .friction(desc.friction)
144 .restitution(desc.restitution)
145 .build();
146
147 let handle = if let Some(parent_handle) = desc.parent_body {
148 let rb_handle = to_rapier_rb_handle(parent_handle);
149 self.collider_set
150 .insert_with_parent(collider, rb_handle, &mut self.rigid_body_set)
151 } else {
152 self.collider_set.insert(collider)
153 };
154
155 ColliderHandle(handle.into_raw_parts().0 as u64)
156 }
157
158 fn remove_collider(&mut self, handle: ColliderHandle) {
159 let cl_handle = to_rapier_cl_handle(handle);
160 self.collider_set.remove(
161 cl_handle,
162 &mut self.island_manager,
163 &mut self.rigid_body_set,
164 true,
165 );
166 }
167
168 fn get_body_transform(&self, handle: RigidBodyHandle) -> (Vec3, Quat) {
169 let rb_handle = to_rapier_rb_handle(handle);
170 if let Some(rb) = self.rigid_body_set.get(rb_handle) {
171 let t = rb.translation();
172 let r = rb.rotation();
173 (from_rapier_vec(t), from_rapier_quat(*r))
174 } else {
175 (Vec3::ZERO, Quat::IDENTITY)
176 }
177 }
178
179 fn set_body_transform(&mut self, handle: RigidBodyHandle, pos: Vec3, rot: Quat) {
180 let rb_handle = to_rapier_rb_handle(handle);
181 if let Some(rb) = self.rigid_body_set.get_mut(rb_handle) {
182 rb.set_translation(to_rapier_vec(pos), true);
183 rb.set_rotation(to_rapier_quat(rot), true);
184 }
185 }
186
187 fn get_all_bodies(&self) -> Vec<RigidBodyHandle> {
188 self.rigid_body_set
189 .iter()
190 .map(|(handle, _)| RigidBodyHandle(handle.into_raw_parts().0 as u64))
191 .collect()
192 }
193
194 fn get_all_colliders(&self) -> Vec<ColliderHandle> {
195 self.collider_set
196 .iter()
197 .map(|(handle, _)| ColliderHandle(handle.into_raw_parts().0 as u64))
198 .collect()
199 }
200
201 fn update_body_properties(&mut self, handle: RigidBodyHandle, desc: RigidBodyDesc) {
202 let rb_handle = to_rapier_rb_handle(handle);
203 if let Some(rb) = self.rigid_body_set.get_mut(rb_handle) {
204 let rb_type = match desc.body_type {
205 BodyType::Dynamic => RigidBodyType::Dynamic,
206 BodyType::Static => RigidBodyType::Fixed,
207 BodyType::Kinematic => RigidBodyType::KinematicVelocityBased,
208 };
209 rb.set_body_type(rb_type, true);
210 rb.set_additional_mass(desc.mass, true);
211 rb.set_linvel(to_rapier_vec(desc.linear_velocity), true);
212 rb.set_angvel(to_rapier_vec(desc.angular_velocity), true);
213 rb.enable_ccd(desc.ccd_enabled);
214 }
215 }
216
217 fn update_collider_properties(&mut self, handle: ColliderHandle, desc: ColliderDesc) {
218 let cl_handle = to_rapier_cl_handle(handle);
219 if let Some(cl) = self.collider_set.get_mut(cl_handle) {
220 cl.set_translation(to_rapier_vec(desc.position));
221 cl.set_rotation(to_rapier_quat(desc.rotation));
222 cl.set_active_events(if desc.active_events {
223 ActiveEvents::COLLISION_EVENTS
224 } else {
225 ActiveEvents::empty()
226 });
227 cl.set_friction(desc.friction);
228 cl.set_restitution(desc.restitution);
229 }
230 }
231
232 fn get_debug_render_data(&self) -> (Vec<Vec3>, Vec<[u32; 2]>) {
233 let mut backend = RapierDebugBackend::default();
234 let mut pipeline = DebugRenderPipeline::default();
235 pipeline.render(
236 &mut backend,
237 &self.rigid_body_set,
238 &self.collider_set,
239 &self.impulse_joint_set,
240 &self.multibody_joint_set,
241 &self.narrow_phase,
242 );
243 (backend.vertices, backend.indices)
244 }
245
246 fn cast_ray(&self, ray: &Ray, max_toi: f32, solid: bool) -> Option<RaycastHit> {
247 let rapier_ray =
248 rapier3d::geometry::Ray::new(to_rapier_vec(ray.origin), to_rapier_vec(ray.direction));
249
250 let query_pipeline = self.broad_phase.as_query_pipeline(
251 self.narrow_phase.query_dispatcher(),
252 &self.rigid_body_set,
253 &self.collider_set,
254 QueryFilter::default(),
255 );
256
257 let (handle, intersection) =
258 query_pipeline.cast_ray_and_get_normal(&rapier_ray, max_toi, solid)?;
259
260 let hit_pos = rapier_ray.point_at(intersection.time_of_impact);
261
262 Some(RaycastHit {
263 collider: ColliderHandle(handle.into_raw_parts().0 as u64),
264 distance: intersection.time_of_impact,
265 normal: from_rapier_vec(intersection.normal),
266 position: from_rapier_vec(hit_pos),
267 })
268 }
269
270 fn get_collision_events(&self) -> Vec<CollisionEvent> {
271 let mut events = self.events.lock().unwrap();
272 std::mem::take(&mut *events)
273 }
274
275 fn move_character(
276 &self,
277 collider: ColliderHandle,
278 desired_translation: Vec3,
279 options: &CharacterControllerOptions,
280 ) -> (Vec3, bool) {
281 let cl_handle = to_rapier_cl_handle(collider);
282 if let Some(cl) = self.collider_set.get(cl_handle) {
283 let kcc = KinematicCharacterController {
284 offset: CharacterLength::Absolute(options.offset),
285 max_slope_climb_angle: options.max_slope_climb_angle,
286 min_slope_slide_angle: options.min_slope_slide_angle,
287 autostep: if options.autostep_enabled {
288 Some(CharacterAutostep {
289 max_height: CharacterLength::Absolute(options.autostep_height),
290 min_width: CharacterLength::Absolute(options.autostep_min_width),
291 include_dynamic_bodies: true,
292 })
293 } else {
294 None
295 },
296 ..Default::default()
297 };
298
299 let query_pipeline = self.broad_phase.as_query_pipeline(
300 self.narrow_phase.query_dispatcher(),
301 &self.rigid_body_set,
302 &self.collider_set,
303 QueryFilter::default().exclude_collider(cl_handle),
304 );
305
306 let result = kcc.move_shape(
307 self.integration_parameters.dt,
308 &query_pipeline,
309 cl.shape(),
310 cl.position(),
311 to_rapier_vec(desired_translation),
312 |_| {},
313 );
314
315 (from_rapier_vec(result.translation), result.grounded)
316 } else {
317 (Vec3::ZERO, false)
318 }
319 }
320}
321
322fn to_rapier_rb_handle(handle: RigidBodyHandle) -> rapier3d::dynamics::RigidBodyHandle {
325 rapier3d::dynamics::RigidBodyHandle::from_raw_parts(handle.0 as u32, 0)
326}
327
328fn to_rapier_cl_handle(handle: ColliderHandle) -> rapier3d::geometry::ColliderHandle {
329 rapier3d::geometry::ColliderHandle::from_raw_parts(handle.0 as u32, 0)
330}