1use crate::components::*;
8use crate::hierarchy;
9use crate::scene::Scene;
10use glam::{Mat4, Quat, Vec3};
11use kvas_ecs::{Entity, World};
12use rapier3d::prelude as r;
13use std::collections::HashMap;
14use std::sync::{Arc, Mutex};
15
16#[derive(Clone, Copy, Debug)]
18pub struct RayHit {
19 pub entity: Entity,
21 pub point: Vec3,
23 pub normal: Vec3,
25 pub distance: f32,
27}
28
29#[derive(Clone, Copy, Debug)]
32pub struct QueryOptions {
33 pub exclude: Option<Entity>,
35 pub include_triggers: bool,
37 pub only_triggers: bool,
40 pub memberships: u32,
42 pub filter: u32,
44}
45
46impl Default for QueryOptions {
47 fn default() -> Self {
48 Self { exclude: None, include_triggers: false, only_triggers: false, memberships: u32::MAX, filter: u32::MAX }
49 }
50}
51
52fn groups(memberships: u32, filter: u32) -> r::InteractionGroups {
53 r::InteractionGroups::new(r::Group::from_bits_retain(memberships), r::Group::from_bits_retain(filter), r::InteractionTestMode::And)
54}
55
56pub struct Physics {
60 world: r::PhysicsWorld,
61 bodies: HashMap<Entity, r::RigidBodyHandle>,
62 colliders: HashMap<Entity, r::ColliderHandle>,
63 joints: HashMap<Entity, r::ImpulseJointHandle>,
64 owners: HashMap<r::ColliderHandle, Entity>,
65 anchors: HashMap<Entity, r::RigidBodyHandle>,
66 shapes: HashMap<Entity, ShapeKey>,
67 joint_configs: HashMap<Entity, (u64, Option<r::RigidBodyHandle>)>,
68}
69
70fn pose_mat(m: Mat4) -> (Mat4, Vec3) {
71 let (scale, rot, pos) = m.to_scale_rotation_translation();
72 (Mat4::from_rotation_translation(rot, pos), scale)
73}
74
75fn pose(m: Mat4) -> r::Pose {
76 let (_, rot, pos) = m.to_scale_rotation_translation();
77 r::Pose::from_parts(pos, rot)
78}
79
80fn global(world: &World, e: Entity) -> Mat4 {
81 world.get::<GlobalTransform>(e).map_or_else(|| hierarchy::global_matrix(world, e), |transform| transform.0)
82}
83
84type ShapeKey = (ShapeKind, Vec3, Vec3, String);
86
87type Triangles = Arc<(Vec<Vec3>, Vec<[u32; 3]>)>;
88
89fn model_triangles(path: &str) -> Option<Triangles> {
91 static CACHE: Mutex<Option<HashMap<String, Option<Triangles>>>> = Mutex::new(None);
92 let mut cache = CACHE.lock().ok()?;
93 cache
94 .get_or_insert_with(HashMap::new)
95 .entry(path.to_string())
96 .or_insert_with(|| {
97 let assets = crate::meshmod::assets().unwrap_or(crate::assets::Assets::Dir(std::path::PathBuf::new()));
98 let mesh = crate::mesh::MeshData::load(&assets, path).ok()?;
99 let points: Vec<Vec3> = mesh.vertices.iter().map(|v| Vec3::from(v.pos)).collect();
100 let tris: Vec<[u32; 3]> = mesh
101 .indices
102 .chunks_exact(3)
103 .map(|t| [t[0], t[1], t[2]])
104 .filter(|t| t.iter().all(|&i| (i as usize) < points.len()) && t[0] != t[1] && t[1] != t[2] && t[0] != t[2])
105 .collect();
106 (!tris.is_empty()).then(|| Arc::new((points, tris)))
107 })
108 .clone()
109}
110
111fn shape_key(world: &World, e: Entity, c: &Collider, scale: Vec3) -> ShapeKey {
112 let mesh = match c.shape {
113 ShapeKind::Mesh | ShapeKind::Hull => world.get::<MeshRenderer>(e).map(|m| m.mesh.clone()).unwrap_or_default(),
114 _ => String::new(),
115 };
116 (c.shape, c.size, scale, mesh)
117}
118
119fn mesh_shape(c: &Collider, scale: Vec3, mesh: &str) -> Option<r::ColliderBuilder> {
121 let tris = model_triangles(mesh)?;
122 let s = c.size.abs() * scale.abs();
123 let points: Vec<Vec3> = tris.0.iter().map(|p| *p * s).collect();
124 if c.shape == ShapeKind::Hull {
125 return r::ColliderBuilder::convex_hull(&points);
126 }
127 let flags = r::TriMeshFlags::FIX_INTERNAL_EDGES | r::TriMeshFlags::MERGE_DUPLICATE_VERTICES;
128 r::ColliderBuilder::trimesh_with_flags(points.clone(), tris.1.clone(), flags)
129 .or_else(|_| r::ColliderBuilder::trimesh(points, tris.1.clone()))
130 .ok()
131}
132
133fn make_shape(c: &Collider, scale: Vec3, mesh: &str) -> r::ColliderBuilder {
134 let s = scale.abs();
135 let size = c.size.abs().max(Vec3::splat(0.001)) * s;
136 let radius = size.x.max(size.z) * 0.5;
137 match c.shape {
138 ShapeKind::Mesh | ShapeKind::Hull => {
139 mesh_shape(c, scale, mesh).unwrap_or_else(|| r::ColliderBuilder::cuboid(size.x * 0.5, size.y * 0.5, size.z * 0.5))
140 }
141 ShapeKind::Box => r::ColliderBuilder::cuboid(size.x * 0.5, size.y * 0.5, size.z * 0.5),
142 ShapeKind::Sphere => r::ColliderBuilder::ball(size.x.max(size.y).max(size.z) * 0.5),
143 ShapeKind::Capsule => r::ColliderBuilder::capsule_y((size.y * 0.5 - radius).max(0.0), radius),
144 ShapeKind::Cylinder => r::ColliderBuilder::cylinder(size.y * 0.5, radius),
145 }
146}
147
148impl Physics {
149 pub fn new() -> Self {
151 Self {
152 world: r::PhysicsWorld::default(),
153 bodies: HashMap::new(),
154 colliders: HashMap::new(),
155 joints: HashMap::new(),
156 owners: HashMap::new(),
157 anchors: HashMap::new(),
158 shapes: HashMap::new(),
159 joint_configs: HashMap::new(),
160 }
161 }
162
163 pub fn raycast(&self, origin: Vec3, dir: Vec3, max_distance: f32) -> Option<RayHit> {
166 self.raycast_excluding(origin, dir, max_distance, None)
167 }
168
169 pub fn raycast_excluding(&self, origin: Vec3, dir: Vec3, max_distance: f32, exclude: Option<Entity>) -> Option<RayHit> {
171 self.raycast_with_options(origin, dir, max_distance, QueryOptions { exclude, ..Default::default() })
172 }
173
174 pub fn raycast_with_options(&self, origin: Vec3, dir: Vec3, max_distance: f32, options: QueryOptions) -> Option<RayHit> {
177 if !origin.is_finite() || !dir.is_finite() || !max_distance.is_finite() || max_distance < 0.0 {
178 return None;
179 }
180 let dir = crate::spatial::Ray::new(origin, dir)?.direction();
181 let ray = r::Ray::new(origin, dir);
182 let filter = self.query_filter(options);
183 let (handle, hit) = self.world.cast_ray_and_get_normal(&ray, max_distance, true, filter)?;
184 Some(RayHit {
185 entity: *self.owners.get(&handle)?,
186 point: origin + dir * hit.time_of_impact,
187 normal: hit.normal,
188 distance: hit.time_of_impact,
189 })
190 }
191
192 fn query_filter(&self, options: QueryOptions) -> r::QueryFilter<'_> {
193 let mut filter = r::QueryFilter::default().groups(groups(options.memberships, options.filter));
194 if options.only_triggers {
195 filter = filter.exclude_solids();
196 } else if !options.include_triggers {
197 filter = filter.exclude_sensors();
198 }
199 if let Some(handle) = options.exclude.and_then(|e| self.colliders.get(&e)) {
200 filter = filter.exclude_collider(*handle);
201 }
202 if let Some(handle) = options.exclude.and_then(|e| self.bodies.get(&e)) {
203 filter = filter.exclude_rigid_body(*handle);
204 }
205 filter
206 }
207
208 pub fn reset(&mut self) {
210 *self = Self::new();
211 }
212
213 pub fn body_count(&self) -> usize {
215 self.bodies.len()
216 }
217 pub fn collider_count(&self) -> usize {
219 self.colliders.len()
220 }
221 pub fn joint_count(&self) -> usize {
223 self.joints.len()
224 }
225
226 pub fn overlap_sphere(&self, center: Vec3, radius: f32) -> Vec<Entity> {
229 self.overlap_sphere_with_options(center, radius, QueryOptions { include_triggers: true, ..Default::default() })
230 }
231
232 pub fn overlap_sphere_with_options(&self, center: Vec3, radius: f32, options: QueryOptions) -> Vec<Entity> {
235 if !center.is_finite() || !radius.is_finite() || radius <= 0.0 {
236 return Vec::new();
237 }
238 let shape = r::Ball::new(radius);
239 let pose = r::Pose::from_translation(center);
240 let mut entities: Vec<_> = self
241 .world
242 .intersect_shape(pose, &shape, self.query_filter(options))
243 .filter_map(|(handle, _)| self.owners.get(&handle).copied())
244 .collect();
245 entities.sort();
246 entities.dedup();
247 entities
248 }
249
250 pub fn touching(&self, entity: Entity) -> Vec<Entity> {
254 let Some(&handle) = self.colliders.get(&entity) else {
255 return Vec::new();
256 };
257 let contacts = self.world.contact_pairs_with(handle).filter(|p| p.has_any_active_contact()).map(|p| {
258 if p.collider1 == handle {
259 p.collider2
260 } else {
261 p.collider1
262 }
263 });
264 let sensors = self.world.intersection_pairs_with(handle).filter(|p| p.4).map(|(a, _, b, _, _)| if a == handle { b } else { a });
265 let mut entities: Vec<_> = contacts.chain(sensors).filter_map(|h| self.owners.get(&h).copied()).collect();
266 entities.sort();
267 entities.dedup();
268 entities
269 }
270
271 fn body_owner(&self, world: &World, mut e: Entity) -> Option<Entity> {
273 let mut seen = std::collections::HashSet::new();
274 loop {
275 if !seen.insert(e) {
276 return None;
277 }
278 if self.bodies.contains_key(&e) {
279 return Some(e);
280 }
281 e = hierarchy::parent_of(world, e)?;
282 }
283 }
284
285 fn remove_joint(&mut self, entity: Entity) {
286 if let Some(handle) = self.joints.remove(&entity) {
287 self.world.remove_impulse_joint(handle);
288 }
289 if let Some(anchor) = self.anchors.remove(&entity) {
290 self.world.remove_body(anchor);
291 }
292 self.joint_configs.remove(&entity);
293 }
294
295 fn reconcile(&mut self, world: &World) {
297 let gone: Vec<_> = self
298 .joints
299 .iter()
300 .filter(|(e, h)| !world.has::<Joint>(**e) || !world.has::<RigidBody>(**e) || self.world.impulse_joints.get(**h).is_none())
301 .map(|(&e, _)| e)
302 .collect();
303 for e in gone {
304 self.remove_joint(e);
305 }
306 let gone: Vec<_> = self.bodies.keys().copied().filter(|&e| !world.has::<RigidBody>(e)).collect();
307 for e in gone {
308 if let Some(h) = self.bodies.remove(&e) {
309 self.world.remove_body(h);
310 }
311 }
312 let gone: Vec<_> = self
313 .colliders
314 .iter()
315 .filter(|(e, h)| !world.has::<Collider>(**e) || self.world.colliders.get(**h).is_none())
316 .map(|(&e, _)| e)
317 .collect();
318 for e in gone {
319 if let Some(h) = self.colliders.remove(&e) {
320 self.owners.remove(&h);
321 self.world.remove_collider(h);
322 }
323 self.shapes.remove(&e);
324 }
325
326 for (e, rb) in world.iter::<RigidBody>() {
327 if self.bodies.contains_key(&e) {
328 continue;
329 }
330 let b = match rb.kind {
331 BodyKind::Dynamic => r::RigidBodyBuilder::dynamic(),
332 BodyKind::Fixed => r::RigidBodyBuilder::fixed(),
333 BodyKind::Kinematic => r::RigidBodyBuilder::kinematic_position_based(),
334 }
335 .pose(pose(global(world, e)))
336 .gravity_scale(rb.gravity_scale)
337 .linear_damping(rb.linear_damping)
338 .angular_damping(rb.angular_damping)
339 .ccd_enabled(rb.ccd)
340 .additional_mass(rb.mass.max(0.001));
341 let b = if rb.lock_rotation { b.lock_rotations() } else { b };
342 self.bodies.insert(e, self.world.insert_body(b));
343 }
344
345 for (e, c) in world.iter::<Collider>() {
346 if self.colliders.contains_key(&e) {
347 let handle = self.colliders[&e];
348 let owner = self.body_owner(world, e);
349 let parent = owner.and_then(|p| self.bodies.get(&p).copied());
350 let old_parent = self.world.colliders.get(handle).and_then(|c| c.parent());
351 if old_parent != parent {
352 self.world.colliders.set_parent(handle, parent, &mut self.world.bodies);
353 }
354 let g = global(world, e);
355 let (_, scale) = pose_mat(g);
356 let shape_key = shape_key(world, e, c, scale);
357 let shape_changed = self.shapes.get(&e) != Some(&shape_key);
358 if let Some(collider) = self.world.colliders.get_mut(handle) {
359 if shape_changed {
360 collider.set_shape(make_shape(c, scale, &shape_key.3).build().shared_shape().clone());
361 }
362 collider.set_friction(c.friction);
363 collider.set_restitution(c.restitution);
364 collider.set_sensor(c.is_trigger);
365 collider.set_collision_groups(groups(c.memberships, c.filter));
366 if let Some(owner) = owner {
367 let (owner_pose, _) = pose_mat(global(world, owner));
368 collider.set_position_wrt_parent(pose(owner_pose.inverse() * g));
369 } else {
370 collider.set_position(pose(g));
371 }
372 }
373 self.shapes.insert(e, shape_key);
374 continue;
375 }
376 let g = global(world, e);
377 let (_, scale) = pose_mat(g);
378 let key = shape_key(world, e, c, scale);
379 let builder = make_shape(c, scale, &key.3)
380 .friction(c.friction)
381 .restitution(c.restitution)
382 .sensor(c.is_trigger)
383 .collision_groups(groups(c.memberships, c.filter))
384 .density(0.0);
385 let handle = match self.body_owner(world, e) {
386 Some(owner) => {
387 let (owner_pose, _) = pose_mat(global(world, owner));
388 let (_, rot, pos) = (owner_pose.inverse() * g).to_scale_rotation_translation();
389 let builder = builder.position(r::Pose::from_parts(pos, rot));
390 self.world.insert_collider(builder, Some(self.bodies[&owner]))
391 }
392 None => self.world.insert_collider(builder.position(pose(g)), None),
393 };
394 self.colliders.insert(e, handle);
395 self.owners.insert(handle, e);
396 self.shapes.insert(e, key);
397 }
398
399 let changed: Vec<_> = self
402 .joints
403 .iter()
404 .filter_map(|(&entity, &handle)| {
405 let joint = world.get::<Joint>(entity)?;
406 let target = if joint.target.is_empty() {
407 None
408 } else {
409 hierarchy::find_by_name(world, &joint.target).and_then(|e| self.bodies.get(&e).copied())
410 };
411 let key = (crate::procedural::signature(joint), target);
412 (self.world.impulse_joints.get(handle).is_none() || self.joint_configs.get(&entity) != Some(&key)).then_some(entity)
413 })
414 .collect();
415 for entity in changed {
416 self.remove_joint(entity);
417 }
418
419 for (e, j) in world.iter::<Joint>() {
420 if self.joints.contains_key(&e) {
421 continue;
422 }
423 let Some(&b1) = self.bodies.get(&e) else { continue };
424 let (target_body, target_mat) = if j.target.is_empty() {
425 (None, Mat4::IDENTITY)
426 } else {
427 match hierarchy::find_by_name(world, &j.target)
428 .and_then(|t| Some((self.bodies.get(&t).copied()?, pose_mat(global(world, t)).0)))
429 {
430 Some((h, m)) => (Some(h), m),
431 None => continue, }
433 };
434 if target_body == Some(b1) {
435 continue;
436 } let (m1, scale) = pose_mat(global(world, e));
438 let pivot = j.anchor * scale;
440 let a1 = if j.kind == JointKind::Spring { Vec3::ZERO } else { pivot };
441 let a2 = target_mat.inverse().transform_point3(m1.transform_point3(pivot));
442 if let Some(joint) = build_joint(j, m1, target_mat, a1, a2) {
443 let b2 = target_body.unwrap_or_else(|| {
444 let anchor = self.world.insert_body(r::RigidBodyBuilder::fixed());
445 self.anchors.insert(e, anchor);
446 anchor
447 });
448 self.joints.insert(e, self.world.insert_impulse_joint(b1, b2, joint));
449 self.joint_configs.insert(e, (crate::procedural::signature(j), target_body));
450 }
451 }
452 }
453
454 pub fn step(&mut self, scene: &mut Scene, dt: f32) {
458 if !dt.is_finite() || dt <= 0.0 {
459 return;
460 }
461 hierarchy::update_globals(&mut scene.world);
464 self.reconcile(&scene.world);
465 self.world.gravity = scene.settings.gravity;
466 self.world.integration_parameters.dt = dt;
467
468 for (&e, &h) in &self.bodies {
469 let g = global(&scene.world, e);
470 let Some(rb) = scene.world.get_mut::<RigidBody>(e) else { continue };
471 let Some(body) = self.world.bodies.get_mut(h) else { continue };
472 let kind = match rb.kind {
473 BodyKind::Dynamic => r::RigidBodyType::Dynamic,
474 BodyKind::Fixed => r::RigidBodyType::Fixed,
475 BodyKind::Kinematic => r::RigidBodyType::KinematicPositionBased,
476 };
477 if body.body_type() != kind {
478 body.set_body_type(kind, true);
479 }
480 body.set_gravity_scale(rb.gravity_scale, false);
481 body.set_linear_damping(rb.linear_damping);
482 body.set_angular_damping(rb.angular_damping);
483 body.enable_ccd(rb.ccd);
484 if (body.mass() - rb.mass.max(0.001)).abs() > f32::EPSILON {
485 body.set_additional_mass(rb.mass.max(0.001), true);
486 }
487 body.lock_rotations(rb.lock_rotation, false);
488 if std::mem::take(&mut rb.teleport) {
489 body.set_position(pose(g), true);
490 }
491 match rb.kind {
492 BodyKind::Kinematic => body.set_next_kinematic_position(pose(g)),
493 BodyKind::Dynamic => {
494 if let Some(v) = rb.set_velocity.take() {
495 body.set_linvel(v, true);
496 }
497 if let Some(w) = rb.set_angular_velocity.take() {
498 body.set_angvel(w, true);
499 }
500 if rb.impulse != Vec3::ZERO {
501 body.apply_impulse(std::mem::take(&mut rb.impulse), true);
502 }
503 }
504 BodyKind::Fixed => body.set_position(pose(g), false),
505 }
506 }
507
508 self.world.step();
509
510 let mut dynamic: Vec<(Entity, r::RigidBodyHandle)> = self
512 .bodies
513 .iter()
514 .filter(|(e, _)| scene.world.get::<RigidBody>(**e).is_some_and(|rb| rb.kind == BodyKind::Dynamic))
515 .map(|(&e, &h)| (e, h))
516 .collect();
517 dynamic.sort_by_key(|&(e, _)| (hierarchy::ancestors_count(&scene.world, e), e));
518 for (e, h) in dynamic {
519 let Some(body) = self.world.bodies.get(h) else { continue };
520 let p = body.position();
521 let (_, scale) = pose_mat(global(&scene.world, e));
522 let new_global = Mat4::from_scale_rotation_translation(scale, p.rotation, p.translation);
523 let parent_m = hierarchy::parent_of(&scene.world, e).map_or(Mat4::IDENTITY, |pa| hierarchy::global_matrix(&scene.world, pa));
524 if let Some(t) = scene.world.get_mut::<Transform>(e) {
525 *t = Transform::from_matrix(parent_m.inverse() * new_global);
526 }
527 if let Some(rb) = scene.world.get_mut::<RigidBody>(e) {
528 rb.velocity = body.linvel();
529 }
530 }
531 hierarchy::update_globals(&mut scene.world);
532 }
533}
534
535impl Default for Physics {
536 fn default() -> Self {
537 Self::new()
538 }
539}
540
541fn build_joint(j: &Joint, m1: Mat4, m2: Mat4, a1: Vec3, a2: Vec3) -> Option<r::GenericJoint> {
542 Some(match j.kind {
543 JointKind::Fixed => {
544 let frame = m1.inverse() * m2 * Mat4::from_translation(a2);
546 let (_, rot, pos) = frame.to_scale_rotation_translation();
547 r::FixedJointBuilder::new().local_frame1(r::Pose::from_parts(pos, rot)).local_anchor2(a2).build().into()
548 }
549 JointKind::Ball => r::SphericalJointBuilder::new().local_anchor1(a1).local_anchor2(a2).build().into(),
550 JointKind::Hinge => {
551 let axis = j.axis.try_normalize()?;
552 let axis2 = Quat::from_mat4(&m2).inverse() * (Quat::from_mat4(&m1) * axis);
553 let mut b = r::RevoluteJointBuilder::new(axis).local_anchor1(a1).local_anchor2(a2);
554 if j.use_limits {
555 b = b.limits([j.limit_min.to_radians(), j.limit_max.to_radians()]);
556 }
557 let mut joint: r::GenericJoint = b.build().into();
558 joint.set_local_axis2(axis2);
559 joint
560 }
561 JointKind::Spring => {
562 r::SpringJointBuilder::new(j.rest_length, j.stiffness, j.damping).local_anchor1(a1).local_anchor2(a2).build().into()
563 }
564 })
565}