summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs3
-rw-r--r--src/input.rs8
-rw-r--r--src/main.rs5
-rw-r--r--src/sim/lib/force.rs6
-rw-r--r--src/sim/particle_manager/mod.rs9
-rw-r--r--src/sim/rb_manager/mod.rs14
-rw-r--r--src/sim/sim_manager/mod.rs49
-rw-r--r--src/sim/sim_manager/utils.rs93
8 files changed, 141 insertions, 46 deletions
diff --git a/src/config.rs b/src/config.rs
index 17c75b3..67669ab 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -15,3 +15,6 @@ pub const CELLS_TO_METRES: f32 = 20.0;
pub const MASS_SCALING: f32 = 1.0 / 200.0;
pub const SETTLED_THRESOHLD: u8 = 7;
+
+// any entity with fewer cells than this will be turned to particles
+pub const MIN_ENTITY_CELLS: usize = 9;
diff --git a/src/input.rs b/src/input.rs
index dd49cd0..7fe6c79 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -21,6 +21,9 @@ pub enum Input {
ClearEntities,
ClearParticles,
+ // game controls?
+ Grenade,
+
// placeholder actions
Action1,
Action2,
@@ -30,9 +33,9 @@ pub enum Input {
Action6,
}
-const INPUT_VAR_LEN: usize = 17;
+const INPUT_VAR_LEN: usize = 16;
-const KEYMAP: [(KeyCode, Input); 15] = [
+const KEYMAP: [(KeyCode, Input); 16] = [
(KeyCode::KeyW, Input::Up),
(KeyCode::KeyA, Input::Left),
(KeyCode::KeyS, Input::Down),
@@ -42,6 +45,7 @@ const KEYMAP: [(KeyCode, Input); 15] = [
(KeyCode::KeyC, Input::ClearGrid),
(KeyCode::KeyV, Input::ClearEntities),
(KeyCode::KeyP, Input::ClearParticles),
+ (KeyCode::KeyG, Input::Grenade),
(KeyCode::Digit1, Input::Action1),
(KeyCode::Digit2, Input::Action2),
(KeyCode::Digit3, Input::Action3),
diff --git a/src/main.rs b/src/main.rs
index f896de3..ccca2db 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,7 +6,6 @@ mod renderer;
mod sim;
use futures::executor;
-use rand::random_range;
use std::{collections::VecDeque, sync::Arc, time::Instant};
use winit::{
application::ApplicationHandler,
@@ -30,7 +29,7 @@ use crate::{
InputManager,
},
renderer::RendererState,
- sim::{cell::Cell, rb_manager::DebugRenderMode, sim_manager::SimManager},
+ sim::{rb_manager::DebugRenderMode, sim_manager::SimManager},
};
pub type Error = Box<dyn std::error::Error>;
@@ -87,7 +86,7 @@ impl App {
));
}
- if self.input_manager.pressed(Input::Action2) {
+ if self.input_manager.pressed(Input::Grenade) {
sim.create_entity(entity_grenade_def(self.input_manager.world_mouse_pos, 3.0));
}
diff --git a/src/sim/lib/force.rs b/src/sim/lib/force.rs
index c110c29..c6cfaf0 100644
--- a/src/sim/lib/force.rs
+++ b/src/sim/lib/force.rs
@@ -81,7 +81,7 @@ pub fn apply_explosion(
.physics_manager
.world
.intersect_aabb_conservative(
- Aabb::new(centre - radius as f32, (centre + radius as f32)),
+ Aabb::new(centre - radius as f32, centre + radius as f32),
QueryFilter::only_dynamic(),
);
@@ -91,8 +91,8 @@ pub fn apply_explosion(
if let Some(h) = h
&& let Some(body) = ctx.rb_manager.physics_manager.world.bodies.get_mut(h)
{
- // magic divisor number for good vibes
- body.apply_impulse(get_vel(body.translation()) / 3.0, true);
+ // magic number for good vibes
+ body.apply_impulse(get_vel(body.translation()) * 3.0, true);
}
}
}
diff --git a/src/sim/particle_manager/mod.rs b/src/sim/particle_manager/mod.rs
index 559bf2c..afebd4b 100644
--- a/src/sim/particle_manager/mod.rs
+++ b/src/sim/particle_manager/mod.rs
@@ -64,13 +64,10 @@ impl ParticleManager {
]
.contains(&cell.material.def().form)
{
+ let mut cell = Cell::from_material(p.material);
+ cell.match_parity(world.seqno);
// write ourselves to the board
- world.set_cell_from_game_position(
- prev.x,
- prev.y,
- Cell::from_material(p.material),
- false,
- );
+ world.set_cell_from_game_position(prev.x, prev.y, cell, false);
self.particles.swap_remove(i);
continue 'outer;
}
diff --git a/src/sim/rb_manager/mod.rs b/src/sim/rb_manager/mod.rs
index efb25f9..3711b80 100644
--- a/src/sim/rb_manager/mod.rs
+++ b/src/sim/rb_manager/mod.rs
@@ -80,13 +80,13 @@ impl RbManager {
fn polyline_from_marchable(
marchable: &impl Marchable,
- minimum_verts: Option<u32>,
+ minimum_verts_per_poly: Option<u32>,
) -> (Vec<Vec2>, 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.unwrap_or(3);
+ let minimum_verts = minimum_verts_per_poly.unwrap_or(3);
let centre_offset = (marchable.size() / 2).as_vec2() - 0.5;
@@ -116,11 +116,15 @@ impl RbManager {
pub fn convex_hull_collider_from_marchable(
marchable: &impl Marchable,
- minimum_verts: Option<u32>,
+ minimum_verts_per_poly: Option<u32>,
) -> geometry::ColliderBuilder {
puffin::profile_function!();
- let (vertices, indices) = RbManager::polyline_from_marchable(marchable, minimum_verts);
- {
+ 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 5ms+
geometry::ColliderBuilder::convex_decomposition(&vertices, &indices)
diff --git a/src/sim/sim_manager/mod.rs b/src/sim/sim_manager/mod.rs
index b2441ef..9ae66be 100644
--- a/src/sim/sim_manager/mod.rs
+++ b/src/sim/sim_manager/mod.rs
@@ -1,6 +1,7 @@
use std::time::Instant;
use fxhash::FxHashMap;
+use glam::Vec2;
use rand::random_range;
use crate::{
@@ -11,7 +12,7 @@ use crate::{
cell::Cell,
cell_manager::manager::CellManager,
entity::{Entity, EntityDef, EntityId},
- particle_manager::ParticleManager,
+ particle_manager::{ParticleManager, particle::Particle},
rb_manager::RbManager,
sim_manager::utils::{
process_entity_update_result, read_back_entities_from_world, write_entities_to_world,
@@ -97,9 +98,41 @@ impl SimManager {
}
}
+ pub fn atomize_entity(&mut self, id: EntityId) {
+ if let Some(entity) = self.entities.get(&id)
+ && let Some(cells) = &entity.data.cells
+ {
+ // TODO these unwraps exist because we don't have separate entity position from rb
+ let rb = self
+ .rb_manager
+ .physics_manager
+ .world
+ .bodies
+ .get(entity.data.rb_h.unwrap())
+ .unwrap();
+
+ for x in 0..cells.size.x {
+ for y in 0..cells.size.y {
+ let r = rb.position().transform_vector(
+ Vec2::new(x as f32, y as f32) - (cells.size / 2).as_vec2(),
+ );
+ self.particle_manager.particles.push(Particle::new(
+ rb.translation() + r,
+ rb.linvel(),
+ cells.cells[(x + y * cells.size.x) as usize].material,
+ 3.0,
+ 0.1,
+ ))
+ }
+ }
+
+ self.destroy_entity(id);
+ }
+ }
+
fn cell_update(&mut self, config: &Config, input_manager: &InputManager, delta_time: f32) {
// before we tick, write all the entities into the sim world
- let cells_written_by_entity = write_entities_to_world(self);
+ let written_entities_scope = write_entities_to_world(self);
// --TEST DRAWING--
if input_manager.lmb_held || input_manager.rmb_held {
@@ -136,6 +169,16 @@ impl SimManager {
}
}
+ if input_manager.pressed(Input::Action2) {
+ if let Some(entity_id) = written_entities_scope
+ .get_entity_id_at_position(input_manager.world_mouse_pos.round().as_ivec2())
+ {
+ self.atomize_entity(entity_id);
+ }
+ }
+
+ // TEST ATOMIZATION
+
// tick
self.cell_manager.tick(config.use_threading);
@@ -157,7 +200,7 @@ impl SimManager {
}
// after we tick, remove the written entity cells and update the entities
- read_back_entities_from_world(self, cells_written_by_entity);
+ read_back_entities_from_world(self, written_entities_scope);
}
fn physics_update(&mut self, input_manager: &InputManager, delta_time: f32) {
diff --git a/src/sim/sim_manager/utils.rs b/src/sim/sim_manager/utils.rs
index 69a4756..38f503a 100644
--- a/src/sim/sim_manager/utils.rs
+++ b/src/sim/sim_manager/utils.rs
@@ -1,12 +1,14 @@
-use glam::IVec2;
+use glam::{IVec2, Vec2};
use rapier2d::dynamics::RigidBodyBuilder;
use crate::{
+ config::MIN_ENTITY_CELLS,
content::materials::MaterialId,
sim::{
cell::Cell,
entity::{EntityCells, EntityDef, EntityId, EntityUpdateResult},
lib::components::compute_components,
+ particle_manager::particle::Particle,
sim_manager::SimManager,
},
};
@@ -66,10 +68,27 @@ fn write_entity_to_world(sim: &mut SimManager, entity_id: EntityId) -> Vec<(u8,
cells_written
}
+pub struct WrittenEntitiesScope {
+ cells_written_by_entity: Vec<(EntityId, Vec<(u8, u8, i32, i32)>)>,
+}
+
+impl WrittenEntitiesScope {
+ // TODO can use a hashmap to optimize this
+ pub fn get_entity_id_at_position(&self, position: IVec2) -> Option<EntityId> {
+ self.cells_written_by_entity
+ .iter()
+ .find(|(_, v)| {
+ v.iter()
+ .any(|&(_, _, x, y)| x == position.x && y == position.y)
+ })
+ .map(|e| e.0)
+ }
+}
+
pub fn write_entities_to_world(
sim: &mut SimManager,
// (entity_x, entity_y, cell_x, cell_y)
-) -> Vec<(EntityId, Vec<(u8, u8, i32, i32)>)> {
+) -> WrittenEntitiesScope {
puffin::profile_function!();
// TODO optimize
let entity_ids: Vec<EntityId> = sim.entities.keys().copied().collect();
@@ -80,16 +99,18 @@ pub fn write_entities_to_world(
cells_written_by_entity.push((entity_id, cells_written));
}
- cells_written_by_entity
+ WrittenEntitiesScope {
+ cells_written_by_entity,
+ }
}
// TODO optimize
pub fn read_back_entities_from_world(
sim: &mut SimManager,
- cells_written_by_entity: Vec<(EntityId, Vec<(u8, u8, i32, i32)>)>,
+ written_entities_scope: WrittenEntitiesScope,
) {
puffin::profile_function!();
- for (entity_id, cells_written) in cells_written_by_entity {
+ for (entity_id, cells_written) in written_entities_scope.cells_written_by_entity {
let mut entity = sim.entities.get_mut(&entity_id);
let mut should_update_entity = false;
@@ -128,7 +149,10 @@ pub fn read_back_entities_from_world(
} else if components.len() == 1 {
// the entity's cells were changed, but not partitioned
puffin::profile_scope!("Recompute collider");
- if let Some(new_collider) = entity.compute_collider() {
+ // if the new entity is too small for a collider, we can turn the entity into particles
+ if components[0].cells.len() < MIN_ENTITY_CELLS {
+ sim.atomize_entity(entity_id);
+ } else if let Some(new_collider) = entity.compute_collider() {
// don't profile this separately as it's basically free
sim.rb_manager
.physics_manager
@@ -146,7 +170,6 @@ pub fn read_back_entities_from_world(
} else {
// the entity was partitioned
// destroy the original entity
- // correctness -- not correct! because we don't have pos without rb
puffin::profile_scope!("Partition entity");
let old_rb = sim
.rb_manager
@@ -154,33 +177,55 @@ pub fn read_back_entities_from_world(
.world
.bodies
.get(entity.data.rb_h.unwrap())
+ // correctness -- not correct! because we don't have pos without rb
.unwrap();
let old_pose = old_rb.position().clone();
let old_linvel = old_rb.linvel().clone();
let old_angvel = old_rb.angvel().clone();
sim.destroy_entity(entity_id);
+
// and create a new entity for each component
for component in components {
- let r = old_pose.transform_vector(component.position);
- let mut pose = old_pose;
- pose.translation += r;
+ // unless it's too small
+ if component.cells.len() < MIN_ENTITY_CELLS {
+ for x in 0..component.size.x {
+ for y in 0..component.size.y {
+ let r = old_pose.transform_vector(
+ Vec2::new(x as f32, y as f32) - (component.size / 2).as_vec2()
+ + component.position,
+ );
+ sim.particle_manager.particles.push(Particle::new(
+ old_pose.translation + r,
+ old_linvel,
+ component.cells[(x + y * component.size.x) as usize].material,
+ 3.0,
+ 0.1,
+ ))
+ }
+ }
+ } else {
+ let r = old_pose.transform_vector(component.position);
+ let mut pose = old_pose;
+ pose.translation += r;
- let rb = RigidBodyBuilder::dynamic()
- .pose(pose)
- .linvel(old_linvel)
- .angvel(old_angvel)
- .build();
+ let rb = RigidBodyBuilder::dynamic()
+ .pose(pose)
+ .linvel(old_linvel)
+ .angvel(old_angvel)
+ .build();
- let def = EntityDef::from_cells_and_rb(
- EntityCells {
- cells: component.cells,
- size: component.size,
- },
- rb,
- None,
- );
- sim.create_entity(def);
+ let def = EntityDef::from_cells_and_rb(
+ EntityCells {
+ cells: component.cells,
+ size: component.size,
+ },
+ rb,
+ None,
+ );
+
+ sim.create_entity(def);
+ }
}
}
}