pub mod debug_ops; pub mod debug_render; pub mod rb_entity; use fxhash::FxHashMap; use glam::Vec2; use rapier2d::{dynamics, geometry, glamx::vec2, prelude}; use crate::{ config::{CHUNK_SIZE, PHYSICS_DELTA_TIME, PIXELS_TO_METRES}, sim::{ cell::cell::Cell, cell_sim::chunk::Chunk, lib::marching_squares::{Marchable, marching_squares_vertex_trace}, rb_sim::{ debug_render::{DebugLineBuffer, DebugVertex}, rb_entity::RbEntity, }, }, }; pub use rapier2d::pipeline::DebugRenderMode; pub struct PhysicsManager { rigid_body_set: prelude::RigidBodySet, collider_set: prelude::ColliderSet, physics_pipeline: prelude::PhysicsPipeline, integration_parameters: prelude::IntegrationParameters, island_manager: prelude::IslandManager, broad_phase: prelude::DefaultBroadPhase, narrow_phase: prelude::NarrowPhase, impulse_joint_set: prelude::ImpulseJointSet, multibody_joint_set: prelude::MultibodyJointSet, ccd_solver: prelude::CCDSolver, debug_render_pipeline: prelude::DebugRenderPipeline, } impl PhysicsManager { pub fn new() -> Self { PhysicsManager { rigid_body_set: prelude::RigidBodySet::new(), collider_set: prelude::ColliderSet::new(), physics_pipeline: prelude::PhysicsPipeline::new(), integration_parameters: prelude::IntegrationParameters { // 20 pixels <-> 1 meter length_unit: PIXELS_TO_METRES, dt: PHYSICS_DELTA_TIME, ..prelude::IntegrationParameters::default() }, island_manager: prelude::IslandManager::new(), broad_phase: prelude::DefaultBroadPhase::new(), narrow_phase: prelude::NarrowPhase::new(), impulse_joint_set: prelude::ImpulseJointSet::new(), multibody_joint_set: prelude::MultibodyJointSet::new(), ccd_solver: prelude::CCDSolver::new(), debug_render_pipeline: prelude::DebugRenderPipeline::new( prelude::DebugRenderStyle { sleep_color_multiplier: [1.0; 4], sleep_eligible_color_multiplier: [1.0; 4], ..prelude::DebugRenderStyle::default() }, prelude::DebugRenderMode::default(), ), } } } pub struct RbSimManager { chunk_colliders: FxHashMap<(i32, i32), geometry::ColliderHandle>, pub rb_entities: FxHashMap, physics_manager: PhysicsManager, next_id: u32, debug_line_buffer: DebugLineBuffer, } impl RbSimManager { pub fn rb_tick(&mut self, delta_time: f32) { let gravity = vec2(0.0, 9.81); self.physics_manager.physics_pipeline.step( gravity, &self.physics_manager.integration_parameters, &mut self.physics_manager.island_manager, &mut self.physics_manager.broad_phase, &mut self.physics_manager.narrow_phase, &mut self.physics_manager.rigid_body_set, &mut self.physics_manager.collider_set, &mut self.physics_manager.impulse_joint_set, &mut self.physics_manager.multibody_joint_set, &mut self.physics_manager.ccd_solver, &(), &(), ); } pub fn debug_render(&mut self, mode: DebugRenderMode) -> &[DebugVertex] { puffin::profile_function!(); let physics = &mut self.physics_manager; self.debug_line_buffer.vertices.clear(); physics.debug_render_pipeline.mode = mode; physics.debug_render_pipeline.render( &mut self.debug_line_buffer, &physics.rigid_body_set, &physics.collider_set, &physics.impulse_joint_set, &physics.multibody_joint_set, &physics.narrow_phase, ); &self.debug_line_buffer.vertices } pub fn create_rb_entity(&mut self, position: Vec2, cells: Vec, w: i32, h: i32) -> u32 { let id = self.next_id; self.next_id += 1; let rb = dynamics::RigidBodyBuilder::dynamic() .translation(position / PIXELS_TO_METRES) .build(); let rb_handle = self.physics_manager.rigid_body_set.insert(rb); let entity: RbEntity = RbEntity { id, cells, rb_parent: rb_handle, width: w, height: h, }; let collider = self.convex_hull_collider_from_marchable(&entity).build(); self.rb_entities.insert(id, entity); self.physics_manager.collider_set.insert_with_parent( collider, rb_handle, &mut self.physics_manager.rigid_body_set, ); id } pub fn destroy_rb_entity(&mut self, entity_id: u32) { if let Some(entity) = self.rb_entities.get(&entity_id) { self.physics_manager.rigid_body_set.remove( entity.rb_parent, &mut self.physics_manager.island_manager, &mut self.physics_manager.collider_set, &mut self.physics_manager.impulse_joint_set, &mut self.physics_manager.multibody_joint_set, true, ); self.rb_entities.remove(&entity_id); } } pub fn get_rb_entity_transform(&self, entity_id: u32) -> Option<(f32, f32, f32, f32)> { let entity = self.rb_entities.get(&entity_id); match entity { Some(entity) => { let rb = self.physics_manager.rigid_body_set.get(entity.rb_parent); rb.map(|rb| { let position = rb.position(); let angle = position.rotation.angle(); ( position.translation.x * PIXELS_TO_METRES, position.translation.y * PIXELS_TO_METRES, angle.cos(), angle.sin(), ) }) } None => return None, } } fn polyline_from_marchable(&self, marchable: &impl Marchable) -> (Vec, Vec<[u32; 2]>) { let (w, h) = marchable.size(); let polys = marching_squares_vertex_trace(marchable, w, h); let mut vertices = Vec::new(); let mut indices = Vec::new(); for poly in polys { let v = vertices.len() as u32; let p = poly.len() as u32; if p < 3 { continue; } for i in 0..p { vertices.push( (poly[i as usize] - vec2((w as f32 - 1.0) / 2.0, (h as f32 - 1.0) / 2.0)) / PIXELS_TO_METRES, ); indices.push([v + i, v + (i + 1) % p]); } } (vertices, indices) } fn polyline_collider_from_marchable( &self, marchable: &impl Marchable, ) -> geometry::ColliderBuilder { let (vertices, indices) = self.polyline_from_marchable(marchable); geometry::ColliderBuilder::polyline(vertices, Some(indices)) } fn convex_hull_collider_from_marchable( &self, marchable: &impl Marchable, ) -> geometry::ColliderBuilder { let (vertices, indices) = self.polyline_from_marchable(marchable); geometry::ColliderBuilder::convex_decomposition(&vertices, &indices) } pub fn upsert_chunk_collider(&mut self, cx: i32, cy: i32, chunk: &Chunk) { let collider = self.polyline_collider_from_marchable(chunk).translation( vec2( (cx as f32 + 0.5) * CHUNK_SIZE as f32, (cy as f32 + 0.5) * CHUNK_SIZE as f32, ) / PIXELS_TO_METRES, ); if let Some(handle) = self.chunk_colliders.remove(&(cx, cy)) { self.physics_manager.collider_set.remove( handle, &mut self.physics_manager.island_manager, &mut self.physics_manager.rigid_body_set, false, ); } let handle = self.physics_manager.collider_set.insert(collider); self.chunk_colliders.insert((cx, cy), handle); } pub fn new() -> Self { RbSimManager { rb_entities: FxHashMap::default(), chunk_colliders: FxHashMap::default(), physics_manager: PhysicsManager::new(), next_id: 0, debug_line_buffer: DebugLineBuffer::default(), } } }