summaryrefslogtreecommitdiff
path: root/src/sim/sim_manager
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-08-24 20:15:21 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-08-24 20:15:21 -0700
commite28f8b4ef5c0744912afd49d8d1ba9a94e144613 (patch)
treea38fe431647834ea21ddd03984e41e5d00c32d50 /src/sim/sim_manager
parent3bae37a2d439804aee543473ef9b44f18692a67f (diff)
fixes for small entities
Diffstat (limited to 'src/sim/sim_manager')
-rw-r--r--src/sim/sim_manager/mod.rs49
-rw-r--r--src/sim/sim_manager/utils.rs93
2 files changed, 115 insertions, 27 deletions
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);
+ }
}
}
}