summaryrefslogtreecommitdiff
path: root/src/sim/rb_manager/mod.rs
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-08-22 15:00:35 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-08-22 15:00:35 -0700
commit1a515237afb7ad09353a65f5fbc6e98a7c29ce8e (patch)
tree48cd4b4bcf8f8e357811f905f22607838d7c1f96 /src/sim/rb_manager/mod.rs
parent6350e4ffca8ce1e46465284ef3d7559e0f40229b (diff)
sim manager refactor
Diffstat (limited to 'src/sim/rb_manager/mod.rs')
-rw-r--r--src/sim/rb_manager/mod.rs289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/sim/rb_manager/mod.rs b/src/sim/rb_manager/mod.rs
new file mode 100644
index 0000000..2c64f12
--- /dev/null
+++ b/src/sim/rb_manager/mod.rs
@@ -0,0 +1,289 @@
+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_manager::chunk::Chunk,
+ lib::marching_squares::{Marchable, marching_squares_vertex_trace},
+ rb_manager::{
+ 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 RbManager {
+ chunk_colliders: FxHashMap<(i32, i32), geometry::ColliderHandle>,
+ pub rb_entities: FxHashMap<u32, RbEntity>,
+
+ physics_manager: PhysicsManager,
+ next_id: u32,
+ debug_line_buffer: DebugLineBuffer,
+}
+
+impl RbManager {
+ pub fn 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<Cell>, 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 mut entity: RbEntity = RbEntity {
+ id,
+ cells,
+ width: w,
+ height: h,
+ rb: rb_handle,
+ collider: None,
+ };
+
+ let collider = self
+ .convex_hull_collider_from_marchable(&entity, None)
+ .build();
+ let collider_handle = self.physics_manager.collider_set.insert_with_parent(
+ collider,
+ rb_handle,
+ &mut self.physics_manager.rigid_body_set,
+ );
+
+ entity.collider = Some(collider_handle);
+
+ self.rb_entities.insert(id, entity);
+
+ id
+ }
+
+ pub fn update_rb_entity(&mut self, entity_id: u32) {
+ let entity = self.rb_entities.get(&entity_id).unwrap();
+ let new_collider = self
+ .convex_hull_collider_from_marchable(entity, None)
+ .build();
+
+ self.physics_manager.collider_set.remove(
+ entity.collider.unwrap(),
+ &mut self.physics_manager.island_manager,
+ &mut self.physics_manager.rigid_body_set,
+ false,
+ );
+
+ let new_collider_handle = self.physics_manager.collider_set.insert_with_parent(
+ new_collider,
+ entity.rb,
+ &mut self.physics_manager.rigid_body_set,
+ );
+
+ let entity = self.rb_entities.get_mut(&entity_id).unwrap();
+ entity.collider = Some(new_collider_handle);
+ }
+
+ 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,
+ &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);
+ 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,
+ minimum_verts: Option<u32>,
+ ) -> (Vec<Vec2>, 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();
+
+ let minimum_verts = minimum_verts.unwrap_or(3);
+
+ 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] - 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,
+ minimum_verts: Option<u32>,
+ ) -> geometry::ColliderBuilder {
+ let (vertices, indices) = self.polyline_from_marchable(marchable, minimum_verts);
+
+ geometry::ColliderBuilder::polyline(vertices, Some(indices))
+ }
+
+ fn convex_hull_collider_from_marchable(
+ &self,
+ marchable: &impl Marchable,
+ minimum_verts: Option<u32>,
+ ) -> geometry::ColliderBuilder {
+ let (vertices, indices) = self.polyline_from_marchable(marchable, minimum_verts);
+ geometry::ColliderBuilder::convex_decomposition(&vertices, &indices)
+ }
+
+ pub fn upsert_chunk_collider(&mut self, cx: i32, cy: i32, chunk: &Chunk) {
+ puffin::profile_function!();
+ let collider = self
+ .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,
+ ) / 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 {
+ RbManager {
+ rb_entities: FxHashMap::default(),
+ chunk_colliders: FxHashMap::default(),
+
+ physics_manager: PhysicsManager::new(),
+ next_id: 0,
+ debug_line_buffer: DebugLineBuffer::default(),
+ }
+ }
+}