pub mod debug_render; use fxhash::FxHashMap; use glam::Vec2; use rapier2d::{ dynamics::{CCDSolver, IntegrationParameters, RigidBody, RigidBodyHandle}, geometry, glamx::vec2, pipeline::{DebugRenderPipeline, PhysicsWorld}, prelude, }; use crate::{ config::{CELLS_TO_METRES, CHUNK_SIZE, GRAVITY, PHYSICS_DELTA_TIME}, sim::{ cell_manager::chunk::Chunk, lib::marching_squares::{Marchable, marching_squares_vertex_trace}, rb_manager::debug_render::{DebugLineBuffer, DebugVertex}, }, }; pub use rapier2d::pipeline::DebugRenderMode; pub struct PhysicsManager { pub world: PhysicsWorld, pub debug_render_pipeline: DebugRenderPipeline, } impl PhysicsManager { pub fn new() -> Self { let mut world = PhysicsWorld::new(); let gravity = vec2(0.0, GRAVITY); world.gravity = gravity; world.integration_parameters = IntegrationParameters { // 20 pixels <-> 1 meter length_unit: CELLS_TO_METRES, dt: PHYSICS_DELTA_TIME, ..prelude::IntegrationParameters::default() }; PhysicsManager { world, 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(), ), } } // Temporary fix for https://github.com/dimforge/rapier/pull/1003 pub fn remove_collider( &mut self, handle: geometry::ColliderHandle, ) -> Option { let removed = self.world.remove_collider(handle); self.ccd_cache_invalidated(); removed } // Temporary fix for https://github.com/dimforge/rapier/pull/1003 pub fn remove_body(&mut self, handle: RigidBodyHandle) -> Option { let removed = self.world.remove_body(handle); self.ccd_cache_invalidated(); removed } // Temporary fix for https://github.com/dimforge/rapier/pull/1003 fn ccd_cache_invalidated(&mut self) { self.world.ccd_solver = CCDSolver::new(); } } // TODO remove pub struct RbManager { // move to sim manager chunk_colliders: FxHashMap<(i32, i32), geometry::ColliderHandle>, // move to sim manager pub physics_manager: PhysicsManager, debug_line_buffer: DebugLineBuffer, } impl RbManager { pub fn tick(&mut self, _delta_time: f32) { self.physics_manager.world.step(); } pub fn debug_render(&mut self, mode: DebugRenderMode) -> &[DebugVertex] { puffin::profile_function!(); self.debug_line_buffer.vertices.clear(); self.physics_manager.debug_render_pipeline.mode = mode; self.physics_manager.world.debug_render( &mut self.physics_manager.debug_render_pipeline, &mut self.debug_line_buffer, ); &self.debug_line_buffer.vertices } fn polyline_from_marchable( marchable: &impl Marchable, minimum_verts_per_poly: Option, ) -> (Vec, Vec<[u32; 2]>) { let polys = marching_squares_vertex_trace(marchable); let mut vertices = Vec::new(); let mut indices = Vec::new(); let minimum_verts = minimum_verts_per_poly.unwrap_or(3); let centre_offset = (marchable.marchable_size() / 2).as_vec2() - 0.5; for poly in polys { let v = vertices.len() as u32; let p = poly.len() as u32; if p < minimum_verts { continue; } for i in 0..p { vertices.push(poly[i as usize] - centre_offset); indices.push([v + i, v + (i + 1) % p]); } } (vertices, indices) } fn polyline_collider_from_marchable( marchable: &impl Marchable, minimum_verts: Option, ) -> geometry::ColliderBuilder { let (vertices, indices) = RbManager::polyline_from_marchable(marchable, minimum_verts); geometry::ColliderBuilder::polyline(vertices, Some(indices)) } pub fn convex_hull_collider_from_marchable( marchable: &impl Marchable, minimum_verts_per_poly: Option, ) -> geometry::ColliderBuilder { puffin::profile_function!(); let (vertices, indices) = RbManager::polyline_from_marchable(marchable, minimum_verts_per_poly); // convex_decomposition will panic if we have fewer verts if vertices.len() < 3 { panic!("Too few vertices to build collider!"); } else { puffin::profile_scope!("Convex decomposition"); // TODO there are some pathological cases here that are taking 11ms+ // VHACD is the wrong thing, we should use hertel-melhorn geometry::ColliderBuilder::convex_decomposition(&vertices, &indices) } } pub fn upsert_chunk_collider(&mut self, cx: i32, cy: i32, chunk: &Chunk) { puffin::profile_function!(); let collider = RbManager::polyline_collider_from_marchable(chunk, Some(20)).translation(vec2( (cx as f32 + 0.5) * CHUNK_SIZE as f32, (cy as f32 + 0.5) * CHUNK_SIZE as f32, )); if let Some(handle) = self.chunk_colliders.remove(&(cx, cy)) { self.physics_manager.remove_collider(handle); } let handle = self.physics_manager.world.insert_collider(collider, None); self.chunk_colliders.insert((cx, cy), handle); } pub fn new() -> Self { RbManager { chunk_colliders: FxHashMap::default(), physics_manager: PhysicsManager::new(), debug_line_buffer: DebugLineBuffer::default(), } } }