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/component.rs b/crates/comet_ecs/src/component.rs index ab17c9b..ad6dc39 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,17 @@ 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()); + let id = *entity.clone().unwrap().id() as usize; + if let Some(transform) = scene.get_component::(id) { + if self.in_view_frustum(camera_position, transform.position()) { + visible_entities.push(entity.clone().unwrap()); + } + } else { + error!("Entity {} missing Transform2D", id); } } visible_entities @@ -498,7 +501,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/scene.rs b/crates/comet_ecs/src/scene.rs index 25c3c40..32218c3 100755 --- a/crates/comet_ecs/src/scene.rs +++ b/crates/comet_ecs/src/scene.rs @@ -65,20 +65,17 @@ impl Scene { /// 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() + self.entities.get(entity_id).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() + self.entities.get_mut(entity_id).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.remove_entity_from_archetype(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() { @@ -107,24 +104,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 +114,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() { @@ -195,19 +162,28 @@ impl Scene { 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); + self.remove_entity_from_archetype(entity_id as u32, old_component_set); } self.components.set_component(entity_id, component); - let component_index = self + 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); + } + } else { + error!( + "Component {} not registered, cannot add to entity {}", + C::type_name(), + entity_id + ); + } let new_component_set = self.get_component_set(entity_id); @@ -215,15 +191,7 @@ impl Scene { 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 as u32, new_component_set); info!( "Added component {} to entity {}!", @@ -234,18 +202,19 @@ impl Scene { 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); + self.remove_entity_from_archetype(entity_id as u32, old_component_set); self.components.remove_component::(entity_id); - let component_index = self + 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); @@ -254,15 +223,7 @@ impl Scene { 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 as u32, new_component_set); } info!( @@ -291,21 +252,17 @@ impl Scene { /// Returns a list of entities that have the given components. 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) { + result.extend(entities.iter().map(|x| *x as usize)); + } + } } - 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 +274,23 @@ 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)) { + 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; + } + 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; + let (c_set, k_set) = self + .components + .get_two_mut(&C::type_id(), &K::type_id()); - unsafe { - func(&mut *c_ptr, &mut *k_ptr); + let c_opt = c_set.and_then(|set| set.get_mut::(entity)); + let k_opt = k_set.and_then(|set| set.get_mut::(entity)); + + if let (Some(c), Some(k)) = (c_opt, k_opt) { + func(c, k); } } } diff --git a/crates/comet_renderer/src/renderer2d.rs b/crates/comet_renderer/src/renderer2d.rs index 1601295..1bef6b5 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,44 @@ 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(), @@ -1012,6 +1042,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..6e2eafc 100644 --- a/crates/comet_structs/src/column.rs +++ b/crates/comet_structs/src/column.rs @@ -272,6 +272,22 @@ impl Column { } } + /// 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/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..8000ba7 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()); }