From 6a30a17f5fc34c78cdc238565d326d628bb40d54 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Wed, 28 Jun 2023 23:18:44 -0400 Subject: [PATCH] simple ecs systems! --- Cargo.lock | 17 +++++++ Cargo.toml | 3 +- src/ecs/components/transform.rs | 12 +++-- src/ecs/mod.rs | 84 +++++++++++++++++++++++++++++++++ src/game.rs | 52 +++++++++++++++++--- src/main.rs | 18 ++++++- src/math/transform.rs | 22 +++++++++ 7 files changed, 196 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b87e89..5e46bd7 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -621,6 +621,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.25" @@ -1003,6 +1009,7 @@ dependencies = [ "hecs", "image", "instant", + "petgraph", "specs", "tobj", "tracing", @@ -1387,6 +1394,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.9" diff --git a/Cargo.toml b/Cargo.toml index 77dd9df..9091887 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,5 @@ instant = "0.1" async-trait = "0.1.65" specs = { version = "0.18.0", features = [ "derive" ] } hecs = "0.10.3" -glam = { version = "0.24.0", features = ["bytemuck"] } \ No newline at end of file +glam = { version = "0.24.0", features = ["bytemuck"] } +petgraph = "0.6.3" diff --git a/src/ecs/components/transform.rs b/src/ecs/components/transform.rs index 585c62c..b30a319 100755 --- a/src/ecs/components/transform.rs +++ b/src/ecs/components/transform.rs @@ -15,14 +15,20 @@ impl Default for TransformComponent { } } +impl From for TransformComponent { + fn from(transform: Transform) -> Self { + Self { + transform + } + } +} + impl TransformComponent { pub fn new() -> Self { Self::default() } pub fn from_transform(transform: Transform) -> Self { - Self { - transform - } + Self::from(transform) } } \ No newline at end of file diff --git a/src/ecs/mod.rs b/src/ecs/mod.rs index f117d71..4d7de4d 100755 --- a/src/ecs/mod.rs +++ b/src/ecs/mod.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use async_trait::async_trait; use hecs::World; +use petgraph::{prelude::StableDiGraph, stable_graph::NodeIndex, visit::Topo}; use tracing::warn; pub mod components; @@ -52,6 +53,89 @@ impl SimpleSystem for SystemFnExecutor { } } + Ok(()) + } +} + +struct SystemGraphNode { + name: String, + dependent_on: Vec, + system: Box, +} + +struct SystemGraphEdge { + +} + +pub struct SystemGraphExecutor { + graph: StableDiGraph, + node_refs: HashMap, +} + +impl Default for SystemGraphExecutor { + fn default() -> Self { + Self { + graph: StableDiGraph::new(), + node_refs: HashMap::new(), + } + } +} + +impl SystemGraphExecutor { + pub fn new() -> Self { + Self::default() + } + + /// Adds a SimpleSystem to the SystemGraph. + /// + /// WARN: Ensure that no cycles are created, this will cause the systems graph to not be sortable by topological sort, + /// and will cause a panic later on in the engine. + /// + /// TODO: Return false if a cycle was created, making it impossible to know when to dispatch them. This will mean that the System would not of had been added. + pub fn add_system(&mut self, name: String, system: Box, dependencies: &[String]) -> bool { + let added_index = + self.graph.add_node(SystemGraphNode { + name: name.clone(), + dependent_on: dependencies.clone().to_vec(), + system, + }); + + // store name ref to the graph node + self.node_refs.insert(name, added_index); + + // find the dependencies in node_refs and add edges directed towards the new node + for depend in dependencies.into_iter() { + let idx = self.node_refs + .get(depend) + .expect("Dependency not found!"); + + self.graph.add_edge(idx.clone(), added_index, SystemGraphEdge { }); + } + + true + } + + pub(crate) fn execute_mut(&mut self, world: &mut World) { + let mut topo = Topo::new(&self.graph); + + while let Some(nx) = topo.next(&self.graph) { + let node = self.graph.node_weight_mut(nx).unwrap(); + + match node.system.execute_mut(world) { + Ok(()) => {}, + Err(e) => { + warn!("System execution of {} resulted in an error! '{}'.", node.name, e); + return; + } + } + } + } +} + +impl SimpleSystem for SystemGraphExecutor { + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { + SystemGraphExecutor::execute_mut(self, world); + Ok(()) } } \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index 2cfee78..bac87b9 100755 --- a/src/game.rs +++ b/src/game.rs @@ -13,7 +13,7 @@ use tracing_subscriber::{ use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}}; -use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SystemFnExecutor, SimpleSystem}}; +use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SystemFnExecutor, SimpleSystem, SystemGraphExecutor}}; struct TickCounter { counter: u32, @@ -69,24 +69,43 @@ impl TickCounter { } } +struct JiggleEntitiesSystem { + +} + +impl SimpleSystem for JiggleEntitiesSystem { + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { + for (_eid, (transform, )) in world.query_mut::<(&mut TransformComponent,)>() { + transform.transform.translate_vec3(glam::Vec3::new(0.0, 0.001, 0.0)); + } + + Ok(()) + } +} struct GameLoop { window: Arc, renderer: Box, world: Arc>, - system_fn_executor: SystemFnExecutor, + /// higher priority systems + engine_systems: Vec>, + systems: Vec>, fps_counter: TickCounter, } impl GameLoop { - pub async fn new(window: Arc, world: Arc>) -> GameLoop { + pub async fn new(window: Arc, world: Arc>, user_systems: Vec>) -> GameLoop { + /* let mut graph_exec = SystemGraphExecutor::new(); + graph_exec.add_system("jiggle_entities".to_string(), Box::new(JiggleEntitiesSystem {}), &[]); */ + Self { window: Arc::clone(&window), renderer: Box::new(BasicRenderer::create_with_window(window).await), world, - system_fn_executor: SystemFnExecutor::new(), + engine_systems: vec![], + systems: user_systems, fps_counter: TickCounter::new(), } } @@ -101,7 +120,14 @@ impl GameLoop { async fn update(&mut self) { let mut world = self.world.lock().await; - self.system_fn_executor.execute_mut(&mut world).unwrap(); // always returns Ok(()) + + for esystem in self.engine_systems.iter_mut() { + esystem.execute_mut(&mut world).unwrap(); + } + + for system in self.systems.iter_mut() { + system.execute_mut(&mut world).unwrap(); + } } async fn input_update(&mut self, event: &InputEvent) -> Option { @@ -208,12 +234,14 @@ impl GameLoop { pub struct Game { world: Option>>, + systems: Option>> } impl Default for Game { fn default() -> Self { Self { world: None, + systems: Some(vec![]), } } } @@ -247,13 +275,25 @@ impl Game { self } + pub fn with_system(&mut self, system: S) -> &mut Self + where + S: SimpleSystem + 'static + { + let systems = self.systems.as_mut().unwrap(); + systems.push(Box::new(system)); + + self + } + pub async fn run(&mut self) { let world = self.world.take().expect("ECS World was never given to Game!"); 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).await; + let systems = self.systems.take().unwrap(); + + let mut g_loop = GameLoop::new(Arc::clone(&window), world, systems).await; event_loop.run(move |event, _, control_flow| { g_loop.run_sync(event, control_flow); diff --git a/src/main.rs b/src/main.rs index 70d3469..add3211 100755 --- a/src/main.rs +++ b/src/main.rs @@ -11,10 +11,11 @@ use ecs::components::transform::TransformComponent; use game::Game; use hecs::World; +use crate::ecs::SystemFnExecutor; use crate::render::material::Material; use crate::render::texture::Texture; use crate::ecs::components::camera::CameraComponent; -use crate::math::Angle; +use crate::math::{Angle, Transform}; //use specs::*; #[derive(Debug, Default)] @@ -77,7 +78,7 @@ async fn main() { shader_id: 0, texture: diffuse_texture }), - TransformComponent::new(), + TransformComponent::from(Transform::from_translation(0.005, 0.0, 0.0)), )); let mut camera = CameraComponent::new(); @@ -86,8 +87,21 @@ async fn main() { camera.transform.rotate_z(Angle::Degrees(-90.0)); world.spawn((camera,)); + let jiggle_system = |world: &mut World| -> anyhow::Result<()> { + for (_eid, (transform, )) in world.query_mut::<(&mut TransformComponent,)>() { + let t = &mut transform.transform; + t.translate_vec3(glam::Vec3::new(t.x() * -1.0, 0.001, 0.0)); + } + + Ok(()) + }; + + let mut fn_exec = SystemFnExecutor::new(); + fn_exec.add_system("jiggle", Box::new(jiggle_system)); + Game::initialize().await .with_world(world) + .with_system(fn_exec) .run().await; /* world.register::(); diff --git a/src/math/transform.rs b/src/math/transform.rs index c05a01a..3e97a14 100755 --- a/src/math/transform.rs +++ b/src/math/transform.rs @@ -65,6 +65,16 @@ impl Transform { Self::from_matrix(matrix) } + pub fn from_translation_vec(translation: Vec3) -> Self { + let matrix = glam::Mat4::from_scale_rotation_translation(Vec3::new(1.0, 1.0, 1.0), Quat::IDENTITY, translation); + + Self::from_matrix(matrix) + } + + pub fn from_translation(x: f32, y: f32, z: f32) -> Self { + Self::from_translation_vec(Vec3::new(x, y, z)) + } + pub fn translate_vec3(&mut self, vec: Vec3) -> &mut Self { let translation = glam::Mat4::from_translation(vec); self.matrix *= translation; @@ -163,4 +173,16 @@ impl Transform { self } + + pub fn x(&self) -> f32 { + self.get_translation().x + } + + pub fn y(&self) -> f32 { + self.get_translation().y + } + + pub fn z(&self) -> f32 { + self.get_translation().z + } } \ No newline at end of file