summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/config.rs2
-rw-r--r--src/main.rs24
-rw-r--r--src/renderer/mod.rs48
-rw-r--r--src/sim/cell/cell.rs12
-rw-r--r--src/sim/cell_manager/chunk.rs12
-rw-r--r--src/sim/entity/entities/entity_cube.rs27
-rw-r--r--src/sim/entity/entities/mod.rs1
-rw-r--r--src/sim/entity/mod.rs155
-rw-r--r--src/sim/lib/marching_squares.rs17
-rw-r--r--src/sim/particle_manager/mod.rs4
-rw-r--r--src/sim/rb_manager/debug_ops.rs48
-rw-r--r--src/sim/rb_manager/debug_render.rs6
-rw-r--r--src/sim/rb_manager/mod.rs167
-rw-r--r--src/sim/rb_manager/rb_entity.rs39
-rw-r--r--src/sim/sim_manager/mod.rs125
-rw-r--r--src/sim/sim_manager/utils.rs28
16 files changed, 372 insertions, 343 deletions
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<u32> =
- 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<u32> = 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;
- }
+ if let Some(cells) = &entity.cells {
+ for i in 0..cells.cells.len() {
+ cell_buffer[i] = cells.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);
+ 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,
- );
+ 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 glam::{IVec2, Vec2};
+use rapier2d::{
+ dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
+ geometry::{Collider, ColliderHandle},
+};
-// use crate::sim::cell::cell::Cell;
+use crate::{
+ config::CELLS_TO_METRES,
+ sim::{
+ cell::{cell::Cell, materials::MaterialId},
+ lib::marching_squares::Marchable,
+ rb_manager::RbManager,
+ sim_manager::SimManager,
+ },
+};
-// pub struct EntityCells {
-// pub size: IVec2,
-// pub cells: Vec<Cell>,
-// }
-// pub trait EntityBehaviour {
-// fn update (&mut self) -> ();
-// }
+pub mod entities;
-// pub struct Entity {
-// pub rb_h: Option<RigidBodyHandle>,
-// pub collider_h: Option<ColliderHandle>,
-// pub cells: EntityCells,
-// pub behaviour:
-// }
+pub struct EntityCells {
+ pub size: IVec2,
+ pub cells: Vec<Cell>,
+}
-// impl Entity {
-// pub fn position(&self, rbsm:) -> Vec2 {
+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;
+ }
+}
-// }
-// pub fn destroy(&mut self) -> {
+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<RigidBody>,
+ pub collider: Option<Collider>,
+ pub cells: Option<EntityCells>,
+ pub behaviour: Option<Box<dyn EntityBehaviour>>,
+}
+
+impl EntityDef {
+ pub fn from_cells(
+ position: Vec2,
+ cells: EntityCells,
+ behaviour: Option<Box<dyn EntityBehaviour>>,
+ ) -> 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<RigidBodyHandle>,
+ pub collider_h: Option<ColliderHandle>,
+ pub cells: Option<EntityCells>,
+ behaviour: Option<Box<dyn EntityBehaviour>>,
+}
+
+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<RigidBodyHandle>,
+ collider_h: Option<ColliderHandle>,
+ cells: Option<EntityCells>,
+ behaviour: Option<Box<dyn EntityBehaviour>>,
+ ) -> 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<u8> {
@@ -52,10 +52,10 @@ pub fn compute_types(marchable: &impl Marchable, w: i32, h: i32) -> Vec<u8> {
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<Vec<Vec2>> {
+pub fn marching_squares_vertex_trace(marchable: &impl Marchable) -> Vec<Vec<Vec2>> {
+ 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<Particle>,
}
-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<u32, RbEntity>,
-
- 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<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 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<u32>,
) -> 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<u32>,
) -> 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<Cell>,
- pub rb: prelude::RigidBodyHandle,
- pub collider: Option<prelude::ColliderHandle>,
-}
-
-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<u32, Entity>,
}
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<u32> = self.rb_manager.rb_entities.keys().copied().collect();
+ let entity_ids: Vec<u32> = 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;
}