diff --git a/crates/comet_app/src/app.rs b/crates/comet_app/src/app.rs index 083aa48..b998b7b 100755 --- a/crates/comet_app/src/app.rs +++ b/crates/comet_app/src/app.rs @@ -1,5 +1,7 @@ use comet_colors::{Color as ColorTrait, LinearRgba}; -use comet_ecs::{Camera2D, Component, Entity, Render2D, Scene, Text, Transform2D, Transform3D}; +use comet_ecs::{ + Camera2D, Component, Entity, EntityId, Render2D, Scene, Text, Transform2D, Transform3D, +}; use comet_input::keyboard::Key; use comet_log::*; use comet_renderer::renderer::Renderer; @@ -163,22 +165,22 @@ impl App { } /// Creates a new entity and returns its ID. - pub fn new_entity(&mut self) -> usize { - self.scene.new_entity() as usize + pub fn new_entity(&mut self) -> EntityId { + self.scene.new_entity() } /// Deletes an entity by its ID. - pub fn delete_entity(&mut self, entity_id: usize) { + pub fn delete_entity(&mut self, entity_id: EntityId) { self.scene.delete_entity(entity_id) } /// Gets an immutable reference to an entity by its ID. - pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> { + pub fn get_entity(&self, entity_id: EntityId) -> Option<&Entity> { self.scene.get_entity(entity_id) } /// Gets a mutable reference to an entity by its ID. - pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> { + pub fn get_entity_mut(&mut self, entity_id: EntityId) -> Option<&mut Entity> { self.scene.get_entity_mut(entity_id) } @@ -194,29 +196,29 @@ impl App { /// Adds a component to an entity by its ID and an instance of the component. /// Overwrites the previous component if another component of the same type is added. - pub fn add_component(&mut self, entity_id: usize, component: C) { + pub fn add_component(&mut self, entity_id: EntityId, component: C) { self.scene.add_component(entity_id, component) } /// Removes a component from an entity by its ID. - pub fn remove_component(&mut self, entity_id: usize) { + pub fn remove_component(&mut self, entity_id: EntityId) { self.scene.remove_component::(entity_id) } /// Returns a reference to a component of an entity by its ID. - pub fn get_component(&self, entity_id: usize) -> Option<&C> { + pub fn get_component(&self, entity_id: EntityId) -> Option<&C> { self.scene.get_component::(entity_id) } /// Returns a mutable reference to a component of an entity by its ID. - pub fn get_component_mut(&mut self, entity_id: usize) -> Option<&mut C> { + pub fn get_component_mut(&mut self, entity_id: EntityId) -> Option<&mut C> { self.scene.get_component_mut::(entity_id) } /// Returns a list of entities that have the given components. /// The amount of queriable components is limited to 3 such that the `Archetype` creation is more efficient. /// Otherwise it would be a factorial complexity chaos. - pub fn get_entities_with(&self, components: Vec) -> Vec { + pub fn get_entities_with(&self, components: Vec) -> Vec { self.scene.get_entities_with(components) } @@ -233,7 +235,7 @@ impl App { } /// Returns whether an entity has the given component. - pub fn has(&self, entity_id: usize) -> bool { + pub fn has(&self, entity_id: EntityId) -> bool { self.scene.has::(entity_id) } @@ -243,7 +245,7 @@ impl App { } /// Spawns a prefab with the given name. - pub fn spawn_prefab(&mut self, name: &str) -> Option { + pub fn spawn_prefab(&mut self, name: &str) -> Option { self.scene.spawn_prefab(name) } diff --git a/crates/comet_ecs/component_derive/src/lib.rs b/crates/comet_ecs/component_derive/src/lib.rs index 79355e9..44e6d6d 100755 --- a/crates/comet_ecs/component_derive/src/lib.rs +++ b/crates/comet_ecs/component_derive/src/lib.rs @@ -27,6 +27,13 @@ pub fn component_derive(input: TokenStream) -> TokenStream { } }); + let clone_fields = fields.iter().map(|field| { + let field_name = &field.ident; + quote! { + #field_name: self.#field_name.clone() + } + }); + let default_fields = if let Data::Struct(data) = &input.data { match &data.fields { Fields::Named(fields) => fields @@ -83,13 +90,11 @@ pub fn component_derive(input: TokenStream) -> TokenStream { impl Clone for #name { fn clone(&self) -> Self { Self { - ..*self + #(#clone_fields),* } } } - impl Copy for #name {} - impl std::fmt::Debug for #name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct(stringify!(#name)) diff --git a/crates/comet_ecs/src/archetypes.rs b/crates/comet_ecs/src/archetypes.rs index c6ccd11..7736ac7 100755 --- a/crates/comet_ecs/src/archetypes.rs +++ b/crates/comet_ecs/src/archetypes.rs @@ -1,9 +1,9 @@ use comet_structs::ComponentSet; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct Archetypes { - archetypes: HashMap>, + archetypes: HashMap>, } impl Archetypes { @@ -13,27 +13,31 @@ impl Archetypes { } } - pub fn component_sets(&self) -> Vec { - self.archetypes.keys().cloned().collect() + pub fn component_sets(&self) -> impl Iterator { + self.archetypes.keys() } pub fn create_archetype(&mut self, components: ComponentSet) { - self.archetypes.insert(components, HashSet::new()); + self.archetypes.entry(components).or_insert_with(Vec::new); } - pub fn get_archetype(&self, components: &ComponentSet) -> Option<&HashSet> { + pub fn get_archetype(&self, components: &ComponentSet) -> Option<&Vec> { self.archetypes.get(components) } pub fn add_entity_to_archetype(&mut self, components: &ComponentSet, entity: u32) { if let Some(archetype) = self.archetypes.get_mut(components) { - archetype.insert(entity); + if !archetype.iter().any(|&e| e == entity) { + archetype.push(entity); + } } } pub fn remove_entity_from_archetype(&mut self, components: &ComponentSet, entity: u32) { if let Some(archetype) = self.archetypes.get_mut(components) { - archetype.retain(|&id| id != entity); + if let Some(pos) = archetype.iter().position(|&id| id == entity) { + archetype.swap_remove(pos); + } } } diff --git a/crates/comet_ecs/src/component.rs b/crates/comet_ecs/src/component.rs index ab17c9b..177f31e 100755 --- a/crates/comet_ecs/src/component.rs +++ b/crates/comet_ecs/src/component.rs @@ -5,6 +5,7 @@ // They are intended to work with the base suite of systems provided by the engine. use crate::math::{v2, v3}; use crate::{Entity, Scene}; +use comet_log::*; use comet_colors::Color as ColorTrait; use comet_math::m4; use component_derive::Component; @@ -110,10 +111,13 @@ pub struct Transform3D { // # TRAITS # // ################################################## -pub trait Component: Send + Sync + PartialEq + Default + 'static { +pub trait Component: Send + Sync + 'static { fn new() -> Self where - Self: Sized; + Self: Sized + Default, + { + Default::default() + } fn type_id() -> std::any::TypeId { std::any::TypeId::of::() @@ -136,7 +140,7 @@ pub trait Render { } pub trait Camera { - fn get_visible_entities(&self, camera_position: Position2D, scene: Scene) -> Vec; + fn get_visible_entities(&self, camera_position: &Position2D, scene: &Scene) -> Vec; fn get_projection_matrix(&self) -> m4; } @@ -246,8 +250,8 @@ impl Rectangle2D { } } - pub fn position(&self) -> Position2D { - self.position + pub fn position(&self) -> &Position2D { + &self.position } pub fn set_position(&mut self, position: Position2D) { @@ -417,7 +421,7 @@ impl Camera2D { self.priority = priority; } - pub fn in_view_frustum(&self, camera_pos: Position2D, entity: Position2D) -> bool { + pub fn in_view_frustum(&self, camera_pos: &Position2D, entity: &Position2D) -> bool { let left = camera_pos.x() - self.zoom; let right = camera_pos.x() + self.zoom; let bottom = camera_pos.y() - self.zoom; @@ -428,18 +432,19 @@ impl Camera2D { } impl Camera for Camera2D { - fn get_visible_entities(&self, camera_position: Position2D, scene: Scene) -> Vec { + fn get_visible_entities(&self, camera_position: &Position2D, scene: &Scene) -> Vec { let entities = scene.entities(); let mut visible_entities = Vec::new(); for entity in entities { - if self.in_view_frustum( - camera_position, - *scene - .get_component::(*entity.clone().unwrap().id() as usize) - .unwrap() - .position(), - ) { - visible_entities.push(entity.clone().unwrap()); + if let Some(ent) = entity.clone() { + let id = ent.id(); + if let Some(transform) = scene.get_component::(id) { + if self.in_view_frustum(camera_position, transform.position()) { + visible_entities.push(ent); + } + } else { + error!("Entity {} missing Transform2D", id.index); + } } } visible_entities @@ -498,7 +503,7 @@ impl Text { } pub fn color(&self) -> Color { - self.color + self.color.clone() } pub fn set_visibility(&mut self, visibility: bool) { diff --git a/crates/comet_ecs/src/entity.rs b/crates/comet_ecs/src/entity.rs index e906d90..f74cb58 100755 --- a/crates/comet_ecs/src/entity.rs +++ b/crates/comet_ecs/src/entity.rs @@ -1,32 +1,46 @@ use bit_set::BitSet; +/// Handle used to reference entities safely. Contains an index into the entity +/// storage and a generation counter to detect stale handles. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct EntityId { + pub index: u32, + pub gen: u32, +} + +impl Default for EntityId { + fn default() -> Self { + Self { index: 0, gen: 0 } + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Entity { - id: u32, - components: BitSet + id: EntityId, + components: BitSet, } impl Entity { - pub(crate) fn new(id: u32) -> Self { - Self { - id, - components: BitSet::new() - } - } + pub(crate) fn new(index: u32, gen: u32) -> Self { + Self { + id: EntityId { index, gen }, + components: BitSet::new(), + } + } - pub fn id(&self) -> &u32 { - &self.id - } + pub fn id(&self) -> EntityId { + self.id + } - pub(crate) fn add_component(&mut self, component_index: usize) { - self.components.insert(component_index); - } + pub(crate) fn add_component(&mut self, component_index: usize) { + self.components.insert(component_index); + } - pub(crate) fn remove_component(&mut self, component_index: usize) { - self.components.remove(component_index); - } + pub(crate) fn remove_component(&mut self, component_index: usize) { + self.components.remove(component_index); + } - pub(crate) fn get_components(&self) -> &BitSet { - &self.components - } + pub(crate) fn get_components(&self) -> &BitSet { + &self.components + } } diff --git a/crates/comet_ecs/src/prefabs.rs b/crates/comet_ecs/src/prefabs.rs index 5fe8698..b6f1e48 100644 --- a/crates/comet_ecs/src/prefabs.rs +++ b/crates/comet_ecs/src/prefabs.rs @@ -1,6 +1,6 @@ use comet_structs::FlatMap; -pub type PrefabFactory = fn(&mut crate::Scene) -> usize; +pub type PrefabFactory = fn(&mut crate::Scene) -> crate::EntityId; pub(crate) struct PrefabManager { pub(crate) prefabs: FlatMap, @@ -26,8 +26,8 @@ impl PrefabManager { macro_rules! register_prefab { ($scene:expr, $name:expr, $($component:expr),* $(,)?) => { { - fn prefab_factory(scene: &mut $crate::Scene) -> usize { - let entity = scene.new_entity() as usize; + fn prefab_factory(scene: &mut $crate::Scene) -> $crate::EntityId { + let entity = scene.new_entity(); $( scene.add_component(entity, $component); )* diff --git a/crates/comet_ecs/src/scene.rs b/crates/comet_ecs/src/scene.rs index 25c3c40..4d76542 100755 --- a/crates/comet_ecs/src/scene.rs +++ b/crates/comet_ecs/src/scene.rs @@ -1,6 +1,6 @@ use crate::archetypes::Archetypes; use crate::prefabs::PrefabManager; -use crate::{Component, Entity, IdQueue}; +use crate::{Component, Entity, EntityId, IdQueue}; use comet_log::*; use comet_structs::*; use std::any::TypeId; @@ -8,6 +8,7 @@ use std::any::TypeId; pub struct Scene { id_queue: IdQueue, next_id: u32, + generations: Vec, entities: Vec>, components: ComponentStorage, archetypes: Archetypes, @@ -19,6 +20,7 @@ impl Scene { Self { id_queue: IdQueue::new(), next_id: 0, + generations: Vec::new(), entities: Vec::new(), components: ComponentStorage::new(), archetypes: Archetypes::new(), @@ -43,48 +45,78 @@ impl Scene { } } + fn is_alive(&self, id: EntityId) -> bool { + self.generations + .get(id.index as usize) + .is_some_and(|g| *g == id.gen) + && self + .entities + .get(id.index as usize) + .is_some_and(|e| e.is_some()) + } + /// Retuns the `Vec` of `Option` which contains all the entities in the current Scene. pub fn entities(&self) -> &Vec> { &self.entities } /// Creates a new entity and returns its ID. - pub fn new_entity(&mut self) -> u32 { - let id = self.next_id; - if (self.next_id as usize) >= self.entities.len() { - self.entities.push(Some(Entity::new(self.next_id))); - self.get_next_id(); - info!("Created entity! ID: {}", id); - return id; + pub fn new_entity(&mut self) -> EntityId { + let index = self.next_id; + let gen = if (index as usize) >= self.generations.len() { + self.generations.push(0); + 0 + } else { + self.generations[index as usize] + }; + + if (index as usize) >= self.entities.len() { + self.entities.push(Some(Entity::new(index, gen))); + } else { + self.entities[index as usize] = Some(Entity::new(index, gen)); } - self.entities[self.next_id as usize] = Some(Entity::new(self.next_id)); + + let id = EntityId { index, gen }; self.get_next_id(); - info!("Created entity! ID: {}", id); + info!("Created entity! ID: {} (gen {})", id.index, id.gen); id } /// Gets an immutable reference to an entity by its ID. - pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> { - self.entities.get(entity_id).unwrap().as_ref() + pub fn get_entity(&self, entity_id: EntityId) -> Option<&Entity> { + if !self.is_alive(entity_id) { + return None; + } + self.entities + .get(entity_id.index as usize) + .and_then(|e| e.as_ref()) } /// Gets a mutable reference to an entity by its ID. - pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> { - self.entities.get_mut(entity_id).unwrap().as_mut() + pub fn get_entity_mut(&mut self, entity_id: EntityId) -> Option<&mut Entity> { + if !self.is_alive(entity_id) { + return None; + } + self.entities + .get_mut(entity_id.index as usize) + .and_then(|e| e.as_mut()) } /// Deletes an entity by its ID. - pub fn delete_entity(&mut self, entity_id: usize) { - self.remove_entity_from_archetype_subsets( - entity_id as u32, - self.get_component_set(entity_id), - ); - self.entities[entity_id] = None; - info!("Deleted entity! ID: {}", entity_id); - for (_, value) in self.components.iter_mut() { - value.remove::(entity_id); + pub fn delete_entity(&mut self, entity_id: EntityId) { + if !self.is_alive(entity_id) { + return; } - self.id_queue.sorted_enqueue(entity_id as u32); + + let idx = entity_id.index as usize; + self.remove_entity_from_archetype(entity_id.index, self.get_component_set(idx)); + self.entities[idx] = None; + info!("Deleted entity! ID: {}", entity_id.index); + self.components.remove_entity(idx); + if let Some(gen) = self.generations.get_mut(idx) { + *gen = gen.wrapping_add(1); + } + self.id_queue.sorted_enqueue(entity_id.index); self.get_next_id(); } @@ -107,24 +139,6 @@ impl Scene { } } - fn get_keys(&self, components: ComponentSet) -> Vec { - let component_sets = self.archetypes.component_sets(); - component_sets - .iter() - .enumerate() - .filter_map(|(i, &ref elem)| { - if elem.is_subset(&components) { - Some(i) - } else { - None - } - }) - .collect::>() - .iter() - .map(|index| component_sets[*index].clone()) - .collect::>() - } - fn add_entity_to_archetype(&mut self, entity_id: u32, components: ComponentSet) { self.archetypes .add_entity_to_archetype(&components, entity_id); @@ -135,18 +149,6 @@ impl Scene { .remove_entity_from_archetype(&components, entity_id); } - fn remove_entity_from_archetype_subsets(&mut self, entity_id: u32, components: ComponentSet) { - let keys = self.get_keys(components); - - for key in keys { - self.remove_entity_from_archetype(entity_id, key.clone()); - if self.archetypes.get_archetype(&key).unwrap().len() == 0 { - self.archetypes.remove_archetype(&key); - } - } - info!("Removed entity {} from all archetypes!", entity_id); - } - fn get_component_set(&self, entity_id: usize) -> ComponentSet { let components = match self.entities.get(entity_id) { Some(cmp) => match cmp.as_ref() { @@ -192,120 +194,152 @@ impl Scene { /// Adds a component to an entity by its ID and an instance of the component. /// Overwrites the previous component if another component of the same type is added. - pub fn add_component(&mut self, entity_id: usize, component: C) { - let old_component_set = self.get_component_set(entity_id); - if !old_component_set.to_vec().is_empty() { - self.remove_entity_from_archetype_subsets(entity_id as u32, old_component_set); + pub fn add_component(&mut self, entity_id: EntityId, component: C) { + if !self.is_alive(entity_id) { + error!( + "Attempted to add component {} to dead entity {}", + C::type_name(), + entity_id.index + ); + return; } - self.components.set_component(entity_id, component); - let component_index = self + let old_component_set = self.get_component_set(entity_id.index as usize); + if !old_component_set.to_vec().is_empty() { + self.remove_entity_from_archetype(entity_id.index, old_component_set); + } + + self.components + .set_component(entity_id.index as usize, component); + if let Some(component_index) = self .components .keys() - .iter_mut() + .iter() .position(|x| *x == C::type_id()) - .unwrap(); - self.get_entity_mut(entity_id) - .unwrap() - .add_component(component_index); + { + if let Some(entity) = self.get_entity_mut(entity_id) { + entity.add_component(component_index); + } else { + error!( + "Attempted to add component to non-existent entity {}", + entity_id.index + ); + } + } else { + error!( + "Component {} not registered, cannot add to entity {}", + C::type_name(), + entity_id.index + ); + } - let new_component_set = self.get_component_set(entity_id); + let new_component_set = self.get_component_set(entity_id.index as usize); if !self.archetypes.contains_archetype(&new_component_set) { self.create_archetype(new_component_set.clone()); } - let subsets = ComponentSet::compute_subsets_up_to_size_3(new_component_set.to_vec()); - - for subset in subsets { - if !self.archetypes.contains_archetype(&subset) { - self.create_archetype(subset.clone()); - } - - self.add_entity_to_archetype(entity_id as u32, subset); - } + self.add_entity_to_archetype(entity_id.index, new_component_set); info!( "Added component {} to entity {}!", C::type_name(), - entity_id + entity_id.index ); } - pub fn remove_component(&mut self, entity_id: usize) { - let old_component_set = self.get_component_set(entity_id); - self.remove_entity_from_archetype_subsets(entity_id as u32, old_component_set); + pub fn remove_component(&mut self, entity_id: EntityId) { + if !self.is_alive(entity_id) { + return; + } + let old_component_set = self.get_component_set(entity_id.index as usize); + self.remove_entity_from_archetype(entity_id.index, old_component_set); - self.components.remove_component::(entity_id); - let component_index = self + self.components + .remove_component::(entity_id.index as usize); + if let Some(component_index) = self .components .keys() .iter() .position(|x| *x == C::type_id()) - .unwrap(); - self.get_entity_mut(entity_id) - .unwrap() - .remove_component(component_index); + { + if let Some(entity) = self.get_entity_mut(entity_id) { + entity.remove_component(component_index); + } + } - let new_component_set = self.get_component_set(entity_id); + let new_component_set = self.get_component_set(entity_id.index as usize); if !new_component_set.to_vec().is_empty() { if !self.archetypes.contains_archetype(&new_component_set) { self.create_archetype(new_component_set.clone()); } - let subsets = ComponentSet::compute_subsets_up_to_size_3(new_component_set.to_vec()); - - for subset in subsets { - if !self.archetypes.contains_archetype(&subset) { - self.create_archetype(subset.clone()); - } - - self.add_entity_to_archetype(entity_id as u32, subset); - } + self.add_entity_to_archetype(entity_id.index, new_component_set); } info!( "Removed component {} from entity {}!", C::type_name(), - entity_id + entity_id.index ); } /// Returns a reference to a component of an entity by its ID. - pub fn get_component(&self, entity_id: usize) -> Option<&C> { - self.components.get_component::(entity_id) + pub fn get_component(&self, entity_id: EntityId) -> Option<&C> { + if !self.is_alive(entity_id) { + return None; + } + self.components + .get_component::(entity_id.index as usize) } pub fn get_component_mut( &mut self, - entity_id: usize, + entity_id: EntityId, ) -> Option<&mut C> { - self.components.get_component_mut::(entity_id) + if !self.is_alive(entity_id) { + return None; + } + self.components + .get_component_mut::(entity_id.index as usize) } - pub fn has(&self, entity_id: usize) -> bool { - self.components.get_component::(entity_id).is_some() + pub fn has(&self, entity_id: EntityId) -> bool { + self.is_alive(entity_id) + && self + .components + .get_component::(entity_id.index as usize) + .is_some() } /// Returns a list of entities that have the given components. - pub fn get_entities_with(&self, components: Vec) -> Vec { + pub fn get_entities_with(&self, components: Vec) -> Vec { let component_set = ComponentSet::from_ids(components); - if component_set.size() > 3 { - error!("An entity query should only contain at most 3 different components!"); - return Vec::new(); + let mut result = Vec::new(); + + for archetype_set in self.archetypes.component_sets() { + if component_set.is_subset(archetype_set) { + if let Some(entities) = self.archetypes.get_archetype(archetype_set) { + for index in entities.iter() { + if let Some(gen) = self.generations.get(*index as usize) { + if self + .entities + .get(*index as usize) + .is_some_and(|e| e.is_some()) + { + result.push(EntityId { + index: *index, + gen: *gen, + }); + } + } + } + } + } } - if self.archetypes.contains_archetype(&component_set) { - return self - .archetypes - .get_archetype(&component_set) - .unwrap() - .clone() - .iter() - .map(|x| *x as usize) - .collect(); - } - Vec::new() + + result } /// Deletes all entities that have the given components. @@ -317,14 +351,31 @@ impl Scene { } /// Iterates over all entities that have the two given components and calls the given function. - pub fn foreach(&mut self, func: fn(&mut C, &mut K)) { - let entities = self.get_entities_with(vec![C::type_id(), K::type_id()]); - for entity in entities { - let c_ptr = self.get_component_mut::(entity).unwrap() as *mut C; - let k_ptr = self.get_component_mut::(entity).unwrap() as *mut K; + pub fn foreach(&mut self, mut func: impl FnMut(&mut C, &mut K)) { + if std::any::TypeId::of::() == std::any::TypeId::of::() { + error!("foreach called with identical component types"); + return; + } - unsafe { - func(&mut *c_ptr, &mut *k_ptr); + let required = ComponentSet::from_ids(vec![C::type_id(), K::type_id()]); + let (c_set, k_set) = self + .components + .get_two_mut(&C::type_id(), &K::type_id()); + + if let (Some(c_store), Some(k_store)) = (c_set, k_set) { + for archetype_set in self.archetypes.component_sets() { + if required.is_subset(archetype_set) { + if let Some(entities) = self.archetypes.get_archetype(archetype_set) { + for &idx in entities { + let idx = idx as usize; + if let (Some(c), Some(k)) = + (c_store.get_mut::(idx), k_store.get_mut::(idx)) + { + func(c, k); + } + } + } + } } } } @@ -335,7 +386,7 @@ impl Scene { } /// Spawns a prefab with the given name. - pub fn spawn_prefab(&mut self, name: &str) -> Option { + pub fn spawn_prefab(&mut self, name: &str) -> Option { if self.prefabs.has_prefab(name) { if let Some(factory) = self.prefabs.prefabs.get(&name.to_string()) { let factory = *factory; // Copy the function pointer diff --git a/crates/comet_renderer/src/camera.rs b/crates/comet_renderer/src/camera.rs index 2e70adf..77f3045 100644 --- a/crates/comet_renderer/src/camera.rs +++ b/crates/comet_renderer/src/camera.rs @@ -18,7 +18,11 @@ impl CameraManager { self.cameras.get(self.active_camera).unwrap() } - pub fn update_from_scene(&mut self, scene: &comet_ecs::Scene, camera_entities: Vec) { + pub fn update_from_scene( + &mut self, + scene: &comet_ecs::Scene, + camera_entities: Vec, + ) { self.cameras.clear(); let mut cameras_with_priority: Vec<(RenderCamera, u8)> = Vec::new(); diff --git a/crates/comet_renderer/src/renderer2d.rs b/crates/comet_renderer/src/renderer2d.rs index 1601295..91ffe06 100644 --- a/crates/comet_renderer/src/renderer2d.rs +++ b/crates/comet_renderer/src/renderer2d.rs @@ -11,6 +11,7 @@ use comet_math::{m4, v2}; use comet_resources::{ font::Font, graphic_resource_manager::GraphicResourceManager, texture_atlas::*, Texture, Vertex, }; +use std::collections::HashSet; use std::sync::Arc; use wgpu::util::DeviceExt; use winit::{dpi::PhysicalSize, window::Window}; @@ -61,6 +62,7 @@ pub struct Renderer2D<'a> { resource_manager: GraphicResourceManager, camera_manager: CameraManager, render_passes: Vec, + cached_render_entities: Vec, last_frame_time: std::time::Instant, delta_time: f32, } @@ -744,16 +746,45 @@ impl<'a> Renderer2D<'a> { return; } - let mut entities = scene.get_entities_with(vec![ + let unsorted_entities = scene.get_entities_with(vec![ comet_ecs::Transform2D::type_id(), comet_ecs::Render2D::type_id(), ]); - entities.sort_by(|&a, &b| { - let ra = scene.get_component::(a).unwrap(); - let rb = scene.get_component::(b).unwrap(); - ra.draw_index().cmp(&rb.draw_index()) - }); + let mut dirty_sort = self.cached_render_entities.len() != unsorted_entities.len(); + + if !dirty_sort { + let unsorted_set: HashSet = + unsorted_entities.iter().copied().collect(); + dirty_sort = self + .cached_render_entities + .iter() + .any(|e| !unsorted_set.contains(e)); + } + + if !dirty_sort { + dirty_sort = !self.cached_render_entities.windows(2).all(|w| { + let a = scene + .get_component::(w[0]) + .map(|r| r.draw_index()); + let b = scene + .get_component::(w[1]) + .map(|r| r.draw_index()); + matches!((a, b), (Some(da), Some(db)) if da <= db) + }); + } + + if dirty_sort { + let mut entities = unsorted_entities; + entities.sort_by(|&a, &b| { + let ra = scene.get_component::(a).unwrap(); + let rb = scene.get_component::(b).unwrap(); + ra.draw_index().cmp(&rb.draw_index()) + }); + self.cached_render_entities = entities; + } + + let entities = self.cached_render_entities.clone(); let texts = scene.get_entities_with(vec![ comet_ecs::Transform2D::type_id(), @@ -922,7 +953,7 @@ impl<'a> Renderer2D<'a> { } } - fn setup_camera(&mut self, scene: &comet_ecs::Scene, cameras: Vec) { + fn setup_camera(&mut self, scene: &comet_ecs::Scene, cameras: Vec) { if cameras.is_empty() { return; } @@ -1012,6 +1043,7 @@ impl<'a> Renderer for Renderer2D<'a> { resource_manager: GraphicResourceManager::new(), camera_manager: CameraManager::new(), render_passes: Vec::new(), + cached_render_entities: Vec::new(), last_frame_time: std::time::Instant::now(), delta_time: 0.0, } diff --git a/crates/comet_structs/src/column.rs b/crates/comet_structs/src/column.rs index b6969b2..6b8ee1a 100644 --- a/crates/comet_structs/src/column.rs +++ b/crates/comet_structs/src/column.rs @@ -272,6 +272,33 @@ impl Column { } } + /// Remove an element without knowing its concrete type. Drops in place. + pub fn remove_any(&mut self, index: usize) { + if index >= self.data.len() { + return; + } + unsafe { + let ptr = self.data.swap_remove_and_forget_unchecked(index); + (self.data.drop)(ptr); + } + } + + /// Overwrites an existing element in place, dropping the previous value. + pub fn set(&mut self, index: usize, item: T) -> Option<()> { + assert_eq!(TypeId::of::(), TypeId::of::(), "Type mismatch"); + if index >= self.data.len() { + return None; + } + + unsafe { + let ptr = self.data.get_unchecked_mut(index) as *mut T; + ptr::drop_in_place(ptr); + ptr::write(ptr, item); + } + + Some(()) + } + pub fn swap(&mut self, index1: usize, index2: usize) { assert!( index1 < self.data.len() && index2 < self.data.len(), diff --git a/crates/comet_structs/src/component_storage.rs b/crates/comet_structs/src/component_storage.rs index 3216bda..017a776 100644 --- a/crates/comet_structs/src/component_storage.rs +++ b/crates/comet_structs/src/component_storage.rs @@ -55,4 +55,11 @@ impl ComponentStorage { None } } + + /// Removes all components belonging to the given entity index. + pub fn remove_entity(&mut self, index: usize) { + for (_, value) in self.iter_mut() { + value.remove_any(index); + } + } } diff --git a/crates/comet_structs/src/componet_set.rs b/crates/comet_structs/src/componet_set.rs index 0d2a816..e37cf1f 100644 --- a/crates/comet_structs/src/componet_set.rs +++ b/crates/comet_structs/src/componet_set.rs @@ -67,6 +67,10 @@ impl ComponentSet { self.set.is_subset(&other.set) } + pub fn is_superset(&self, other: &ComponentSet) -> bool { + self.set.is_superset(&other.set) + } + pub fn to_vec(&self) -> Vec { self.set.iter().cloned().collect() } diff --git a/crates/comet_structs/src/flat_map.rs b/crates/comet_structs/src/flat_map.rs index 1b0edef..49524c8 100644 --- a/crates/comet_structs/src/flat_map.rs +++ b/crates/comet_structs/src/flat_map.rs @@ -44,6 +44,39 @@ impl FlatMap { } } + pub fn get_two_mut(&mut self, a: &K, b: &K) -> (Option<&mut V>, Option<&mut V>) { + if a == b { + let first = self.get_mut(a); + return (first, None); + } + + let mut first_index = None; + let mut second_index = None; + + for (idx, (key, _)) in self.map.iter().enumerate() { + if key == a { + first_index = Some(idx); + } else if key == b { + second_index = Some(idx); + } + } + + match (first_index, second_index) { + (Some(i), Some(j)) => { + if i < j { + let (left, right) = self.map.split_at_mut(j); + (Some(&mut left[i].1), Some(&mut right[0].1)) + } else { + let (left, right) = self.map.split_at_mut(i); + (Some(&mut right[0].1), Some(&mut left[j].1)) + } + } + (Some(i), None) => (self.map.get_mut(i).map(|pair| &mut pair.1), None), + (None, Some(j)) => (None, self.map.get_mut(j).map(|pair| &mut pair.1)), + _ => (None, None), + } + } + pub fn contains(&self, key: &K) -> bool { self.map.iter().any(|node| node.0 == *key) } @@ -51,4 +84,4 @@ impl FlatMap { pub fn iter_mut(&mut self) -> impl Iterator { self.map.iter_mut() } -} \ No newline at end of file +} diff --git a/crates/comet_structs/src/sparse_set.rs b/crates/comet_structs/src/sparse_set.rs index bd8527d..d5bbcb1 100644 --- a/crates/comet_structs/src/sparse_set.rs +++ b/crates/comet_structs/src/sparse_set.rs @@ -28,6 +28,12 @@ impl SparseSet { } if let Some(page_vec) = &mut self.sparse[page] { + // If there is already a mapping, overwrite the existing dense value instead of pushing. + if let Some(sparse_index) = page_vec[index % self.page_size] { + let _ = self.dense.set::(sparse_index, value); + return; + } + page_vec[index % self.page_size] = Some(self.dense.data.len()); } @@ -69,6 +75,64 @@ impl SparseSet { None } + /// Removes an element by external index without knowing its type. Returns true if something was removed. + pub fn remove_any(&mut self, index: usize) -> bool { + if let Some(page_vec) = self + .sparse + .get(index / self.page_size) + .and_then(|x| x.as_ref()) + { + if let Some(sparse_index) = page_vec + .get(index % self.page_size) + .and_then(|x| x.as_ref()) + { + let dense_index = *sparse_index; + let last_dense_index = self.dense.data.len() - 1; + + if dense_index != last_dense_index { + if let Some(ext_index) = self.find_external_index(last_dense_index) { + if let Some(page_vec) = self + .sparse + .get_mut(ext_index / self.page_size) + .and_then(|x| x.as_mut()) + { + page_vec[ext_index % self.page_size] = Some(dense_index); + } + } + self.dense.swap(dense_index, last_dense_index); + } + + if let Some(page_vec) = self + .sparse + .get_mut(index / self.page_size) + .and_then(|x| x.as_mut()) + { + page_vec[index % self.page_size] = None; + } + + self.dense.remove_any(dense_index); + return true; + } + } + false + } + + /// Finds the external index that maps to a given dense index. + fn find_external_index(&self, dense_index: usize) -> Option { + for (page_idx, page_opt) in self.sparse.iter().enumerate() { + if let Some(page) = page_opt { + for (offset, entry) in page.iter().enumerate() { + if let Some(idx) = entry { + if *idx == dense_index { + return Some(page_idx * self.page_size + offset); + } + } + } + } + } + None + } + pub fn get(&self, index: usize) -> Option<&T> { if let Some(page_vec) = self .sparse