From bdad89910dcf3a56790dba60ed1f0db679432323 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Sat, 22 Aug 2026 16:49:56 -0700 Subject: refactor entities --- src/config.rs | 2 +- src/main.rs | 24 ++--- src/renderer/mod.rs | 50 +++++----- src/sim/cell/cell.rs | 12 +-- src/sim/cell_manager/chunk.rs | 12 ++- src/sim/entity/entities/entity_cube.rs | 27 ++++++ src/sim/entity/entities/mod.rs | 1 + src/sim/entity/mod.rs | 167 +++++++++++++++++++++++++++------ src/sim/lib/marching_squares.rs | 17 ++-- src/sim/particle_manager/mod.rs | 4 +- src/sim/rb_manager/debug_ops.rs | 48 ---------- src/sim/rb_manager/debug_render.rs | 6 +- src/sim/rb_manager/mod.rs | 167 ++++++--------------------------- src/sim/rb_manager/rb_entity.rs | 39 -------- src/sim/sim_manager/mod.rs | 125 ++++++++++++++++++++---- src/sim/sim_manager/utils.rs | 28 +++--- 16 files changed, 379 insertions(+), 350 deletions(-) create mode 100644 src/sim/entity/entities/entity_cube.rs create mode 100644 src/sim/entity/entities/mod.rs delete mode 100644 src/sim/rb_manager/debug_ops.rs delete mode 100644 src/sim/rb_manager/rb_entity.rs (limited to 'src') diff --git a/src/config.rs b/src/config.rs index 25986ae..5ce5d1f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,6 @@ pub const SIM_FPS: u32 = 120; pub const PHYSICS_FPS: u32 = 60; pub const PHYSICS_DELTA_TIME: f32 = 1.0 / PHYSICS_FPS as f32; -pub const PIXELS_TO_METRES: f32 = 20.0; +pub const CELLS_TO_METRES: f32 = 20.0; pub const SETTLED_THRESOHLD: u8 = 7; diff --git a/src/main.rs b/src/main.rs index 3976d71..372ef4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,10 @@ use crate::{ sim::{ cell::{cell::Cell, materials::MaterialId}, cell_manager::manager::CellManager, + entity::entities::entity_cube::entity_cube_def, lib::force::apply_explosion, particle_manager::particle::Particle, - rb_manager::{DebugRenderMode, debug_ops::DebugOperator}, + rb_manager::DebugRenderMode, sim_manager::SimManager, }, }; @@ -103,16 +104,10 @@ impl App { && let Some(lm) = self.input.last_mouse_world_pos { self.input.trigger_test_1 = false; - sim.rb_manager - .test_spawn_box(lm.0, lm.1, self.config.dropper_material); - } - - if self.input.trigger_test_2 - && let Some(lm) = self.input.last_mouse_world_pos - { - self.input.trigger_test_2 = false; - sim.rb_manager - .test_spawn_ball(lm.0, lm.1, self.config.dropper_material); + sim.create_entity(entity_cube_def( + Vec2::new(lm.0, lm.1), + self.config.dropper_material, + )); } if self.input.trigger_test_3 @@ -286,10 +281,9 @@ impl ApplicationHandler for App { KeyCode::KeyC => sim.cell_manager = CellManager::from_default_size(), KeyCode::KeyP => sim.particle_manager.particles = Vec::new(), KeyCode::KeyV => { - let entity_ids: Vec = - sim.rb_manager.rb_entities.keys().copied().collect(); - for entity_id in entity_ids { - sim.rb_manager.destroy_rb_entity(entity_id); + let ids: Vec = sim.entities.keys().cloned().collect(); + for id in ids { + sim.destroy_entity(id); } } KeyCode::Space if pressed => sim.paused = !sim.paused, diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 403eeb2..291b6d4 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -11,11 +11,8 @@ use crate::{ config::{CELLS_IN_CHUNK, CHUNK_SIZE}, renderer::ui::draw_egui, sim::{ - cell::materials::MaterialId, - cell_manager::manager::CellManager, - particle_manager::ParticleManager, - rb_manager::{RbManager, debug_render::DebugVertex, rb_entity::RbEntity}, - sim_manager::SimManager, + cell::materials::MaterialId, cell_manager::manager::CellManager, entity::Entity, + rb_manager::debug_render::DebugVertex, sim_manager::SimManager, }, }; @@ -693,23 +690,25 @@ impl RendererState { puffin::profile_scope!("Upload entity cells"); // TODO optimize, this is very inefficient, just build it per frame with positions and skip the lookup? self.renderer_rb_entities.drain(); - for entity in sim.rb_manager.rb_entities.values() { + for entity in sim.entities.values() { // TODO add "needs texture update"? - for i in 0..entity.cells.len() { - cell_buffer[i] = entity.cells[i].material as u8; - } - - let next_slot = CHUNK_SLOTS + self.renderer_rb_entities.len(); - let slot = *self - .renderer_rb_entities - .entry(entity.id) - .or_insert(next_slot); + if let Some(cells) = &entity.cells { + for i in 0..cells.cells.len() { + cell_buffer[i] = cells.cells[i].material as u8; + } - self.queue.write_buffer( - &self.cell_buffer, - (slot * CHUNK_SIZE as usize * CHUNK_SIZE as usize) as u64, - &cell_buffer, - ); + let next_slot = CHUNK_SLOTS + self.renderer_rb_entities.len(); + let slot = *self + .renderer_rb_entities + .entry(entity.id) + .or_insert(next_slot); + + self.queue.write_buffer( + &self.cell_buffer, + (slot * CHUNK_SIZE as usize * CHUNK_SIZE as usize) as u64, + &cell_buffer, + ); + } } } @@ -743,14 +742,15 @@ impl RendererState { } for (id, slot) in &self.renderer_rb_entities { - if let Some(&RbEntity { width, height, .. }) = sim.rb_manager.rb_entities.get(id) - && let Some((x, y, cos, sin)) = sim.rb_manager.get_rb_entity_transform(*id) + if let Some(entity) = sim.entities.get(id) + && let Some(cells) = &entity.cells + && let Some((pos, (cos, sin))) = entity.transform(sim) { instances.push(RendererInstance { - centre: [x, y], + centre: pos.to_array(), cos_sin: [cos, sin], - half_size: [width as f32 / 2.0, height as f32 / 2.0], - dims: [width as u32, height as u32], + half_size: (cells.size.as_vec2() / 2.0).to_array(), + dims: cells.size.as_uvec2().to_array(), cell_offset: (slot * CHUNK_SIZE as usize * CHUNK_SIZE as usize) as u32, _padding: 0, }); diff --git a/src/sim/cell/cell.rs b/src/sim/cell/cell.rs index 054ac62..2a16d0c 100644 --- a/src/sim/cell/cell.rs +++ b/src/sim/cell/cell.rs @@ -9,7 +9,7 @@ pub struct Cell { impl Cell { const FLAG_PARITY: u8 = 0b0000_0001; - const FLAG_RB: u8 = 0b0000_0010; + const FLAG_ENTITY: u8 = 0b0000_0010; const MASK_SETTLED: u8 = 0b0001_1100; #[inline] @@ -31,15 +31,15 @@ impl Cell { } #[inline] - pub fn rb(self) -> bool { - (self.flags & Self::FLAG_RB) == Self::FLAG_RB + pub fn entity(self) -> bool { + (self.flags & Self::FLAG_ENTITY) == Self::FLAG_ENTITY } #[inline] - pub fn set_rb(&mut self, rb: bool) { + pub fn set_entity(&mut self, rb: bool) { if rb { - self.flags |= Self::FLAG_RB + self.flags |= Self::FLAG_ENTITY } else { - self.flags = self.flags & !Self::FLAG_RB + self.flags = self.flags & !Self::FLAG_ENTITY } } diff --git a/src/sim/cell_manager/chunk.rs b/src/sim/cell_manager/chunk.rs index 3fe5056..3cf34ff 100644 --- a/src/sim/cell_manager/chunk.rs +++ b/src/sim/cell_manager/chunk.rs @@ -1,3 +1,5 @@ +use glam::IVec2; + use crate::{ config::{CELLS_IN_CHUNK, CHUNK_SIZE}, sim::{ @@ -32,16 +34,16 @@ impl Chunk { } impl Marchable for Chunk { - fn occupied(&self, x: i32, y: i32) -> bool { - if x < 0 || x >= CHUNK_SIZE || y < 0 || y >= CHUNK_SIZE { + fn occupied(&self, pos: IVec2) -> bool { + if pos.x < 0 || pos.x >= CHUNK_SIZE || pos.y < 0 || pos.y >= CHUNK_SIZE { false } else { // we only build a path for solid cells or settled powder cells - let cell = self.get_cell_at_local_position(x as u8, y as u8); + let cell = self.get_cell_at_local_position(pos.x as u8, pos.y as u8); cell.material.def().form == MaterialForm::Solid || cell.settled() > 4 } } - fn size(&self) -> (i32, i32) { - (CHUNK_SIZE, CHUNK_SIZE) + fn size(&self) -> IVec2 { + IVec2::new(CHUNK_SIZE, CHUNK_SIZE) } } diff --git a/src/sim/entity/entities/entity_cube.rs b/src/sim/entity/entities/entity_cube.rs new file mode 100644 index 0000000..2c79aa5 --- /dev/null +++ b/src/sim/entity/entities/entity_cube.rs @@ -0,0 +1,27 @@ +use glam::{IVec2, Vec2}; + +use crate::sim::{ + cell::{cell::Cell, materials::MaterialId}, + entity::{EntityCells, EntityDef}, +}; + +pub fn entity_cube_def(position: Vec2, material: MaterialId) -> EntityDef { + let w = 10; + let h = 10; + let mut raw_cells = vec![Cell::void(); (w * h) as usize]; + + for x in 0..w { + for y in 0..h { + let cell_idx = x + y * w; + raw_cells[cell_idx as usize] = Cell::from_material(material); + raw_cells[cell_idx as usize].set_entity(true); + } + } + + let entity_cells = EntityCells { + cells: raw_cells, + size: IVec2::new(w, h), + }; + + EntityDef::from_cells(position, entity_cells, None) +} diff --git a/src/sim/entity/entities/mod.rs b/src/sim/entity/entities/mod.rs new file mode 100644 index 0000000..449b068 --- /dev/null +++ b/src/sim/entity/entities/mod.rs @@ -0,0 +1 @@ +pub mod entity_cube; diff --git a/src/sim/entity/mod.rs b/src/sim/entity/mod.rs index ab10ea8..b648f1d 100644 --- a/src/sim/entity/mod.rs +++ b/src/sim/entity/mod.rs @@ -1,28 +1,139 @@ -// use glam::{IVec2, Vec2}; -// use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; - -// use crate::sim::cell::cell::Cell; - -// pub struct EntityCells { -// pub size: IVec2, -// pub cells: Vec, -// } -// pub trait EntityBehaviour { -// fn update (&mut self) -> (); -// } - -// pub struct Entity { -// pub rb_h: Option, -// pub collider_h: Option, -// pub cells: EntityCells, -// pub behaviour: -// } - -// impl Entity { -// pub fn position(&self, rbsm:) -> Vec2 { - -// } -// pub fn destroy(&mut self) -> { - -// } -// } +use glam::{IVec2, Vec2}; +use rapier2d::{ + dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle}, + geometry::{Collider, ColliderHandle}, +}; + +use crate::{ + config::CELLS_TO_METRES, + sim::{ + cell::{cell::Cell, materials::MaterialId}, + lib::marching_squares::Marchable, + rb_manager::RbManager, + sim_manager::SimManager, + }, +}; + +pub mod entities; + +pub struct EntityCells { + pub size: IVec2, + pub cells: Vec, +} + +impl EntityCells { + #[inline] + pub fn get_cell_at_local_position(&self, pos: IVec2) -> Cell { + self.cells[pos.x as usize + pos.y as usize * self.size.x as usize] + } + #[inline] + pub fn set_cell_at_local_position(&mut self, pos: IVec2, cell: Cell) { + self.cells[pos.x as usize + pos.y as usize * self.size.x as usize] = cell; + } +} + +impl Marchable for EntityCells { + fn occupied(&self, pos: IVec2) -> bool { + if pos.x < 0 || pos.x >= self.size.x || pos.y < 0 || pos.y >= self.size.y { + false + } else { + self.get_cell_at_local_position(pos).material != MaterialId::Void + } + } + fn size(&self) -> IVec2 { + self.size + } +} + +pub trait EntityBehaviour { + fn update(&mut self, sim: &mut SimManager, delta_time: f32) -> (); + fn physics_update(&mut self, sim: &mut SimManager, delta_time: f32) -> (); +} + +pub struct EntityDef { + pub position: Vec2, + pub rb: Option, + pub collider: Option, + pub cells: Option, + pub behaviour: Option>, +} + +impl EntityDef { + pub fn from_cells( + position: Vec2, + cells: EntityCells, + behaviour: Option>, + ) -> Self { + let rb = RigidBodyBuilder::dynamic() + .translation(position / CELLS_TO_METRES) + .build(); + + let collider = RbManager::convex_hull_collider_from_marchable(&cells, None).build(); + + EntityDef { + position, + rb: Some(rb), + collider: Some(collider), + cells: Some(cells), + behaviour, + } + } +} + +pub struct Entity { + pub id: u32, + pub rb_h: Option, + pub collider_h: Option, + pub cells: Option, + behaviour: Option>, +} + +impl Entity { + pub fn transform(&self, sim: &SimManager) -> Option<(Vec2, (f32, f32))> { + self.rb_h + .map(|rb_h| { + sim.rb_manager + .physics_manager + .rigid_body_set + .get(rb_h) + .map(|rb| { + ( + rb.translation() * CELLS_TO_METRES, + (rb.rotation().cos(), rb.rotation().sin()), + ) + }) + }) + .flatten() + } + + // TODO use drop? + pub fn destroy(&mut self, sim: &mut SimManager) -> () {} + + pub fn update(&mut self, sim: &mut SimManager, delta_time: f32) -> () { + if let Some(behaviour) = &mut self.behaviour { + behaviour.update(sim, delta_time); + } + } + + pub fn physics_update(&mut self, sim: &mut SimManager, delta_time: f32) -> () { + if let Some(behaviour) = &mut self.behaviour { + behaviour.physics_update(sim, delta_time); + } + } + + pub fn new( + id: u32, + rb_h: Option, + collider_h: Option, + cells: Option, + behaviour: Option>, + ) -> Self { + Entity { + id, + rb_h, + collider_h, + cells, + behaviour, + } + } +} diff --git a/src/sim/lib/marching_squares.rs b/src/sim/lib/marching_squares.rs index 9df3dfa..ff48fd5 100644 --- a/src/sim/lib/marching_squares.rs +++ b/src/sim/lib/marching_squares.rs @@ -1,4 +1,4 @@ -use glam::Vec2; +use glam::{IVec2, Vec2}; /** 0b(tl)(tr)(br)(bl), 0..=15 @@ -42,8 +42,8 @@ fn derive_type(tl: bool, tr: bool, br: bool, bl: bool) -> usize { } pub trait Marchable { - fn occupied(&self, x: i32, y: i32) -> bool; - fn size(&self) -> (i32, i32); + fn occupied(&self, pos: IVec2) -> bool; + fn size(&self) -> IVec2; } pub fn compute_types(marchable: &impl Marchable, w: i32, h: i32) -> Vec { @@ -52,10 +52,10 @@ pub fn compute_types(marchable: &impl Marchable, w: i32, h: i32) -> Vec { for y in -1..h { // TODO precompute per column for x in -1..w { - let tl = marchable.occupied(x, y); - let tr = marchable.occupied(x + 1, y); - let br = marchable.occupied(x + 1, y + 1); - let bl = marchable.occupied(x, y + 1); + let tl = marchable.occupied(IVec2::new(x, y)); + let tr = marchable.occupied(IVec2::new(x + 1, y)); + let br = marchable.occupied(IVec2::new(x + 1, y + 1)); + let bl = marchable.occupied(IVec2::new(x, y + 1)); types.push(derive_type(tl, tr, br, bl) as u8); } } @@ -132,7 +132,8 @@ fn toward_side(x: i32, y: i32, exit: Side) -> (i32, i32, Side) { } // outer is cw, inner ccw -pub fn marching_squares_vertex_trace(marchable: &impl Marchable, w: i32, h: i32) -> Vec> { +pub fn marching_squares_vertex_trace(marchable: &impl Marchable) -> Vec> { + let [w, h] = marchable.size().to_array(); let mut visited = vec![0u8; ((w + 1) * (h + 1)) as usize]; let idx = |cx: i32, cy: i32| (((cy + 1) * (w + 1)) + (cx + 1)) as usize; diff --git a/src/sim/particle_manager/mod.rs b/src/sim/particle_manager/mod.rs index e5e47eb..8ecf6f5 100644 --- a/src/sim/particle_manager/mod.rs +++ b/src/sim/particle_manager/mod.rs @@ -1,5 +1,5 @@ use crate::{ - config::PIXELS_TO_METRES, + config::CELLS_TO_METRES, sim::{ cell::{cell::Cell, materials::MaterialForm}, cell_manager::manager::CellManager, @@ -14,7 +14,7 @@ pub struct ParticleManager { pub particles: Vec, } -const PARTICLE_GRAVITY: f32 = 9.81 * PIXELS_TO_METRES; +const PARTICLE_GRAVITY: f32 = 9.81 * CELLS_TO_METRES; impl ParticleManager { pub fn tick(&mut self, world: &mut CellManager, delta_time: f32) { diff --git a/src/sim/rb_manager/debug_ops.rs b/src/sim/rb_manager/debug_ops.rs deleted file mode 100644 index cb56256..0000000 --- a/src/sim/rb_manager/debug_ops.rs +++ /dev/null @@ -1,48 +0,0 @@ -use glam::{ivec2, vec2}; - -use crate::sim::{ - cell::{cell::Cell, materials::MaterialId}, - rb_manager::RbManager, -}; - -pub trait DebugOperator { - fn test_spawn_box(&mut self, x: f32, y: f32, material: MaterialId) -> (); - fn test_spawn_ball(&mut self, x: f32, y: f32, material: MaterialId) -> (); -} - -impl DebugOperator for RbManager { - fn test_spawn_box(&mut self, x: f32, y: f32, material: MaterialId) { - let w = 10; - let h = 10; - let mut test_cells = vec![Cell::void(); (w * h) as usize]; - - for x in 0..w { - for y in 0..h { - let cell_idx = x + y * w; - test_cells[cell_idx as usize] = Cell::from_material(material); - test_cells[cell_idx as usize].set_rb(true); - } - } - - self.create_rb_entity(vec2(x, y), test_cells, w, h); - } - - fn test_spawn_ball(&mut self, x: f32, y: f32, material: MaterialId) { - let r = 5; - let w = r * 2; - let h = r * 2; - let mut test_cells = vec![Cell::void(); (w * h) as usize]; - - for x in 0..w { - for y in 0..h { - let cell_idx = x + y * w; - if ivec2(x, y).distance_squared(ivec2(w / 2, h / 2)) < r.pow(2) { - test_cells[cell_idx as usize] = Cell::from_material(material); - test_cells[cell_idx as usize].set_rb(true); - } - } - } - - self.create_rb_entity(vec2(x, y), test_cells, w, h); - } -} diff --git a/src/sim/rb_manager/debug_render.rs b/src/sim/rb_manager/debug_render.rs index 342fc5b..532d9a6 100644 --- a/src/sim/rb_manager/debug_render.rs +++ b/src/sim/rb_manager/debug_render.rs @@ -1,6 +1,6 @@ use rapier2d::pipeline::{DebugColor, DebugRenderBackend, DebugRenderObject}; -use crate::config::PIXELS_TO_METRES; +use crate::config::CELLS_TO_METRES; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -24,11 +24,11 @@ impl DebugRenderBackend for DebugLineBuffer { ) { let color = hsla_to_linear_rgba(color); self.vertices.push(DebugVertex { - position: [a.x * PIXELS_TO_METRES, a.y * PIXELS_TO_METRES], + position: [a.x * CELLS_TO_METRES, a.y * CELLS_TO_METRES], color, }); self.vertices.push(DebugVertex { - position: [b.x * PIXELS_TO_METRES, b.y * PIXELS_TO_METRES], + position: [b.x * CELLS_TO_METRES, b.y * CELLS_TO_METRES], color, }); } diff --git a/src/sim/rb_manager/mod.rs b/src/sim/rb_manager/mod.rs index 2c64f12..a70c677 100644 --- a/src/sim/rb_manager/mod.rs +++ b/src/sim/rb_manager/mod.rs @@ -1,38 +1,32 @@ -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 rapier2d::{geometry, glamx::vec2, prelude}; use crate::{ - config::{CHUNK_SIZE, PHYSICS_DELTA_TIME, PIXELS_TO_METRES}, + config::{CELLS_TO_METRES, CHUNK_SIZE, PHYSICS_DELTA_TIME}, 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, - }, + rb_manager::debug_render::{DebugLineBuffer, DebugVertex}, }, }; 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, + pub rigid_body_set: prelude::RigidBodySet, + pub collider_set: prelude::ColliderSet, + pub physics_pipeline: prelude::PhysicsPipeline, + pub integration_parameters: prelude::IntegrationParameters, + pub island_manager: prelude::IslandManager, + pub broad_phase: prelude::DefaultBroadPhase, + pub narrow_phase: prelude::NarrowPhase, + pub impulse_joint_set: prelude::ImpulseJointSet, + pub multibody_joint_set: prelude::MultibodyJointSet, + pub ccd_solver: prelude::CCDSolver, + pub debug_render_pipeline: prelude::DebugRenderPipeline, } impl PhysicsManager { @@ -43,7 +37,7 @@ impl PhysicsManager { physics_pipeline: prelude::PhysicsPipeline::new(), integration_parameters: prelude::IntegrationParameters { // 20 pixels <-> 1 meter - length_unit: PIXELS_TO_METRES, + length_unit: CELLS_TO_METRES, dt: PHYSICS_DELTA_TIME, ..prelude::IntegrationParameters::default() }, @@ -65,12 +59,12 @@ impl PhysicsManager { } } +// TODO remove pub struct RbManager { + // move to sim manager chunk_colliders: FxHashMap<(i32, i32), geometry::ColliderHandle>, - pub rb_entities: FxHashMap, - - physics_manager: PhysicsManager, - next_id: u32, + // move to sim manager + pub physics_manager: PhysicsManager, debug_line_buffer: DebugLineBuffer, } @@ -112,104 +106,11 @@ impl RbManager { &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 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, ) -> (Vec, Vec<[u32; 2]>) { - let (w, h) = marchable.size(); - let polys = marching_squares_vertex_trace(marchable, w, h); + let polys = marching_squares_vertex_trace(marchable); let mut vertices = Vec::new(); let mut indices = Vec::new(); @@ -223,8 +124,7 @@ impl RbManager { } 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, + (poly[i as usize] - (marchable.size().as_vec2() - 1.0) / 2.0) / CELLS_TO_METRES, ); indices.push([v + i, v + (i + 1) % p]); } @@ -234,34 +134,30 @@ impl RbManager { } fn polyline_collider_from_marchable( - &self, marchable: &impl Marchable, minimum_verts: Option, ) -> geometry::ColliderBuilder { - let (vertices, indices) = self.polyline_from_marchable(marchable, minimum_verts); + let (vertices, indices) = RbManager::polyline_from_marchable(marchable, minimum_verts); geometry::ColliderBuilder::polyline(vertices, Some(indices)) } - fn convex_hull_collider_from_marchable( - &self, + pub fn convex_hull_collider_from_marchable( marchable: &impl Marchable, minimum_verts: Option, ) -> geometry::ColliderBuilder { - let (vertices, indices) = self.polyline_from_marchable(marchable, minimum_verts); + let (vertices, indices) = RbManager::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, - ); + 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, + ) / CELLS_TO_METRES, + ); if let Some(handle) = self.chunk_colliders.remove(&(cx, cy)) { self.physics_manager.collider_set.remove( @@ -278,11 +174,8 @@ impl RbManager { 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(), } } diff --git a/src/sim/rb_manager/rb_entity.rs b/src/sim/rb_manager/rb_entity.rs deleted file mode 100644 index 18a6693..0000000 --- a/src/sim/rb_manager/rb_entity.rs +++ /dev/null @@ -1,39 +0,0 @@ -use rapier2d::prelude; - -use crate::sim::{ - cell::{cell::Cell, materials::MaterialId}, - lib::marching_squares::Marchable, -}; - -pub struct RbEntity { - pub id: u32, - pub width: i32, - pub height: i32, - pub cells: Vec, - pub rb: prelude::RigidBodyHandle, - pub collider: Option, -} - -impl RbEntity { - #[inline] - pub fn get_cell_at_local_position(&self, x: u8, y: u8) -> Cell { - self.cells[x as usize + y as usize * self.width as usize] - } - #[inline] - pub fn set_cell_at_local_position(&mut self, x: u8, y: u8, cell: Cell) { - self.cells[x as usize + y as usize * self.width as usize] = cell; - } -} - -impl Marchable for RbEntity { - fn occupied(&self, x: i32, y: i32) -> bool { - if x < 0 || x >= self.width || y < 0 || y >= self.height { - false - } else { - self.get_cell_at_local_position(x as u8, y as u8).material != MaterialId::Void - } - } - fn size(&self) -> (i32, i32) { - (self.width, self.height) - } -} diff --git a/src/sim/sim_manager/mod.rs b/src/sim/sim_manager/mod.rs index a98f3b2..b6d2528 100644 --- a/src/sim/sim_manager/mod.rs +++ b/src/sim/sim_manager/mod.rs @@ -1,14 +1,18 @@ use std::time::Instant; +use fxhash::FxHashMap; +use glam::IVec2; + use crate::{ Config, config::{PHYSICS_DELTA_TIME, PHYSICS_FPS, SIM_FPS}, sim::{ cell::{cell::Cell, materials::MaterialId}, cell_manager::manager::CellManager, + entity::{Entity, EntityDef}, particle_manager::ParticleManager, rb_manager::RbManager, - sim_manager::utils::write_rb_entity_to_world, + sim_manager::utils::write_entity_to_world, }, }; @@ -27,53 +31,116 @@ pub struct SimManager { pub cell_manager: CellManager, pub rb_manager: RbManager, pub particle_manager: ParticleManager, + + // entities + // TODO entity manager? + next_entity_id: u32, + pub entities: FxHashMap, } impl SimManager { - pub fn new() -> Self { - SimManager { - paused: false, - ignore_pause_next_tick: false, - last_cell_update: Instant::now(), - cell_updates_due: 0.0, - last_physics_update: Instant::now(), - physics_updates_due: 0.0, - cell_manager: CellManager::from_default_size(), - rb_manager: RbManager::new(), - particle_manager: ParticleManager::new(), + pub fn create_entity(&mut self, def: EntityDef) -> u32 { + let (rb_h, collider_h) = if let Some(rb) = def.rb { + let rb_h = self.rb_manager.physics_manager.rigid_body_set.insert(rb); + + if let Some(collider) = def.collider { + let collider_h = self + .rb_manager + .physics_manager + .collider_set + .insert_with_parent( + collider, + rb_h, + &mut self.rb_manager.physics_manager.rigid_body_set, + ); + (Some(rb_h), Some(collider_h)) + } else { + (Some(rb_h), None) + } + } else if let Some(collider) = def.collider { + let collider_h = self + .rb_manager + .physics_manager + .collider_set + .insert(collider); + (None, Some(collider_h)) + } else { + (None, None) + }; + + let entity = Entity::new( + self.next_entity_id, + rb_h, + collider_h, + def.cells, + def.behaviour, + ); + + let id = self.next_entity_id; + self.entities.insert(id, entity); + self.next_entity_id += 1; + id + } + + pub fn destroy_entity(&mut self, id: u32) { + if let Some(entity) = self.entities.get(&id) { + if let Some(rb_h) = entity.rb_h { + self.rb_manager.physics_manager.rigid_body_set.remove( + rb_h, + &mut self.rb_manager.physics_manager.island_manager, + &mut self.rb_manager.physics_manager.collider_set, + &mut self.rb_manager.physics_manager.impulse_joint_set, + &mut self.rb_manager.physics_manager.multibody_joint_set, + true, + ); + } else if let Some(collider_h) = entity.collider_h { + self.rb_manager.physics_manager.collider_set.remove( + collider_h, + &mut self.rb_manager.physics_manager.island_manager, + &mut self.rb_manager.physics_manager.rigid_body_set, + true, + ); + } + + self.entities.remove(&id); } } fn cell_update(&mut self, config: &Config) { - // before we tick, write all the rb entities into the sim world + // before we tick, write all the entities into the sim world // TODO optimize - let entity_ids: Vec = self.rb_manager.rb_entities.keys().copied().collect(); + let entity_ids: Vec = self.entities.keys().copied().collect(); let mut cells_written_by_entity: Vec<(u32, Vec<(u8, u8, i32, i32)>)> = Vec::new(); for entity_id in entity_ids { - let cells_written = write_rb_entity_to_world(self, entity_id, self.cell_manager.seqno); + let cells_written = write_entity_to_world(self, entity_id, self.cell_manager.seqno); cells_written_by_entity.push((entity_id, cells_written)); } self.cell_manager.tick(config.use_threading); - // after we tick, remove the written rb cells and update the entities + // after we tick, remove the written entity cells and update the entities // TODO optimize for (entity_id, cells_written) in cells_written_by_entity { - let rb_entity = self.rb_manager.rb_entities.get_mut(&entity_id).unwrap(); + let entity = self.entities.get_mut(&entity_id).unwrap(); for (lx, ly, x, y) in cells_written { // update the entity // TODO we should skip cells that weren't changed? // TODO optimize let new_local_cell = self.cell_manager.get_cell_from_game_position(x, y).unwrap(); - if new_local_cell.material != MaterialId::Void && !new_local_cell.rb() { + if new_local_cell.material != MaterialId::Void && !new_local_cell.entity() { panic!( - "Someone swapped into this rb's cell! ({x}, {y}, {m:#?}, {f})", + "Someone swapped into this entity's cell! ({x}, {y}, {m:#?}, {f})", m = new_local_cell.material, f = new_local_cell.flags ); } - rb_entity.set_cell_at_local_position(lx, ly, new_local_cell); + if let Some(cells) = &mut entity.cells { + cells.set_cell_at_local_position( + IVec2::new(lx as i32, ly as i32), + new_local_cell, + ); + } // update the world self.cell_manager .set_cell_from_game_position(x, y, Cell::void(), false); @@ -149,4 +216,22 @@ impl SimManager { self.ignore_pause_next_tick = false; } + + pub fn new() -> Self { + SimManager { + paused: false, + ignore_pause_next_tick: false, + last_cell_update: Instant::now(), + cell_updates_due: 0.0, + last_physics_update: Instant::now(), + physics_updates_due: 0.0, + + cell_manager: CellManager::from_default_size(), + rb_manager: RbManager::new(), + particle_manager: ParticleManager::new(), + + next_entity_id: 0, + entities: FxHashMap::default(), + } + } } diff --git a/src/sim/sim_manager/utils.rs b/src/sim/sim_manager/utils.rs index b636186..1565ced 100644 --- a/src/sim/sim_manager/utils.rs +++ b/src/sim/sim_manager/utils.rs @@ -1,28 +1,30 @@ +use glam::IVec2; + use crate::sim::{cell::materials::MaterialId, sim_manager::SimManager}; -pub fn write_rb_entity_to_world( +pub fn write_entity_to_world( sim: &mut SimManager, - rb_entity_id: u32, + entity_id: u32, // make sure these cells will be simulated seqno: u64, // (entity_x, entity_y, cell_x, cell_y) ) -> Vec<(u8, u8, i32, i32)> { let mut cells_written: Vec<(u8, u8, i32, i32)> = Vec::new(); - if let Some(rb_entity) = sim.rb_manager.rb_entities.get(&rb_entity_id) - && let Some((rb_x, rb_y, cos, sin)) = sim.rb_manager.get_rb_entity_transform(rb_entity_id) + if let Some(entity) = sim.entities.get(&entity_id) + && let Some(cells) = &entity.cells + && let Some((pos, (cos, sin))) = entity.transform(sim) { - let (half_size_x, half_size_y) = - (rb_entity.width as f32 / 2.0, rb_entity.height as f32 / 2.0); + let (half_size_x, half_size_y) = (cells.size.x as f32 / 2.0, cells.size.y as f32 / 2.0); // half-extent of the rotated grid's axis-aligned bounding box, plus a cell of margin let (radius_x, radius_y) = ( half_size_x * (cos.abs() + sin.abs()) + 1.0, half_size_y * (cos.abs() + sin.abs()) + 1.0, ); - let world_xl = (rb_x - radius_x).floor() as i32; - let world_xu = (rb_x + radius_x).ceil() as i32; - let world_yl = (rb_y - radius_y).floor() as i32; - let world_yu = (rb_y + radius_y).ceil() as i32; + let world_xl = (pos.x - radius_x).floor() as i32; + let world_xu = (pos.x + radius_x).ceil() as i32; + let world_yl = (pos.y - radius_y).floor() as i32; + let world_yu = (pos.y + radius_y).ceil() as i32; for world_x in world_xl..=world_xu { for world_y in world_yl..=world_yu { @@ -32,18 +34,18 @@ pub fn write_rb_entity_to_world( && cur_world_cell.material == MaterialId::Void { // same as shader - let d = (world_x as f32 + 0.5 - rb_x, world_y as f32 + 0.5 - rb_y); + let d = (world_x as f32 + 0.5 - pos.x, world_y as f32 + 0.5 - pos.y); let q = (d.0.floor() + 0.5, d.1.floor() + 0.5); let (lx, ly) = ( (q.0 * cos + q.1 * sin + half_size_x).floor() as i32, (-q.0 * sin + q.1 * cos + half_size_y).floor() as i32, ); - if lx < 0 || ly < 0 || lx >= rb_entity.width || ly >= rb_entity.height { + if lx < 0 || ly < 0 || lx >= cells.size.x || ly >= cells.size.y { continue; } - let mut cell = rb_entity.get_cell_at_local_position(lx as u8, ly as u8); + let mut cell = cells.get_cell_at_local_position(IVec2::new(lx, ly)); if cell.material == MaterialId::Void { continue; } -- cgit v1.3.1