mirror of
https://github.com/lisk77/comet.git
synced 2025-10-23 21:38:50 +00:00
initial commit
This commit is contained in:
commit
6154c72b0e
55 changed files with 9481 additions and 0 deletions
12
crates/comet_ecs/Cargo.toml
Normal file
12
crates/comet_ecs/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "comet_ecs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bit-set = "0.8.0"
|
||||
component_derive = { path = "./component_derive" }
|
||||
comet_math = { path = "../comet_math" }
|
||||
comet_resources = { path = "../comet_resources" }
|
||||
comet_log = { path = "../comet_log" }
|
||||
chrono = "0.4"
|
11
crates/comet_ecs/component_derive/Cargo.toml
Normal file
11
crates/comet_ecs/component_derive/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "component_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "2.0"
|
||||
quote = "1.0"
|
129
crates/comet_ecs/component_derive/src/lib.rs
Normal file
129
crates/comet_ecs/component_derive/src/lib.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput, Data, Fields};
|
||||
|
||||
// This is the procedural macro for `derive(MyTrait)`
|
||||
#[proc_macro_derive(Component)]
|
||||
pub fn my_trait_derive(input: TokenStream) -> TokenStream {
|
||||
// Parse the input tokens into a syntax tree (AST)
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
// Get the name of the struct
|
||||
let name = &input.ident;
|
||||
|
||||
let fields = if let Data::Struct(data) = &input.data {
|
||||
match &data.fields {
|
||||
Fields::Named(fields) => fields.named.iter().collect::<Vec<_>>(),
|
||||
Fields::Unnamed(fields) => fields.unnamed.iter().collect::<Vec<_>>(),
|
||||
Fields::Unit => Vec::new(),
|
||||
}
|
||||
} else {
|
||||
panic!("Component derive macro only works on structs");
|
||||
};
|
||||
|
||||
let field_comparisons = fields.iter().map(|field| {
|
||||
let field_name = &field.ident; // Name of the field
|
||||
quote! {
|
||||
self.#field_name == other.#field_name
|
||||
}
|
||||
});
|
||||
|
||||
let default_fields = if let Data::Struct(data) = &input.data {
|
||||
match &data.fields {
|
||||
Fields::Named(fields) => {
|
||||
// Generate code to assign each field a default value
|
||||
fields.named.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
quote! { #field_name: Default::default() }
|
||||
}).collect::<Vec<_>>()
|
||||
},
|
||||
Fields::Unnamed(fields) => {
|
||||
// Generate default values for tuple structs
|
||||
fields.unnamed.iter().map(|_field| {
|
||||
quote! { Default::default() }
|
||||
}).collect::<Vec<_>>()
|
||||
},
|
||||
Fields::Unit => Vec::new(),
|
||||
}
|
||||
} else {
|
||||
panic!("Default can only be derived for structs");
|
||||
};
|
||||
|
||||
let debug_fields = fields.iter().map(|field| {
|
||||
let field_name = field.ident.as_ref().expect("Expected named fields");
|
||||
quote! {
|
||||
.field(stringify!(#field_name), &self.#field_name)
|
||||
}
|
||||
});
|
||||
|
||||
for field in &fields {
|
||||
if !is_copy_field(&field) {
|
||||
panic!("All fields in the struct must implement Copy");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the implementation of MyTrait for the given struct
|
||||
let expanded = quote! {
|
||||
impl Component for #name {
|
||||
fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<Self>()
|
||||
}
|
||||
|
||||
fn type_name() -> String {
|
||||
std::any::type_name::<Self>().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for #name {
|
||||
fn default() -> Self {
|
||||
// Construct the struct with default values
|
||||
Self {
|
||||
#(#default_fields),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for #name {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
#(#debug_fields)*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for #name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
true #(&& #field_comparisons)*
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Convert the generated code into a TokenStream and return it
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
|
||||
fn is_copy_field(field: &syn::Field) -> bool {
|
||||
// Logic to check if the field type implements Copy (this is simplified)
|
||||
// You might need more sophisticated logic to check the actual type of the field.
|
||||
let field_type = &field.ty;
|
||||
// Implement a check for Copy trait for the field type if needed
|
||||
// Return true if it implements Copy; false otherwise
|
||||
true // Assuming it does, just for simplicity
|
||||
}
|
182
crates/comet_ecs/src/component.rs
Normal file
182
crates/comet_ecs/src/component.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
use std::path::Path;
|
||||
use crate::math::{
|
||||
Vec2,
|
||||
Vec3
|
||||
};
|
||||
use component_derive::Component;
|
||||
|
||||
pub trait Component: Send + Sync + PartialEq + Default + 'static {
|
||||
fn new() -> Self where Self: Sized;
|
||||
|
||||
fn type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<Self>()
|
||||
}
|
||||
|
||||
fn type_name() -> String {
|
||||
std::any::type_name::<Self>().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// ##################################################
|
||||
// # BASIC #
|
||||
// ##################################################
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Position2D {
|
||||
x: f32,
|
||||
y: f32
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Position3D {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Rotation2D {
|
||||
theta: f32
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Rotation3D {
|
||||
theta_x: f32,
|
||||
theta_y: f32,
|
||||
theta_z: f32
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Renderer2D {
|
||||
is_visible: bool,
|
||||
texture: &'static str,
|
||||
scale: f32
|
||||
}
|
||||
|
||||
impl Position2D {
|
||||
pub fn from_vec(vec: Vec2) -> Self {
|
||||
Self {
|
||||
x: vec.x(),
|
||||
y: vec.y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec(&self) -> Vec2 {
|
||||
Vec2::new(
|
||||
self.x,
|
||||
self.y
|
||||
)
|
||||
}
|
||||
|
||||
pub fn x(&self) -> &f32 {
|
||||
&self.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> &f32 {
|
||||
&self.y
|
||||
}
|
||||
|
||||
pub fn set_x(&mut self, new_x: f32) {
|
||||
self.x = new_x;
|
||||
}
|
||||
|
||||
pub fn set_y(&mut self, new_y: f32) {
|
||||
self.y = new_y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Position3D {
|
||||
pub fn from_vec(vec: Vec3) -> Self {
|
||||
Self {
|
||||
x: vec.x(),
|
||||
y: vec.y(),
|
||||
z: vec.z()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec(&self) -> Vec3 {
|
||||
Vec3::new(
|
||||
self.x,
|
||||
self.y,
|
||||
self.z
|
||||
)
|
||||
}
|
||||
|
||||
pub fn x(&self) -> &f32 {
|
||||
&self.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> &f32 {
|
||||
&self.y
|
||||
}
|
||||
|
||||
pub fn z(&self) -> &f32 {
|
||||
&self.z
|
||||
}
|
||||
|
||||
pub fn set_x(&mut self, new_x: f32) {
|
||||
self.x = new_x;
|
||||
}
|
||||
|
||||
pub fn set_y(&mut self, new_y: f32) {
|
||||
self.y = new_y;
|
||||
}
|
||||
|
||||
pub fn set_z(&mut self, new_z: f32) {
|
||||
self.z = new_z
|
||||
}
|
||||
}
|
||||
|
||||
// ##################################################
|
||||
// # BUNDLES #
|
||||
// ##################################################
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Transform2D {
|
||||
position: Position2D,
|
||||
rotation: Rotation2D
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Transform3D {
|
||||
position: Position3D,
|
||||
rotation: Rotation3D
|
||||
}
|
||||
|
||||
impl Transform2D {
|
||||
pub fn position(&self) -> &Position2D {
|
||||
&self.position
|
||||
}
|
||||
|
||||
pub fn position_mut(&mut self) -> &mut Position2D {
|
||||
&mut self.position
|
||||
}
|
||||
|
||||
pub fn rotation(&self) -> &Rotation2D {
|
||||
&self.rotation
|
||||
}
|
||||
|
||||
pub fn rotation_mut(&mut self) -> &mut Rotation2D {
|
||||
&mut self.rotation
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform3D {
|
||||
pub fn position(&self) -> &Position3D {
|
||||
&self.position
|
||||
}
|
||||
|
||||
pub fn position_mut(&mut self) -> &mut Position3D {
|
||||
&mut self.position
|
||||
}
|
||||
|
||||
pub fn rotation(&self) -> &Rotation3D {
|
||||
&self.rotation
|
||||
}
|
||||
|
||||
pub fn rotation_mut(&mut self) -> &mut Rotation3D {
|
||||
&mut self.rotation
|
||||
}
|
||||
}
|
||||
|
||||
|
35
crates/comet_ecs/src/entity.rs
Normal file
35
crates/comet_ecs/src/entity.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use bit_set::BitSet;
|
||||
use crate::ComponentSet;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Entity {
|
||||
id: u32,
|
||||
components: BitSet
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
pub fn new(id: u32) -> Self {
|
||||
let mut components = BitSet::new();
|
||||
components.insert(0);
|
||||
Self {
|
||||
id,
|
||||
components
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &u32 {
|
||||
&self.id
|
||||
}
|
||||
|
||||
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 get_components(&self) -> &BitSet {
|
||||
&self.components
|
||||
}
|
||||
}
|
40
crates/comet_ecs/src/id.rs
Normal file
40
crates/comet_ecs/src/id.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct IdQueue {
|
||||
queue: Vec<u32>
|
||||
}
|
||||
|
||||
impl IdQueue {
|
||||
pub fn new() -> Self {
|
||||
Self { queue: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn from_vec(queue: Vec<u32>) -> Self {
|
||||
Self { queue }
|
||||
}
|
||||
|
||||
pub fn front(&self) -> Option<u32> {
|
||||
Some(self.queue[0])
|
||||
}
|
||||
|
||||
pub fn enqueue(&mut self, id: u32) {
|
||||
self.queue.push(id)
|
||||
}
|
||||
|
||||
pub fn sorted_enqueue(&mut self, id: u32) {
|
||||
self.enqueue(id);
|
||||
self.queue.sort();
|
||||
}
|
||||
|
||||
pub fn dequeue(&mut self) -> Option<u32> {
|
||||
Some(self.queue.remove(0))
|
||||
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.queue.len() == 0
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
self.queue.len() as u32
|
||||
}
|
||||
}
|
13
crates/comet_ecs/src/lib.rs
Normal file
13
crates/comet_ecs/src/lib.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
pub use storage::*;
|
||||
pub use entity::*;
|
||||
pub use component::*;
|
||||
pub use world::*;
|
||||
pub use id::*;
|
||||
pub use component_derive::*;
|
||||
pub use comet_math as math;
|
||||
|
||||
mod storage;
|
||||
mod entity;
|
||||
mod component;
|
||||
mod world;
|
||||
mod id;
|
577
crates/comet_ecs/src/storage.rs
Normal file
577
crates/comet_ecs/src/storage.rs
Normal file
|
@ -0,0 +1,577 @@
|
|||
use crate::{Component};
|
||||
use std::{
|
||||
alloc::{
|
||||
handle_alloc_error,
|
||||
Layout
|
||||
},
|
||||
any::TypeId,
|
||||
collections::{
|
||||
HashMap,
|
||||
HashSet
|
||||
},
|
||||
hash::{
|
||||
DefaultHasher,
|
||||
Hash,
|
||||
Hasher
|
||||
},
|
||||
mem::MaybeUninit,
|
||||
ptr::NonNull
|
||||
};
|
||||
use std::ptr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlobVec {
|
||||
item_layout: Layout,
|
||||
capacity: usize,
|
||||
len: usize,
|
||||
data: NonNull<u8>,
|
||||
swap_scratch: NonNull<u8>,
|
||||
drop: unsafe fn(*mut u8)
|
||||
}
|
||||
|
||||
|
||||
impl BlobVec {
|
||||
pub fn new(item_layout: Layout, drop: unsafe fn(*mut u8), capacity: usize) -> Self {
|
||||
if item_layout.size() == 0 {
|
||||
BlobVec {
|
||||
swap_scratch: NonNull::dangling(),
|
||||
data: NonNull::dangling(),
|
||||
capacity: usize:: MAX,
|
||||
len: 0,
|
||||
item_layout,
|
||||
drop,
|
||||
}
|
||||
}
|
||||
else {
|
||||
let swap_scratch = NonNull::new(unsafe { std::alloc::alloc(item_layout) })
|
||||
.unwrap_or_else(|| handle_alloc_error(item_layout));
|
||||
|
||||
let mut blob_vec = BlobVec {
|
||||
swap_scratch,
|
||||
data: NonNull::dangling(),
|
||||
capacity: 0,
|
||||
len: 0,
|
||||
item_layout,
|
||||
drop,
|
||||
};
|
||||
blob_vec.reserve_exact(capacity);
|
||||
blob_vec
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_exact(&mut self, additional: usize) {
|
||||
let available_space = self.capacity - self.len;
|
||||
if available_space < additional {
|
||||
self.grow_exact(additional - available_space);
|
||||
}
|
||||
}
|
||||
|
||||
fn grow_exact(&mut self, increment: usize) {
|
||||
debug_assert!(self.item_layout.size() != 0);
|
||||
|
||||
let new_capacity = self.capacity + increment;
|
||||
let new_layout =
|
||||
array_layout(&self.item_layout, new_capacity).expect("array layout should be valid");
|
||||
unsafe {
|
||||
let new_data = if self.capacity == 0 {
|
||||
std::alloc::alloc(new_layout)
|
||||
} else {
|
||||
std::alloc::realloc(
|
||||
self.get_ptr().as_ptr(),
|
||||
array_layout(&self.item_layout, self.capacity)
|
||||
.expect("array layout should be valid"),
|
||||
new_layout.size(),
|
||||
)
|
||||
};
|
||||
|
||||
self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));
|
||||
}
|
||||
self.capacity = new_capacity;
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_ptr(&self) -> NonNull<u8> {
|
||||
self.data
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn push_uninit(&mut self) -> usize {
|
||||
self.reserve_exact(1);
|
||||
let index = self.len;
|
||||
self.len += 1;
|
||||
index
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked(&self, index: usize) -> *mut u8 {
|
||||
debug_assert!(index < self.len());
|
||||
self.get_ptr().as_ptr().add(index * self.item_layout.size())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> *mut u8 {
|
||||
debug_assert!(index < self.len());
|
||||
self.get_ptr().as_ptr().add(index * self.item_layout.size())
|
||||
}
|
||||
|
||||
pub unsafe fn push_element<T>(&mut self, element: T) {
|
||||
let index = self.push_uninit();
|
||||
let ptr = self.get_unchecked(index) as *mut T;
|
||||
ptr::write(ptr,element);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
let len = self.len;
|
||||
// We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't
|
||||
// accidentally drop elements twice in the event of a drop impl panicking.
|
||||
self.len = 0;
|
||||
for i in 0..len {
|
||||
unsafe {
|
||||
// NOTE: this doesn't use self.get_unchecked(i) because the debug_assert on index
|
||||
// will panic here due to self.len being set to 0
|
||||
let ptr = self.get_ptr().as_ptr().add(i * self.item_layout.size());
|
||||
(self.drop)(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn swap_remove_and_forget_unchecked(&mut self, index: usize) -> *mut u8 {
|
||||
debug_assert!(index < self.len());
|
||||
let last = self.len - 1;
|
||||
let swap_scratch = self.swap_scratch.as_ptr();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.get_unchecked(index),
|
||||
swap_scratch,
|
||||
self.item_layout.size(),
|
||||
);
|
||||
ptr::copy(
|
||||
self.get_unchecked(last),
|
||||
self.get_unchecked(index),
|
||||
self.item_layout.size(),
|
||||
);
|
||||
self.len -= 1;
|
||||
swap_scratch
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn initialize_unchecked(&mut self, index: usize, value: *mut u8) {
|
||||
debug_assert!(index < self.len());
|
||||
let ptr = self.get_unchecked(index);
|
||||
ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BlobVec {
|
||||
fn drop(&mut self) {
|
||||
self.clear();
|
||||
let array_layout =
|
||||
array_layout(&self.item_layout, self.capacity).expect("array layout should be valid");
|
||||
if array_layout.size() > 0 {
|
||||
unsafe {
|
||||
std::alloc::dealloc(self.get_ptr().as_ptr(), array_layout);
|
||||
std::alloc::dealloc(self.swap_scratch.as_ptr(), self.item_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn array_layout(layout: &Layout, n: usize) -> Option<Layout> {
|
||||
let (array_layout, offset) = repeat_layout(layout, n)?;
|
||||
debug_assert_eq!(layout.size(), offset);
|
||||
Some(array_layout)
|
||||
}
|
||||
|
||||
fn repeat_layout(layout: &Layout, n: usize) -> Option<(Layout, usize)> {
|
||||
let padded_size = layout.size() + padding_needed_for(layout, layout.align());
|
||||
let alloc_size = padded_size.checked_mul(n)?;
|
||||
|
||||
unsafe {
|
||||
Some((
|
||||
Layout::from_size_align_unchecked(alloc_size, layout.align()),
|
||||
padded_size,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
|
||||
let len = layout.size();
|
||||
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
|
||||
len_rounded_up.wrapping_sub(len)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Column {
|
||||
pub data: BlobVec
|
||||
}
|
||||
|
||||
impl Column {
|
||||
pub fn new<T: 'static>(capacity: usize) -> Self {
|
||||
let layout = Layout::new::<T>();
|
||||
let drop_fn = |ptr: *mut u8| unsafe {
|
||||
ptr::drop_in_place(ptr as *mut T);
|
||||
};
|
||||
Self {
|
||||
data: BlobVec::new(layout, drop_fn, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(&self) -> BlobVec {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
pub fn push<T: 'static>(&mut self, item: T) {
|
||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
||||
unsafe {
|
||||
let index = self.data.push_uninit();
|
||||
let ptr = self.data.get_unchecked(index);
|
||||
ptr::write(ptr as *mut T, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
||||
if index >= self.data.len() {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let ptr = self.data.get_unchecked(index);
|
||||
Some(&*(ptr as *const T))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
||||
|
||||
if index >= self.data.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Access the element at the given index
|
||||
unsafe {
|
||||
let ptr = self.data.get_unchecked(index);
|
||||
// Convert the pointer to a mutable reference and return it
|
||||
Some(&mut *(ptr as *mut T))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T> {
|
||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
||||
if index >= self.data.len() {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let ptr = self.data.swap_remove_and_forget_unchecked(index);
|
||||
Some(ptr::read(ptr as *const T))
|
||||
}
|
||||
}
|
||||
|
||||
fn swap(&mut self, index1: usize, index2: usize) {
|
||||
assert!(index1 < self.data.len() && index2 < self.data.len(), "Index out of bounds");
|
||||
|
||||
unsafe {
|
||||
let ptr1 = self.data.get_unchecked(index1);
|
||||
let ptr2 = self.data.get_unchecked(index2);
|
||||
|
||||
let mut temp = MaybeUninit::<u8>::uninit();
|
||||
|
||||
// Swap the elements at index1 and index2
|
||||
ptr::copy_nonoverlapping(ptr1, temp.as_mut_ptr(), self.data.item_layout.size());
|
||||
ptr::copy_nonoverlapping(ptr2, ptr1, self.data.item_layout.size());
|
||||
ptr::copy_nonoverlapping(temp.as_ptr(), ptr2, self.data.item_layout.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterMut<'a, K, V> {
|
||||
keys_iter: std::slice::IterMut<'a, K>,
|
||||
values_iter: std::slice::IterMut<'a, V>,
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
|
||||
type Item = (&'a mut K, &'a mut V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match (self.keys_iter.next(), self.values_iter.next()) {
|
||||
(Some(key), Some(value)) => Some((key, value)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SparseSet {
|
||||
sparse: Vec<Option<usize>>,
|
||||
dense: Column,
|
||||
len: usize
|
||||
}
|
||||
|
||||
impl SparseSet {
|
||||
pub fn new<T: 'static>(capacity: usize) -> Self {
|
||||
Self {
|
||||
sparse: Vec::with_capacity(capacity),
|
||||
dense: Column::new::<T>(capacity),
|
||||
len: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set<T: 'static>(&mut self, index: usize, element: T) {
|
||||
if index >= self.sparse.len() {
|
||||
self.sparse.resize_with(index + 1, || None);
|
||||
}
|
||||
|
||||
if let Some(column_index) = self.sparse[index] {
|
||||
// Explicitly drop the existing component before replacing it
|
||||
unsafe {
|
||||
let existing_ptr = self.dense.data.get_unchecked_mut(column_index) as *mut T;
|
||||
ptr::drop_in_place(existing_ptr);
|
||||
ptr::write(existing_ptr, element);
|
||||
}
|
||||
} else {
|
||||
let column_index = unsafe { self.dense.data.push_uninit() };
|
||||
unsafe {
|
||||
self.dense.data.initialize_unchecked(column_index, &element as *const T as *mut u8);
|
||||
}
|
||||
self.sparse[index] = Some(column_index);
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T> {
|
||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
||||
return None;
|
||||
}
|
||||
|
||||
let column_index = self.sparse[index];
|
||||
let element = unsafe {
|
||||
self.dense.data.swap_remove_and_forget_unchecked(column_index.unwrap())
|
||||
};
|
||||
|
||||
self.sparse[index] = None;
|
||||
self.len -= 1;
|
||||
|
||||
Some(unsafe { ptr::read(element as *const T) })
|
||||
}
|
||||
|
||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.dense.get::<T>(index)
|
||||
}
|
||||
|
||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.dense.get_mut::<T>(index)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ComponentStorage {
|
||||
index_map: HashMap<TypeId, usize>,
|
||||
pub(crate) keys: Vec<TypeId>,
|
||||
components: Vec<SparseSet>
|
||||
}
|
||||
|
||||
impl ComponentStorage {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
index_map: HashMap::new(),
|
||||
keys: Vec::new(),
|
||||
components: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keys(&self) -> &Vec<TypeId> {
|
||||
&self.keys
|
||||
}
|
||||
|
||||
pub fn contains_component(&self, type_id: &TypeId) -> bool {
|
||||
self.keys.contains(type_id)
|
||||
}
|
||||
|
||||
pub fn get<T: Component + 'static>(&self) -> Option<&SparseSet> {
|
||||
self.components.get(*self.index_map.get(&T::type_id()).unwrap())
|
||||
}
|
||||
|
||||
pub fn set<T: Component + 'static>(&mut self, sparse_set: SparseSet) {
|
||||
let _ = self.components.get_mut(*self.index_map.get(&T::type_id()).unwrap());
|
||||
}
|
||||
|
||||
pub fn register_component<T: Component + 'static>(&mut self, capacity: usize) {
|
||||
//self.storage.insert(T::type_id(), SparseSet::new::<T>(capacity));
|
||||
assert!(!self.keys.contains(&T::type_id()), "This component ({}) is already registered!", T::type_name());
|
||||
self.keys.push(T::type_id());
|
||||
self.index_map.insert(T::type_id(), self.keys.len()-1);
|
||||
self.components.push(SparseSet::new::<T>(capacity));
|
||||
}
|
||||
|
||||
pub fn get_component<T: Component + 'static>(&self, entity_id: usize) -> Option<&T> {
|
||||
//self.storage.get(&T::type_id()).unwrap().get::<T>(*entity.id() as usize)
|
||||
self.components.get(*self.index_map.get(&T::type_id()).unwrap()).unwrap().get::<T>(entity_id)
|
||||
}
|
||||
|
||||
pub fn get_component_mut<T: Component + 'static>(&mut self, entity_id: usize) -> Option<&mut T> {
|
||||
self.components.get_mut(*self.index_map.get(&T::type_id()).unwrap()).unwrap().get_mut::<T>(entity_id)
|
||||
}
|
||||
|
||||
pub fn set_component<T: Component + 'static>(&mut self, entity_id: usize, component: T) {
|
||||
let index = *self.index_map.get(&T::type_id()).unwrap();
|
||||
let sparse_set = self.components.get_mut(index).unwrap();
|
||||
|
||||
// Check if a component already exists for this entity
|
||||
if let Some(existing_component) = sparse_set.get_mut::<T>(entity_id) {
|
||||
// Explicitly drop the existing component before overwriting it
|
||||
std::mem::drop(existing_component);
|
||||
}
|
||||
|
||||
// Set the new component
|
||||
sparse_set.set(entity_id, component);
|
||||
}
|
||||
|
||||
pub fn deregister_component<T: Component + 'static>(&mut self) {
|
||||
let type_id = T::type_id();
|
||||
if let Some(&index) = self.index_map.get(&type_id) {
|
||||
// Before removing the SparseSet, ensure all elements are properly dropped
|
||||
let sparse_set = self.components.get_mut(index).unwrap();
|
||||
for i in 0..sparse_set.sparse.len() {
|
||||
if sparse_set.sparse[i].is_some() {
|
||||
sparse_set.remove::<T>(i);
|
||||
}
|
||||
}
|
||||
|
||||
self.components.remove(index);
|
||||
self.index_map.remove(&type_id);
|
||||
self.keys.retain(|&k| k != type_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_component<T: Component + 'static>(&mut self, entity_id: usize) {
|
||||
if let Some(index) = self.index_map.get(&T::type_id()) {
|
||||
let sparse_set = self.components.get_mut(*index).unwrap();
|
||||
sparse_set.remove::<T>(entity_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_dense_list_as_vec<T: Component + Clone + 'static>(&self) -> Option<Vec<T>> {
|
||||
let mut resulting_vec: Vec<T> = Vec::new();
|
||||
|
||||
if let Some(sparse_set) = self.components.get(*self.index_map.get(&T::type_id()).unwrap()) {
|
||||
for i in 0..sparse_set.dense.data.len() {
|
||||
let item: T = sparse_set.dense.get::<T>(i)?.clone();
|
||||
resulting_vec.push(item);
|
||||
}
|
||||
Some(resulting_vec)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, TypeId, SparseSet> {
|
||||
IterMut {
|
||||
keys_iter: self.keys.iter_mut(),
|
||||
values_iter: self.components.iter_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ComponentSet {
|
||||
set: HashSet<TypeId>
|
||||
}
|
||||
|
||||
impl ComponentSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
set: HashSet::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_ids(ids: Vec<TypeId>) -> Self {
|
||||
Self {
|
||||
set: ids.into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_subset(&self, other: &ComponentSet) -> bool {
|
||||
self.set.is_subset(&other.set)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ComponentSet {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let mut types: Vec<TypeId> = self.set.iter().cloned().collect();
|
||||
types.sort();
|
||||
types.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Archetypes {
|
||||
archetypes: HashMap<ComponentSet, Vec<u32>>
|
||||
}
|
||||
|
||||
impl Archetypes {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
archetypes: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn component_sets(&self) -> Vec<ComponentSet> {
|
||||
self.archetypes.keys().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn create_archetype(&mut self, components: ComponentSet) {
|
||||
self.archetypes.insert(components, Vec::new());
|
||||
}
|
||||
|
||||
pub fn get_archetype(&self, components: &ComponentSet) -> Option<&Vec<u32>> {
|
||||
self.archetypes.get(components)
|
||||
}
|
||||
|
||||
pub fn get_archetype_mut(&mut self, components: &ComponentSet) -> Option<&mut Vec<u32>> {
|
||||
self.archetypes.get_mut(components)
|
||||
}
|
||||
|
||||
pub fn add_entity_to_archetype(&mut self, components: &ComponentSet, entity: u32) {
|
||||
if let Some(archetype) = self.archetypes.get_mut(components) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_archetype(&mut self, components: &ComponentSet) {
|
||||
self.archetypes.remove(components);
|
||||
}
|
||||
|
||||
pub fn contains_archetype(&self, components: &ComponentSet) -> bool {
|
||||
self.archetypes.contains_key(components)
|
||||
}
|
||||
}
|
209
crates/comet_ecs/src/world.rs
Normal file
209
crates/comet_ecs/src/world.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
use std::any::TypeId;
|
||||
use bit_set::BitSet;
|
||||
use crate::{
|
||||
Entity,
|
||||
Component,
|
||||
Transform2D,
|
||||
Transform3D,
|
||||
ComponentStorage,
|
||||
SparseSet,
|
||||
IdQueue,
|
||||
Archetypes,
|
||||
ComponentSet
|
||||
};
|
||||
use comet_log::*;
|
||||
|
||||
pub struct World {
|
||||
dimension: String,
|
||||
id_queue: IdQueue,
|
||||
next_id: u32,
|
||||
entities: Vec<Option<Entity>>,
|
||||
components: ComponentStorage,
|
||||
archetypes: Archetypes
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new(dimension: &str) -> Self {
|
||||
let mut component_storage = ComponentStorage::new();
|
||||
match dimension {
|
||||
"2D" => component_storage.register_component::<Transform2D>(0),
|
||||
"3D" => component_storage.register_component::<Transform3D>(0),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Self {
|
||||
dimension: dimension.to_string(),
|
||||
id_queue: IdQueue::new(),
|
||||
next_id: 0,
|
||||
entities: Vec::new(),
|
||||
components: component_storage,
|
||||
archetypes: Archetypes::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_entities(&self) -> u32 {
|
||||
self.entities.len() as u32 - self.id_queue.size()
|
||||
}
|
||||
|
||||
fn get_next_id(&mut self) {
|
||||
if self.id_queue.is_empty() {
|
||||
self.next_id = self.entities.len() as u32;
|
||||
return;
|
||||
}
|
||||
if self.next_id > self.id_queue.front().unwrap() || self.entities[self.next_id as usize] != None {
|
||||
self.next_id = self.id_queue.dequeue().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dimension(&self) -> &String {
|
||||
&self.dimension
|
||||
}
|
||||
|
||||
pub fn id_queue(&self) -> &IdQueue {
|
||||
&self.id_queue
|
||||
}
|
||||
|
||||
pub fn entities(&self) -> &Vec<Option<Entity>> {
|
||||
&self.entities
|
||||
}
|
||||
|
||||
pub fn components(&self) -> &ComponentStorage {
|
||||
&self.components
|
||||
}
|
||||
|
||||
pub fn components_mut(&mut self) -> &mut ComponentStorage {
|
||||
&mut self.components
|
||||
}
|
||||
|
||||
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)));
|
||||
match self.dimension.as_str() {
|
||||
"2D" => self.add_component::<Transform2D>(self.next_id as usize, Transform2D::new()),
|
||||
"3D" => self.add_component::<Transform3D>(self.next_id as usize, Transform3D::new()),
|
||||
_ => {}
|
||||
}
|
||||
self.get_next_id();
|
||||
return id;
|
||||
}
|
||||
self.entities[self.next_id as usize] = Some(Entity::new(self.next_id));
|
||||
println!("{:?}", self.dimension);
|
||||
match self.dimension.as_str() {
|
||||
"2D" => self.add_component::<Transform2D>(self.next_id as usize, Transform2D::new()),
|
||||
"3D" => self.add_component::<Transform3D>(self.next_id as usize, Transform3D::new()),
|
||||
_ => {}
|
||||
}
|
||||
self.get_next_id();
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_entity(&self, entity_id: usize) -> &Entity {
|
||||
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
||||
self.entities.get(entity_id).unwrap().as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_entity_mut(&mut self, entity_id: usize) -> &mut Entity {
|
||||
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
||||
self.entities.get_mut(entity_id).unwrap().as_mut().unwrap()
|
||||
//self.entities.get_mut(id).unwrap()
|
||||
}
|
||||
|
||||
pub fn delete_entity(&mut self, entity_id: usize) {
|
||||
self.entities[entity_id] = None;
|
||||
//self.get_entity(id);
|
||||
for (key, value) in self.components.iter_mut() {
|
||||
value.remove::<u8>(entity_id);
|
||||
}
|
||||
self.id_queue.sorted_enqueue(entity_id as u32);
|
||||
self.get_next_id();
|
||||
self.remove_entity_from_archetype_subsets(entity_id as u32, self.get_component_set(entity_id));
|
||||
info!(format!("Deleted entity! ID: {}", entity_id));
|
||||
}
|
||||
|
||||
fn create_archetype(&mut self, components: ComponentSet) {
|
||||
self.archetypes.create_archetype(components);
|
||||
}
|
||||
|
||||
fn add_entity_to_archetype(&mut self, entity_id: u32, components: ComponentSet) {
|
||||
self.archetypes.add_entity_to_archetype(&components, entity_id);
|
||||
}
|
||||
|
||||
fn remove_entity_from_archetype(&mut self, entity_id: u32, components: ComponentSet) {
|
||||
self.archetypes.remove_entity_from_archetype(&components, entity_id);
|
||||
}
|
||||
|
||||
fn remove_entity_from_archetype_subsets(&mut self, entity_id: u32, components: ComponentSet) {
|
||||
let component_sets = self.archetypes.component_sets();
|
||||
let keys: Vec<ComponentSet> = component_sets.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, &ref elem)| if elem.is_subset(&components) { Some(i) } else { None })
|
||||
.collect::<Vec<usize>>()
|
||||
.iter()
|
||||
.map(|index| component_sets[*index].clone())
|
||||
.collect::<Vec<ComponentSet>>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_component_set(&self, entity_id: usize) -> ComponentSet {
|
||||
let components = self.entities.get(entity_id).unwrap().as_ref().unwrap().get_components().iter().collect::<Vec<usize>>();
|
||||
let type_ids = components.iter().map(|index| self.components.keys[*index]).collect::<Vec<TypeId>>();
|
||||
ComponentSet::from_ids(type_ids)
|
||||
}
|
||||
|
||||
pub fn register_component<T: Component + 'static>(&mut self) {
|
||||
self.components.register_component::<T>(self.entities.len());
|
||||
info!(format!("Registered component: {}", T::type_name()));
|
||||
}
|
||||
|
||||
pub fn deregister_component<T: Component + 'static>(&mut self) {
|
||||
self.components.deregister_component::<T>();
|
||||
info!(format!("Deregistered component: {}", T::type_name()));
|
||||
}
|
||||
|
||||
pub fn add_component<T: Component + 'static>(&mut self, entity_id: usize, component: T) {
|
||||
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
||||
self.components.set_component(entity_id, component);
|
||||
let component_index = self.components.keys.iter_mut().position(|x| *x == T::type_id()).unwrap();
|
||||
|
||||
self.get_entity_mut(entity_id).add_component(component_index);
|
||||
|
||||
if !self.archetypes.contains_archetype(&self.get_component_set(entity_id)) {
|
||||
self.create_archetype(self.get_component_set(entity_id));
|
||||
}
|
||||
self.add_entity_to_archetype(entity_id as u32, self.get_component_set(entity_id));
|
||||
|
||||
info!(format!("Added component {} to entity {}", T::type_name(), entity_id));
|
||||
debug!(format!("{:?}", self.archetypes));
|
||||
}
|
||||
|
||||
pub fn remove_component<T: Component + 'static>(&mut self, entity_id: usize) {
|
||||
self.components.remove_component::<T>(entity_id);
|
||||
self.remove_entity_from_archetype_subsets(entity_id as u32, self.get_component_set(entity_id));
|
||||
info!(format!("Removed component {} from entity {}", T::type_name(), entity_id));
|
||||
}
|
||||
|
||||
pub fn get_component<T: Component + 'static>(&self, entity_id: usize) -> &T {
|
||||
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
||||
assert!(self.components.get_component::<T>(entity_id) != None, "There is no component {} bound to the entity {} in the world!", T::type_name(), entity_id);
|
||||
self.components.get_component::<T>(entity_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_component_mut<T: Component + 'static>(&mut self, entity_id: usize) -> &mut T {
|
||||
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
||||
assert!(self.components.get_component::<T>(entity_id) != None, "There is no component {} bound to the entity {} in the world!", T::type_name(), entity_id);
|
||||
self.components.get_component_mut::<T>(entity_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_entities_with(&self, components: ComponentSet) -> Vec<u32> {
|
||||
assert!(self.archetypes.contains_archetype(&components), "The given components {:?} are not registered in the world!", components);
|
||||
info!(format!("Querying entities with components: {:?}", components));
|
||||
self.archetypes.get_archetype(&components).unwrap().clone()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue