use glam::{IVec2, Vec2}; use rapier2d::{ dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle}, geometry::{Collider, ColliderHandle}, }; use crate::{ config::MASS_SCALING, content::materials::MaterialId, sim::{ cell::Cell, lib::marching_squares::Marchable, rb_manager::RbManager, sim_manager::SimCtx, }, sprite_loader::load_sprite_to_cells, }; fn mass_from_cells(cells: &[Cell]) -> f32 { cells .iter() .fold(0.0, |acc, cur| acc + cur.material.def().density as f32) * MASS_SCALING } 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 marchable_size(&self) -> IVec2 { self.size } } pub trait EntityBehaviour { // TODO merge ctxs? fn update(&mut self, _update_ctx: &mut EntityUpdateCtx, _ctx: &mut SimCtx, _delta_time: f32) {} fn physics_update( &mut self, _update_ctx: &mut EntityUpdateCtx, _ctx: &mut SimCtx, _delta_time: f32, ) { } } pub struct EntityDef { pub rb: Option, pub collider: Option, pub cells: Option, pub behaviour: Option>, } impl EntityDef { pub fn from_cells_and_rb( cells: EntityCells, rb: RigidBody, behaviour: Option>, ) -> Self { let collider = RbManager::convex_hull_collider_from_marchable(&cells, Some(10)) .mass(mass_from_cells(&cells.cells)) .build(); EntityDef { rb: Some(rb), collider: Some(collider), cells: Some(cells), behaviour, } } pub fn from_cells( position: Vec2, cells: EntityCells, behaviour: Option>, ) -> Self { let rb = RigidBodyBuilder::dynamic().translation(position).build(); let collider = RbManager::convex_hull_collider_from_marchable(&cells, Some(10)) .mass(mass_from_cells(&cells.cells)) .build(); EntityDef { rb: Some(rb), collider: Some(collider), cells: Some(cells), behaviour, } } pub fn from_sprite( position: Vec2, path: &str, behaviour: Option>, ) -> Self { let sprite_cells = load_sprite_to_cells(path); let mut entity_cells = EntityCells { cells: sprite_cells.cells, size: IVec2::new(sprite_cells.width as i32, sprite_cells.height as i32), }; entity_cells .cells .iter_mut() .for_each(|c| c.set_entity_integrated(true)); EntityDef::from_cells(position, entity_cells, behaviour) } } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct EntityId(pub u32); pub struct EntityData { pub id: EntityId, pub rb_h: Option, pub collider_h: Option, pub cells: Option, } pub struct EntityUpdateResult { pub deferred_destructions: Vec, } pub struct EntityUpdateCtx<'a> { pub entity_data: &'a mut EntityData, deferred_destructions: Vec, } impl<'a> EntityUpdateCtx<'a> { pub fn from_entity_data(entity_data: &'a mut EntityData) -> Self { EntityUpdateCtx { entity_data, deferred_destructions: Vec::new(), } } pub fn deferred_destroy(&mut self, entity_id: EntityId) { self.deferred_destructions.push(entity_id); } pub fn to_result(self) -> EntityUpdateResult { EntityUpdateResult { deferred_destructions: self.deferred_destructions, } } } impl EntityData { pub fn _linvel(&self, rb_manager: &RbManager) -> Option { self.rb_h.and_then(|rb_h| { rb_manager .physics_manager .world .bodies .get(rb_h) .map(|rb| rb.linvel()) }) } pub fn _transform(&self, rb_manager: &RbManager) -> Option<(Vec2, (f32, f32))> { self.rb_h.and_then(|rb_h| { rb_manager .physics_manager .world .bodies .get(rb_h) .map(|rb| (rb.translation(), (rb.rotation().cos(), rb.rotation().sin()))) }) } pub fn transform(&self, ctx: &SimCtx) -> Option<(Vec2, (f32, f32))> { self._transform(ctx.rb_manager) } } pub struct Entity { pub data: EntityData, behaviour: Option>, } impl Entity { pub fn update(&mut self, ctx: &mut SimCtx, delta_time: f32) -> Option { if let Some(behaviour) = &mut self.behaviour { let mut ectx = EntityUpdateCtx::from_entity_data(&mut self.data); behaviour.update(&mut ectx, ctx, delta_time); return Some(ectx.to_result()); } None } pub fn physics_update( &mut self, ctx: &mut SimCtx, delta_time: f32, ) -> Option { if let Some(behaviour) = &mut self.behaviour { let mut ectx = EntityUpdateCtx::from_entity_data(&mut self.data); behaviour.physics_update(&mut ectx, ctx, delta_time); return Some(ectx.to_result()); } None } pub fn compute_collider(&self) -> Option { self.data.cells.as_ref().map(|cells| { RbManager::convex_hull_collider_from_marchable(cells, Some(10)) .mass(mass_from_cells(&cells.cells)) .build() }) } pub fn new( id: EntityId, rb_h: Option, collider_h: Option, cells: Option, behaviour: Option>, ) -> Self { Entity { data: EntityData { id, rb_h, collider_h, cells, }, behaviour, } } }