simple ecs systems!

This commit is contained in:
SeanOMik 2023-06-28 23:18:44 -04:00
parent 74e21a083b
commit 6a30a17f5f
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
7 changed files with 196 additions and 12 deletions

17
Cargo.lock generated
View File

@ -621,6 +621,12 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.25" version = "1.0.25"
@ -1003,6 +1009,7 @@ dependencies = [
"hecs", "hecs",
"image", "image",
"instant", "instant",
"petgraph",
"specs", "specs",
"tobj", "tobj",
"tracing", "tracing",
@ -1387,6 +1394,16 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 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]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.9" version = "0.2.9"

View File

@ -24,3 +24,4 @@ async-trait = "0.1.65"
specs = { version = "0.18.0", features = [ "derive" ] } specs = { version = "0.18.0", features = [ "derive" ] }
hecs = "0.10.3" hecs = "0.10.3"
glam = { version = "0.24.0", features = ["bytemuck"] } glam = { version = "0.24.0", features = ["bytemuck"] }
petgraph = "0.6.3"

View File

@ -15,14 +15,20 @@ impl Default for TransformComponent {
} }
} }
impl From<Transform> for TransformComponent {
fn from(transform: Transform) -> Self {
Self {
transform
}
}
}
impl TransformComponent { impl TransformComponent {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
pub fn from_transform(transform: Transform) -> Self { pub fn from_transform(transform: Transform) -> Self {
Self { Self::from(transform)
transform
}
} }
} }

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use async_trait::async_trait; use async_trait::async_trait;
use hecs::World; use hecs::World;
use petgraph::{prelude::StableDiGraph, stable_graph::NodeIndex, visit::Topo};
use tracing::warn; use tracing::warn;
pub mod components; pub mod components;
@ -55,3 +56,86 @@ impl SimpleSystem for SystemFnExecutor {
Ok(()) Ok(())
} }
} }
struct SystemGraphNode {
name: String,
dependent_on: Vec<String>,
system: Box<dyn SimpleSystem>,
}
struct SystemGraphEdge {
}
pub struct SystemGraphExecutor {
graph: StableDiGraph<SystemGraphNode, SystemGraphEdge>,
node_refs: HashMap<String, NodeIndex>,
}
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<dyn SimpleSystem>, 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(())
}
}

View File

@ -13,7 +13,7 @@ use tracing_subscriber::{
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}}; 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 { struct TickCounter {
counter: u32, 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 { struct GameLoop {
window: Arc<Window>, window: Arc<Window>,
renderer: Box<dyn Renderer>, renderer: Box<dyn Renderer>,
world: Arc<Mutex<World>>, world: Arc<Mutex<World>>,
system_fn_executor: SystemFnExecutor, /// higher priority systems
engine_systems: Vec<Box<dyn SimpleSystem>>,
systems: Vec<Box<dyn SimpleSystem>>,
fps_counter: TickCounter, fps_counter: TickCounter,
} }
impl GameLoop { impl GameLoop {
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>) -> GameLoop { pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, user_systems: Vec<Box<dyn SimpleSystem>>) -> GameLoop {
/* let mut graph_exec = SystemGraphExecutor::new();
graph_exec.add_system("jiggle_entities".to_string(), Box::new(JiggleEntitiesSystem {}), &[]); */
Self { Self {
window: Arc::clone(&window), window: Arc::clone(&window),
renderer: Box::new(BasicRenderer::create_with_window(window).await), renderer: Box::new(BasicRenderer::create_with_window(window).await),
world, world,
system_fn_executor: SystemFnExecutor::new(), engine_systems: vec![],
systems: user_systems,
fps_counter: TickCounter::new(), fps_counter: TickCounter::new(),
} }
} }
@ -101,7 +120,14 @@ impl GameLoop {
async fn update(&mut self) { async fn update(&mut self) {
let mut world = self.world.lock().await; 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<ControlFlow> { async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
@ -208,12 +234,14 @@ impl GameLoop {
pub struct Game { pub struct Game {
world: Option<Arc<Mutex<World>>>, world: Option<Arc<Mutex<World>>>,
systems: Option<Vec<Box<dyn SimpleSystem>>>
} }
impl Default for Game { impl Default for Game {
fn default() -> Self { fn default() -> Self {
Self { Self {
world: None, world: None,
systems: Some(vec![]),
} }
} }
} }
@ -247,13 +275,25 @@ impl Game {
self self
} }
pub fn with_system<S>(&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) { pub async fn run(&mut self) {
let world = self.world.take().expect("ECS World was never given to Game!"); let world = self.world.take().expect("ECS World was never given to Game!");
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); 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| { event_loop.run(move |event, _, control_flow| {
g_loop.run_sync(event, control_flow); g_loop.run_sync(event, control_flow);

View File

@ -11,10 +11,11 @@ use ecs::components::transform::TransformComponent;
use game::Game; use game::Game;
use hecs::World; use hecs::World;
use crate::ecs::SystemFnExecutor;
use crate::render::material::Material; use crate::render::material::Material;
use crate::render::texture::Texture; use crate::render::texture::Texture;
use crate::ecs::components::camera::CameraComponent; use crate::ecs::components::camera::CameraComponent;
use crate::math::Angle; use crate::math::{Angle, Transform};
//use specs::*; //use specs::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -77,7 +78,7 @@ async fn main() {
shader_id: 0, shader_id: 0,
texture: diffuse_texture texture: diffuse_texture
}), }),
TransformComponent::new(), TransformComponent::from(Transform::from_translation(0.005, 0.0, 0.0)),
)); ));
let mut camera = CameraComponent::new(); let mut camera = CameraComponent::new();
@ -86,8 +87,21 @@ async fn main() {
camera.transform.rotate_z(Angle::Degrees(-90.0)); camera.transform.rotate_z(Angle::Degrees(-90.0));
world.spawn((camera,)); 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 Game::initialize().await
.with_world(world) .with_world(world)
.with_system(fn_exec)
.run().await; .run().await;
/* world.register::<Point2d>(); /* world.register::<Point2d>();

View File

@ -65,6 +65,16 @@ impl Transform {
Self::from_matrix(matrix) 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 { pub fn translate_vec3(&mut self, vec: Vec3) -> &mut Self {
let translation = glam::Mat4::from_translation(vec); let translation = glam::Mat4::from_translation(vec);
self.matrix *= translation; self.matrix *= translation;
@ -163,4 +173,16 @@ impl Transform {
self 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
}
} }