pub mod debug_render; pub mod rb_entity; use fxhash::FxHashMap; use glam::Vec2; use rapier2d::{ dynamics::{self}, geometry::{self, ColliderHandle}, glamx::vec2, math, prelude, }; use crate::{ config::{CELLS_IN_CHUNK, CHUNK_SIZE, PHYSICS_DELTA_TIME, PIXELS_TO_METRES}, sim::{ cell::{cell::Cell, materials::MaterialId}, cell_sim::chunk::Chunk, lib::marching_squares::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), 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, cells: Box<[Cell; CELLS_IN_CHUNK]>, rb_parent: prelude::RigidBodyHandle, ) { let id = self.next_id; self.next_id += 1; let entity: RbEntity = RbEntity { id, cells, rb_parent, }; self.rb_entities.insert(id, entity); } 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 collider_from_chunk(&self, position: Vec2, chunk: &Chunk) -> geometry::Collider { let polys = marching_squares_vertex_trace(chunk, CHUNK_SIZE, CHUNK_SIZE); 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] / PIXELS_TO_METRES); indices.push([v + i, v + (i + 1) % p]); } } geometry::ColliderBuilder::polyline(vertices, Some(indices)) .translation(position / PIXELS_TO_METRES) .build() } pub fn upsert_chunk_collider(&mut self, cx: i32, cy: i32, chunk: &Chunk) { let collider = self.collider_from_chunk( Vec2::new((cx * CHUNK_SIZE) as f32, (cy * CHUNK_SIZE) as f32), chunk, ); 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 test(&mut self) { /* Create the ground. */ let collider = geometry::ColliderBuilder::cuboid(100.0 / PIXELS_TO_METRES, 8.0 / PIXELS_TO_METRES) .position(math::Pose2::from_translation(vec2( 0 as f32, CHUNK_SIZE as f32 / PIXELS_TO_METRES, ))) .build(); self.physics_manager.collider_set.insert(collider); } pub fn test_spawn_box(&mut self, x: f32, y: f32, material: MaterialId) { /* Create the bouncing ball. */ let rigid_body = dynamics::RigidBodyBuilder::dynamic() .translation(math::Vector::new( x / PIXELS_TO_METRES, y / PIXELS_TO_METRES, )) .build(); let collider = geometry::ColliderBuilder::cuboid(5.0 / PIXELS_TO_METRES, 5.0 / PIXELS_TO_METRES) .build(); let ball_body_handle = self.physics_manager.rigid_body_set.insert(rigid_body); self.physics_manager.collider_set.insert_with_parent( collider, ball_body_handle, &mut self.physics_manager.rigid_body_set, ); let mut test_cells = Box::new([Cell::void(); CELLS_IN_CHUNK]); for x in CHUNK_SIZE / 2 - 5..CHUNK_SIZE / 2 + 5 { for y in CHUNK_SIZE / 2 - 5..CHUNK_SIZE / 2 + 5 { let cell_idx = x + y * CHUNK_SIZE; test_cells[cell_idx as usize] = Cell::from_material(material); test_cells[cell_idx as usize].set_rb(true); } } self.create_rb_entity(test_cells, ball_body_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(), } } }