diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-24 20:15:21 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-24 20:15:21 -0700 |
| commit | e28f8b4ef5c0744912afd49d8d1ba9a94e144613 (patch) | |
| tree | a38fe431647834ea21ddd03984e41e5d00c32d50 /src/sim | |
| parent | 3bae37a2d439804aee543473ef9b44f18692a67f (diff) | |
fixes for small entities
Diffstat (limited to 'src/sim')
| -rw-r--r-- | src/sim/lib/force.rs | 6 | ||||
| -rw-r--r-- | src/sim/particle_manager/mod.rs | 9 | ||||
| -rw-r--r-- | src/sim/rb_manager/mod.rs | 14 | ||||
| -rw-r--r-- | src/sim/sim_manager/mod.rs | 49 | ||||
| -rw-r--r-- | src/sim/sim_manager/utils.rs | 93 |
5 files changed, 130 insertions, 41 deletions
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); + } } } } |
