Giant refactor for a better event-driven architecture
This commit is contained in:
parent
341d531db3
commit
88a21040cd
22 changed files with 936 additions and 67 deletions
|
|
@ -2,3 +2,8 @@
|
|||
name = "raidillon_core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
glam = "0.30.4"
|
||||
hecs = "0.10.5"
|
||||
|
|
|
|||
95
raidillon_core/src/assets.rs
Normal file
95
raidillon_core/src/assets.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
// Forward declarations - these will be from other crates
|
||||
pub trait Model {}
|
||||
pub trait Material {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ModelId(pub usize);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct MaterialId(pub usize);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct TextureHandle(pub usize);
|
||||
|
||||
pub struct AssetManager<M: Model + ?Sized, Mat: Material + ?Sized> {
|
||||
models: Vec<Box<M>>,
|
||||
materials: Vec<Box<Mat>>,
|
||||
textures: HashMap<String, TextureHandle>,
|
||||
model_cache: HashMap<String, ModelId>,
|
||||
next_texture_id: usize,
|
||||
}
|
||||
|
||||
impl<M: Model + ?Sized, Mat: Material + ?Sized> AssetManager<M, Mat> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
models: Vec::new(),
|
||||
materials: Vec::new(),
|
||||
textures: HashMap::new(),
|
||||
model_cache: HashMap::new(),
|
||||
next_texture_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_model(&mut self, model: Box<M>) -> ModelId {
|
||||
let id = ModelId(self.models.len());
|
||||
self.models.push(model);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn cache_model(&mut self, path: String, model: Box<M>) -> ModelId {
|
||||
if let Some(&cached_id) = self.model_cache.get(&path) {
|
||||
return cached_id;
|
||||
}
|
||||
|
||||
let model_id = self.add_model(model);
|
||||
self.model_cache.insert(path, model_id);
|
||||
model_id
|
||||
}
|
||||
|
||||
pub fn get_model(&self, id: ModelId) -> Option<&M> {
|
||||
self.models.get(id.0).map(|boxed| boxed.as_ref())
|
||||
}
|
||||
|
||||
pub fn get_model_mut(&mut self, id: ModelId) -> Option<&mut M> {
|
||||
self.models.get_mut(id.0).map(|boxed| boxed.as_mut())
|
||||
}
|
||||
|
||||
pub fn add_material(&mut self, material: Box<Mat>) -> MaterialId {
|
||||
let id = MaterialId(self.materials.len());
|
||||
self.materials.push(material);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_material(&self, id: MaterialId) -> Option<&Mat> {
|
||||
self.materials.get(id.0).map(|boxed| boxed.as_ref())
|
||||
}
|
||||
|
||||
pub fn add_texture(&mut self, name: String) -> TextureHandle {
|
||||
if let Some(&handle) = self.textures.get(&name) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
let handle = TextureHandle(self.next_texture_id);
|
||||
self.next_texture_id += 1;
|
||||
self.textures.insert(name, handle);
|
||||
handle
|
||||
}
|
||||
|
||||
pub fn get_texture_handle(&self, name: &str) -> Option<TextureHandle> {
|
||||
self.textures.get(name).copied()
|
||||
}
|
||||
|
||||
pub fn model_count(&self) -> usize {
|
||||
self.models.len()
|
||||
}
|
||||
|
||||
pub fn material_count(&self) -> usize {
|
||||
self.materials.len()
|
||||
}
|
||||
|
||||
pub fn clear_cache(&mut self) {
|
||||
self.model_cache.clear();
|
||||
}
|
||||
}
|
||||
80
raidillon_core/src/engine.rs
Normal file
80
raidillon_core/src/engine.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use hecs::World;
|
||||
use crate::{
|
||||
Time, EventBus, GameEvent, SystemRegistry,
|
||||
AssetManager, Model, Material, ModelId
|
||||
};
|
||||
|
||||
pub struct Engine {
|
||||
pub world: World,
|
||||
pub systems: SystemRegistry,
|
||||
pub assets: AssetManager<dyn Model, dyn Material>,
|
||||
pub events: EventBus,
|
||||
pub time: Time,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new() -> Self {
|
||||
let systems = SystemRegistry::new();
|
||||
|
||||
Self {
|
||||
world: World::new(),
|
||||
systems,
|
||||
assets: AssetManager::new(),
|
||||
events: EventBus::new(),
|
||||
time: Time::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_system<S: crate::System + 'static>(&mut self, system: S) {
|
||||
self.systems.add_system(system);
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
self.time.tick();
|
||||
let dt = self.time.delta_seconds();
|
||||
|
||||
// Update all systems
|
||||
self.systems.update_all(&mut self.world, &self.assets, &mut self.events, dt);
|
||||
|
||||
// Process events
|
||||
self.events.process();
|
||||
}
|
||||
|
||||
pub fn handle_window_event(&mut self, event: &GameEvent) {
|
||||
self.events.emit(event.clone());
|
||||
self.systems.handle_event_for_all(event, &mut self.world);
|
||||
}
|
||||
|
||||
pub fn load_model(&mut self, path: &str) -> anyhow::Result<ModelId> {
|
||||
// This is a placeholder - in a real implementation, we'd need to
|
||||
// coordinate with the render system to actually load the model
|
||||
// For now, just return a dummy ID
|
||||
Ok(ModelId(0))
|
||||
}
|
||||
|
||||
pub fn spawn_entity_with_model(&mut self, model_id: ModelId) -> hecs::Entity {
|
||||
// This would need proper Transform and ModelHandle types
|
||||
// For now, return a placeholder entity
|
||||
self.world.spawn(())
|
||||
}
|
||||
|
||||
pub fn delta_time(&self) -> f32 {
|
||||
self.time.delta_seconds()
|
||||
}
|
||||
|
||||
pub fn emit_event(&mut self, event: GameEvent) {
|
||||
self.events.emit(event);
|
||||
}
|
||||
|
||||
pub fn world(&self) -> &World {
|
||||
&self.world
|
||||
}
|
||||
|
||||
pub fn world_mut(&mut self) -> &mut World {
|
||||
&mut self.world
|
||||
}
|
||||
|
||||
pub fn system_count(&self) -> usize {
|
||||
self.systems.system_count()
|
||||
}
|
||||
}
|
||||
60
raidillon_core/src/events.rs
Normal file
60
raidillon_core/src/events.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use glam::Vec3;
|
||||
use hecs::Entity;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GameEvent {
|
||||
InputAction(InputAction),
|
||||
CameraMove { position: Vec3, front: Vec3 },
|
||||
WindowResize { width: u32, height: u32 },
|
||||
EntitySpawned(Entity),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum InputAction {
|
||||
MoveForward,
|
||||
MoveBackward,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
}
|
||||
|
||||
pub trait EventHandler {
|
||||
fn handle(&mut self, event: &GameEvent);
|
||||
}
|
||||
|
||||
pub struct EventBus {
|
||||
events: Vec<GameEvent>,
|
||||
handlers: Vec<Box<dyn EventHandler>>,
|
||||
}
|
||||
|
||||
impl EventBus {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
events: Vec::new(),
|
||||
handlers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit(&mut self, event: GameEvent) {
|
||||
self.events.push(event);
|
||||
}
|
||||
|
||||
pub fn subscribe<H: EventHandler + 'static>(&mut self, handler: H) {
|
||||
self.handlers.push(Box::new(handler));
|
||||
}
|
||||
|
||||
pub fn process(&mut self) {
|
||||
for event in self.events.drain(..) {
|
||||
for handler in &mut self.handlers {
|
||||
handler.handle(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_events(&self) -> bool {
|
||||
!self.events.is_empty()
|
||||
}
|
||||
|
||||
pub fn events(&self) -> &[GameEvent] {
|
||||
&self.events
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
pub mod time;
|
||||
pub mod events;
|
||||
pub mod assets;
|
||||
pub mod systems;
|
||||
pub mod engine;
|
||||
|
||||
pub use time::Time;
|
||||
pub use events::{GameEvent, InputAction, EventHandler, EventBus};
|
||||
pub use assets::{AssetManager, ModelId, MaterialId, TextureHandle, Model, Material};
|
||||
pub use systems::{System, SystemRegistry};
|
||||
pub use engine::Engine;
|
||||
|
|
|
|||
45
raidillon_core/src/systems.rs
Normal file
45
raidillon_core/src/systems.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use hecs::World;
|
||||
use crate::assets::{AssetManager, Model, Material};
|
||||
use crate::events::{EventBus, GameEvent};
|
||||
|
||||
pub trait System {
|
||||
fn update(&mut self, world: &mut World, resources: &AssetManager<dyn Model, dyn Material>, events: &mut EventBus, dt: f32);
|
||||
fn handle_event(&mut self, event: &GameEvent, world: &mut World);
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
pub struct SystemRegistry {
|
||||
systems: Vec<Box<dyn System>>,
|
||||
}
|
||||
|
||||
impl SystemRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
systems: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_system<S: System + 'static>(&mut self, system: S) {
|
||||
self.systems.push(Box::new(system));
|
||||
}
|
||||
|
||||
pub fn update_all(&mut self, world: &mut World, resources: &AssetManager<dyn Model, dyn Material>, events: &mut EventBus, dt: f32) {
|
||||
for system in &mut self.systems {
|
||||
system.update(world, resources, events, dt);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_event_for_all(&mut self, event: &GameEvent, world: &mut World) {
|
||||
for system in &mut self.systems {
|
||||
system.handle_event(event, world);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_count(&self) -> usize {
|
||||
self.systems.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.systems.clear();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue