From 3fe294b8b2dc577b24c210d68355760a94bdf0c1 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 14 Apr 2023 00:22:17 -0400 Subject: [PATCH] Create the ground works for an ECS in the engine --- .gitignore | 0 .vscode/launch.json | 45 +++++++++++++++ .vscode/settings.json | 3 + Cargo.lock | 102 +++++++++++++++++++++++++++++--- Cargo.toml | 4 +- shell.nix | 0 src/controls.rs | 3 + src/ecs/component.rs | 6 -- src/ecs/entity.rs | 12 ---- src/ecs/mod.rs | 3 - src/ecs/registry.rs | 131 ------------------------------------------ src/game.rs | 95 ++++++++++++++++++------------ src/input_event.rs | 0 src/main.rs | 97 ++++++++++++++++++++----------- src/renderer.rs | 0 src/shader.wgsl | 0 src/system.rs | 0 17 files changed, 268 insertions(+), 233 deletions(-) mode change 100644 => 100755 .gitignore create mode 100755 .vscode/launch.json create mode 100755 .vscode/settings.json mode change 100644 => 100755 Cargo.lock mode change 100644 => 100755 Cargo.toml mode change 100644 => 100755 shell.nix create mode 100755 src/controls.rs delete mode 100644 src/ecs/component.rs delete mode 100644 src/ecs/entity.rs delete mode 100644 src/ecs/mod.rs delete mode 100644 src/ecs/registry.rs mode change 100644 => 100755 src/game.rs mode change 100644 => 100755 src/input_event.rs mode change 100644 => 100755 src/main.rs mode change 100644 => 100755 src/renderer.rs mode change 100644 => 100755 src/shader.wgsl mode change 100644 => 100755 src/system.rs diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..521ee82 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'lyra-engine'", + "cargo": { + "args": [ + "build", + "--bin=lyra-engine", + "--package=lyra-engine" + ], + "filter": { + "name": "lyra-engine", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'lyra-engine'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=lyra-engine", + "--package=lyra-engine" + ], + "filter": { + "name": "lyra-engine", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100755 index 0000000..12b229e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock old mode 100644 new mode 100755 index 52f204c..94418d4 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,12 @@ dependencies = [ "syn", ] +[[package]] +name = "atom" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3" + [[package]] name = "atomic-waker" version = "1.1.0" @@ -546,6 +552,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.14" @@ -826,6 +842,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hibitset" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a1bb8316a44459a7d14253c4d28dd7395cbd23cc04a68c46e851b8e46d64b1" +dependencies = [ + "atom", + "rayon", +] + [[package]] name = "image" version = "0.24.5" @@ -967,7 +993,7 @@ dependencies = [ "cgmath", "image", "instant", - "shipyard", + "specs", "tobj", "tracing", "tracing-log", @@ -1059,6 +1085,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "mopa" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" + [[package]] name = "naga" version = "0.11.0" @@ -1531,28 +1563,37 @@ dependencies = [ ] [[package]] -name = "shipyard" -version = "0.6.2" +name = "shred" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3511ae730f2e1c3d62a9025e2f9b2acbf130968057f1b3caab6d74a54a5e0e56" +checksum = "102269e720bb814df57e136161cad841f2b6f411e003ac748fc48aaf2363bea3" dependencies = [ + "arrayvec", "hashbrown", - "lock_api", + "mopa", "rayon", - "shipyard_proc", + "shred-derive", + "smallvec", + "tynm", ] [[package]] -name = "shipyard_proc" -version = "0.3.0" +name = "shred-derive" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb847f4b9582e468198b5cfb5731b65cc67fe5e535acc9cbf3c11703d15f08c" +checksum = "d5404c36bd155e41a54276ab6aafedad2fb627e5e5849d36ec439c9ddc044a2f" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "shrev" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ea33232fdcf1bf691ca33450e5a94dde13e1a8cbb8caabc5e4f9d761e10b1a" + [[package]] name = "signal-hook" version = "0.3.15" @@ -1625,6 +1666,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "specs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea85dac2880f84d4025ff5ace80cda6d8bc43bc88b6a389b9277fcf894b51e9" +dependencies = [ + "crossbeam-queue", + "hashbrown", + "hibitset", + "log", + "rayon", + "shred", + "shrev", + "specs-derive", + "tuple_utils", +] + +[[package]] +name = "specs-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e23e09360f3d2190fec4222cd9e19d3158d5da948c0d1ea362df617dd103511" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -1812,6 +1881,21 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" +[[package]] +name = "tuple_utils" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffaaf9392ef73cd30828797152476aaa2fa37a17856934fa63d4843f34290e9" + +[[package]] +name = "tynm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd59ecde1e694c0495c8b92bdd0f9a70e3b489cd8180fc6a18f6ea1c1026f3b2" +dependencies = [ + "nom", +] + [[package]] name = "unicode-ident" version = "1.0.6" diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 index 01aec67..77430fd --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -shipyard = "0.6.2" winit = "0.28.1" #winit-modular = "0.1.1" tracing = "0.1.37" @@ -23,4 +22,5 @@ tobj = { version = "3.2.1", features = [ "async", ]} instant = "0.1" -async-trait = "0.1.65" \ No newline at end of file +async-trait = "0.1.65" +specs = { version = "0.18.0", features = [ "derive" ] } diff --git a/shell.nix b/shell.nix old mode 100644 new mode 100755 diff --git a/src/controls.rs b/src/controls.rs new file mode 100755 index 0000000..5f0aed2 --- /dev/null +++ b/src/controls.rs @@ -0,0 +1,3 @@ +pub struct Controls { + +} \ No newline at end of file diff --git a/src/ecs/component.rs b/src/ecs/component.rs deleted file mode 100644 index 9d9d815..0000000 --- a/src/ecs/component.rs +++ /dev/null @@ -1,6 +0,0 @@ -use std::any::Any; - -pub trait Component { - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; -} \ No newline at end of file diff --git a/src/ecs/entity.rs b/src/ecs/entity.rs deleted file mode 100644 index 6dc544f..0000000 --- a/src/ecs/entity.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Entity { - pub handle: i32, -} - -impl Entity { - pub fn new(handle: i32) -> Self { - Self { - handle - } - } -} \ No newline at end of file diff --git a/src/ecs/mod.rs b/src/ecs/mod.rs deleted file mode 100644 index ba47e73..0000000 --- a/src/ecs/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod entity; -pub mod registry; -pub mod component; \ No newline at end of file diff --git a/src/ecs/registry.rs b/src/ecs/registry.rs deleted file mode 100644 index 5dd98cd..0000000 --- a/src/ecs/registry.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::{collections::HashMap, cell::{RefCell, RefMut, Ref}, rc::Rc, borrow::BorrowMut, any::Any, ops::Deref}; - -use super::{entity::Entity, component::Component}; - -pub trait Registry { - /// Create an entity - fn create_entity(&mut self) -> Entity; - - /// Remove the entity from the registry. Its components will also be destroyed - fn destroy_entity(&mut self, entity: Entity); - - /// Add a component to an entity - fn entity_add_component(&mut self, entity: Entity, component: C) -> Option>>; // TODO: Return result if the component is already added. - - /// Remove a component from an entity - fn entity_remove_component(&mut self, entity: Entity) -> bool; - - fn entity_has_component(&self, entity: Entity) -> bool; - - /// Get all the components that an entity has - fn entity_get_components(&self, entity: Entity) -> Option>>>; - - /// Get a component of a specific type. - fn entity_get_component(&self, entity: Entity) -> Option>; -} - -pub struct BasicRegistry { - next_handle: i32, - map: HashMap>>>, -} - -impl BasicRegistry { - pub fn new() -> Self { - Self { - next_handle: 0, - map: HashMap::new(), - } - } -} - -impl Registry for BasicRegistry { - fn create_entity(&mut self) -> Entity { - //self.map.insert() - let entity = Entity::new(self.next_handle); - self.next_handle += 1; - - self.map.insert(entity, Vec::new()); - - entity - } - - fn destroy_entity(&mut self, entity: Entity) { - self.map.remove(&entity); - } - - fn entity_add_component(&mut self, entity: Entity, component: C) -> Option>> { - if let Some(components) = self.map.get_mut(&entity) { - let rc = Rc::new(RefCell::new(component)); - let clone = Rc::clone(&rc); - - components.push(rc); - - return Some(clone); - } - - None - } - - fn entity_remove_component(&mut self, entity: Entity) -> bool { - if let Some(components) = self.map.get_mut(&entity) { - let mut removed = false; - components.retain(|component| { - if removed { - return true; - } - - let borrowed_comp = component.deref().borrow_mut(); - let any_comp = borrowed_comp.as_any(); - - if any_comp.is::() { - removed = true; - false - } else { - true - } - }); - - return removed; - } - false - } - - fn entity_has_component(&self, entity: Entity) -> bool { - if let Some(components) = self.map.get(&entity) { - for component in components.iter() { - let borrowed_comp = component.deref().borrow_mut(); - let any_comp = borrowed_comp.as_any(); - if any_comp.is::() { - return true; - } - } - } - false - } - - fn entity_get_components(&self, entity: Entity) -> Option>>> { - if let Some(components) = self.map.get(&entity) { - let mut cloned_components = Vec::new(); - - for component in components.iter() { - cloned_components.push(Rc::clone(&component)); - } - - return Some(cloned_components); - } - None - } - - fn entity_get_component(&self, entity: Entity) -> Option> { - if let Some(components) = self.map.get(&entity) { - for component in components.iter() { - let borrowed_comp = component.deref().borrow_mut(); - let any_comp = borrowed_comp.as_any(); - if any_comp.is::() { - return Some(RefMut::map(borrowed_comp, |c| c.as_any_mut().downcast_mut().unwrap())); - } - } - } - None - } -} \ No newline at end of file diff --git a/src/game.rs b/src/game.rs old mode 100644 new mode 100755 index 29c6eae..f06d7f5 --- a/src/game.rs +++ b/src/game.rs @@ -1,10 +1,10 @@ -use std::{sync::Arc, ops::Deref}; +use std::{sync::Arc}; use async_std::{task::block_on, sync::Mutex}; -use shipyard::{World, Workload}; -use tracing::{metadata::LevelFilter, info, debug, Level, warn}; -/* use tracing_subscriber::{filter::FilterFn, util::SubscriberInitExt}; -use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; */ + +use specs::{Dispatcher, World}; + +use tracing::{metadata::LevelFilter, info, debug, warn}; use tracing_subscriber::{ layer::{Layer, SubscriberExt}, filter::FilterFn, @@ -15,16 +15,22 @@ use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, Keyboar use crate::{renderer::{Renderer, BasicRenderer}, input_event::InputEvent}; -struct GameLoop { +struct GameLoop<'a, 'b> { window: Arc, renderer: Box, + + world: Arc>, + system_dispatchers: Vec>, } -impl GameLoop { - pub async fn new(window: &Arc) -> Self { +impl<'a, 'b> GameLoop<'a, 'b> { + pub async fn new(window: Arc, world: Arc>, system_dispatchers: Vec>) -> GameLoop<'a, 'b> { Self { window: Arc::clone(&window), - renderer: Box::new(BasicRenderer::create_with_window(Arc::clone(window)).await), + renderer: Box::new(BasicRenderer::create_with_window(window).await), + + world, + system_dispatchers, } } @@ -37,8 +43,10 @@ impl GameLoop { } async fn update(&mut self) { - - todo!() + let world = self.world.lock().await; + for dispatcher in self.system_dispatchers.iter_mut() { + dispatcher.dispatch(&world); + } } async fn input_update(&mut self, event: &InputEvent) -> Option { @@ -112,6 +120,8 @@ impl GameLoop { }, Event::RedrawRequested(window_id) if window_id == self.window.id() => { + self.update().await; + match self.renderer.as_mut().render().await { Ok(_) => {} // Reconfigure the surface if lost @@ -131,20 +141,23 @@ impl GameLoop { } } -pub struct Game { - +pub struct Game<'a, 'b> { + world: Option>>, + system_dispatchers: Option>>, } -impl Game { - /* fn lyra_engine_log_filter() { +impl<'a, 'b> Default for Game<'a, 'b> { + fn default() -> Self { + Self { + world: None, + system_dispatchers: None, + } + } +} - } */ - - pub async fn initialize() -> Self { +impl<'a, 'b> Game<'static, 'static> { + pub async fn initialize() -> Game<'static, 'static> { let filter = FilterFn::new(|metadata| { - //metadata. - //println!("Metadata: {:?}", metadata); - metadata.module_path() .unwrap_or_else(|| metadata.target()) .starts_with("lyra_engine") && (LevelFilter::INFO >= metadata.level().to_owned()) @@ -156,36 +169,42 @@ impl Game { .with(layer.with_filter(filter)) .init(); - Self { - - } + Self::default() } - fn update() { - todo!() + pub fn with_world(&mut self, world: World) -> &mut Self { + self.world = Some(Arc::new(Mutex::new(world))); + + self } - fn input_update() { - todo!() + pub fn with_world_arc(&mut self, world: Arc>) -> &mut Self { + self.world = Some(world); + + self } - fn render_window() { - todo!() + pub fn with_dispatchers(&mut self, dispatchers: Vec>) -> &mut Self { + self.system_dispatchers = Some(dispatchers); + + self } - fn render_item() { - todo!() - } + pub fn with_dispatcher(&mut self, dispatcher: Dispatcher<'static, 'static>) -> &mut Self { + self.system_dispatchers.get_or_insert_with(|| Vec::new()).push(dispatcher); - fn exit() { - todo!() + self } pub async fn run(&mut self) { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let world = self.world.take().expect("ECS World was never given to Game!"); + let system_dispatchers = self.system_dispatchers + .take().unwrap_or_else(|| Vec::new()); - let mut g_loop = GameLoop::new(&Arc::new(window)).await; + let event_loop = EventLoop::new(); + let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); + + let mut g_loop = GameLoop::new(Arc::clone(&window), world, system_dispatchers).await; event_loop.run(move |event, _, control_flow| { g_loop.run_sync(event, control_flow); diff --git a/src/input_event.rs b/src/input_event.rs old mode 100644 new mode 100755 diff --git a/src/main.rs b/src/main.rs old mode 100644 new mode 100755 index 127eb10..0b95b4e --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,23 @@ mod system; mod game; mod renderer; -//mod ecs; mod input_event; use game::Game; +use specs::*; -use shipyard::{Component, World, EntitiesViewMut, ViewMut, Get, Workload}; -use shipyard::IntoWorkload; - -//use ecs::{registry::Registry, component::Component}; - -#[derive(Component, Debug)] +#[derive(Component, Debug, Default)] struct Point2d { x: i32, y: i32, } +impl std::fmt::Display for Point2d { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(x={}, y={})", self.x, self.y) + } +} + impl Point2d { pub fn new(x: i32, y: i32) -> Self { Self { @@ -26,37 +27,69 @@ impl Point2d { } } +#[derive(Component, Debug, Default)] +struct Point3d { + x: i32, + y: i32, + z: i32, +} + +impl std::fmt::Display for Point3d { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(x={}, y={}, z={})", self.x, self.y, self.z) + } +} + +impl Point3d { + pub fn new(x: i32, y: i32, z: i32) -> Self { + Self { + x, + y, + z, + } + } +} + +struct TestingSystem; + +impl<'a> System<'a> for TestingSystem { + // These are the resources required for execution. + // You can also define a struct and `#[derive(SystemData)]`, + // see the `full` example. + type SystemData = (ReadStorage<'a, Point2d>,); + + fn run(&mut self, (point,): Self::SystemData) { + // The `.join()` combines multiple component storages, + // so we get access to all entities which have + // both a position and a velocity. + for (point,) in (&point,).join() { + //point.0 += vel.0; + println!("Entity at: {}", point); + } + } +} + #[async_std::main] async fn main() { let mut world = World::new(); + world.register::(); + world.register::(); - let entity = world.add_entity(Point2d::new(10, 10)); - let entity2 = world.add_entity(Point2d::new(846, 2188)); + world.create_entity() + .with(Point2d::new(10, 10)) + .with(Point3d::new(50, 50, 50)) + .build(); - world.run( - |mut vm_point: ViewMut| { - let mut p = (&mut vm_point).get(entity).unwrap(); - - println!("Point: {:?}", p); - p.x = 100; - println!("Point after: {:?}", p); - drop(p); + let mut dispatcher = DispatcherBuilder::new().with(TestingSystem, "testing_system", &[]).build(); + dispatcher.setup(&mut world); - let mut p = (&mut vm_point).get(entity2).unwrap(); - - println!("Point: {:?}", p); - p.x = 1000; - println!("Point after: {:?}", p); + //dispatcher.dispatch(&mut world); - //let mut vm_ - //println!("Point2d: {:?}", vm_point.get); - /* let empty_entity = entities.add_entity((), ()); - let single_component = entities.add_entity(&mut vm_pos, Pos::new()); - let multiple_components = - entities.add_entity((&mut vm_pos, &mut vm_vel), (Pos::new(), Vel::new())); */ - }, - ); + Game::initialize().await + .with_world(world) + .with_dispatcher(dispatcher) + .run().await; - let mut game = Game::initialize().await; - game.run().await; + /* let mut game = Game::initialize().await; + game.run().await; */ } diff --git a/src/renderer.rs b/src/renderer.rs old mode 100644 new mode 100755 diff --git a/src/shader.wgsl b/src/shader.wgsl old mode 100644 new mode 100755 diff --git a/src/system.rs b/src/system.rs old mode 100644 new mode 100755