Add ECSRenderer for improved integration of rendering and ECS
- Introduced ECSRenderer struct to manage the connection between the renderer and the ECS. - Implemented methods for spawning and despawning meshes within the ECS. - Updated main function to utilize ECSRenderer for rendering and object management. - Added a new Time module to handle frame timing and updates.
This commit is contained in:
parent
cdbac0d4ab
commit
7e4168eee5
3 changed files with 101 additions and 30 deletions
40
src/ecs.rs
40
src/ecs.rs
|
|
@ -1,13 +1,48 @@
|
||||||
use glam::{Mat4, Quat, Vec3};
|
use glam::{Mat4, Quat, Vec3};
|
||||||
use hecs::World;
|
use hecs::{Entity, World};
|
||||||
|
use crate::{render::{GliumRenderer, Renderer}, model};
|
||||||
|
|
||||||
|
/// This system joins the renderer and ECS,
|
||||||
|
/// and provides tools to use them together
|
||||||
|
/// effectively.
|
||||||
|
pub struct ECSRenderer {
|
||||||
|
pub renderer: GliumRenderer,
|
||||||
|
pub world: World,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ECSRenderer {
|
||||||
|
pub fn new(renderer: GliumRenderer, world: World) -> Self {
|
||||||
|
Self { renderer, world }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_mesh(&mut self, mesh: model::Mesh, transform: Transform) -> Entity {
|
||||||
|
let mesh_id = self.renderer.meshes.len();
|
||||||
|
self.renderer.meshes.push(mesh);
|
||||||
|
self.world.spawn((transform, MeshHandle(mesh_id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn despawn_mesh(&mut self, entity: Entity) {
|
||||||
|
if let Ok(mesh_handle) = self.world.get::<&MeshHandle>(entity) {
|
||||||
|
if mesh_handle.0 < self.renderer.meshes.len() {
|
||||||
|
self.renderer.meshes.remove(mesh_handle.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = self.world.despawn(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a single frame using the internal renderer & world.
|
||||||
|
pub fn render(&mut self) {
|
||||||
|
self.renderer.render(&self.world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ------------ components ------------
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Transform {
|
pub struct Transform {
|
||||||
pub translation: Vec3,
|
pub translation: Vec3,
|
||||||
pub rotation: Quat,
|
pub rotation: Quat,
|
||||||
pub scale: Vec3,
|
pub scale: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform {
|
impl Transform {
|
||||||
pub fn matrix(&self) -> Mat4 {
|
pub fn matrix(&self) -> Mat4 {
|
||||||
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
|
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
|
||||||
|
|
@ -16,4 +51,3 @@ impl Transform {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MeshHandle(pub usize);
|
pub struct MeshHandle(pub usize);
|
||||||
|
|
||||||
|
|
|
||||||
51
src/main.rs
51
src/main.rs
|
|
@ -2,6 +2,7 @@ mod camera;
|
||||||
mod ecs;
|
mod ecs;
|
||||||
mod model;
|
mod model;
|
||||||
mod render;
|
mod render;
|
||||||
|
mod time;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
|
|
@ -9,9 +10,10 @@ use ecs::{MeshHandle, Transform};
|
||||||
use glam::{Quat, Vec3};
|
use glam::{Quat, Vec3};
|
||||||
use glium::backend::glutin::SimpleWindowBuilder;
|
use glium::backend::glutin::SimpleWindowBuilder;
|
||||||
use render::{Renderer, GliumRenderer};
|
use render::{Renderer, GliumRenderer};
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
const ROTATION_SPEED: f32 = 1.0;
|
||||||
|
|
||||||
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
||||||
.build()
|
.build()
|
||||||
.expect("create event-loop");
|
.expect("create event-loop");
|
||||||
|
|
@ -21,27 +23,28 @@ fn main() -> Result<()> {
|
||||||
.with_inner_size(1280, 720)
|
.with_inner_size(1280, 720)
|
||||||
.build(&event_loop);
|
.build(&event_loop);
|
||||||
|
|
||||||
let mut world = hecs::World::new();
|
// Create ECS renderer which internally owns both the world and the renderer
|
||||||
|
let mut ecsr = {
|
||||||
|
let world = hecs::World::new();
|
||||||
|
let renderer = GliumRenderer::new(display.clone())?;
|
||||||
|
ecs::ECSRenderer::new(renderer, world)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut time = time::Time::new();
|
||||||
|
|
||||||
|
let object_ent = {
|
||||||
let mesh = model::load_gltf("resources/monkey-smooth.gltf", &display)?;
|
let mesh = model::load_gltf("resources/monkey-smooth.gltf", &display)?;
|
||||||
// let mesh = model::cube(&display)?;
|
ecsr.spawn_mesh(mesh, Transform {
|
||||||
let mut renderer = GliumRenderer::new(display)?;
|
|
||||||
let mesh_id = renderer.meshes.len();
|
|
||||||
renderer.meshes.push(mesh);
|
|
||||||
|
|
||||||
let object_ent = world.spawn((
|
|
||||||
Transform {
|
|
||||||
translation: Vec3::ZERO,
|
translation: Vec3::ZERO,
|
||||||
rotation: Quat::IDENTITY,
|
rotation: Quat::IDENTITY,
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
},
|
})
|
||||||
MeshHandle(mesh_id),
|
};
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
let camera_ent = {
|
let camera_ent = {
|
||||||
let (w, h): (u32, u32) = window.inner_size().into();
|
let (w, h): (u32, u32) = window.inner_size().into();
|
||||||
world.spawn((Camera {
|
ecsr.world.spawn((Camera {
|
||||||
eye: Vec3::new(3.0, 2.0, 3.0),
|
eye: Vec3::new(3.0, 2.0, 3.0),
|
||||||
center: Vec3::ZERO,
|
center: Vec3::ZERO,
|
||||||
up: Vec3::Y,
|
up: Vec3::Y,
|
||||||
|
|
@ -60,27 +63,27 @@ fn main() -> Result<()> {
|
||||||
Event::WindowEvent { event, .. } => match event {
|
Event::WindowEvent { event, .. } => match event {
|
||||||
WindowEvent::CloseRequested => el.exit(),
|
WindowEvent::CloseRequested => el.exit(),
|
||||||
WindowEvent::Resized(sz) => {
|
WindowEvent::Resized(sz) => {
|
||||||
world.query_one_mut::<&mut crate::camera::Camera>(camera_ent).map(|mut cam| {
|
ecsr.world.query_one_mut::<&mut crate::camera::Camera>(camera_ent).map(|mut cam| {
|
||||||
cam.aspect = sz.width as f32 / sz.height as f32;
|
cam.aspect = sz.width as f32 / sz.height as f32;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
renderer.render(&world);
|
ecsr.render();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Event::AboutToWait => {
|
Event::AboutToWait => {
|
||||||
// -- update logic --
|
time.tick();
|
||||||
let now = Instant::now();
|
let dt = time.delta_seconds();
|
||||||
static mut LAST: Option<Instant> = None;
|
ecsr.world.query_one_mut::<&mut Transform>(object_ent).map(|mut object| {
|
||||||
let dt = unsafe { // FIXME
|
object.rotation *= Quat::from_rotation_y(ROTATION_SPEED * dt);
|
||||||
let last = LAST.replace(now).unwrap_or(now);
|
|
||||||
(now - last).as_secs_f32()
|
|
||||||
};
|
|
||||||
world.query_one_mut::<&mut Transform>(object_ent).map(|mut object| {
|
|
||||||
object.rotation *= Quat::from_rotation_y(dt);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// despawn the object after 3 seconds
|
||||||
|
if time.total_seconds() > 3.0 {
|
||||||
|
ecsr.despawn_mesh(object_ent);
|
||||||
|
}
|
||||||
|
|
||||||
// ask for next frame
|
// ask for next frame
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
src/time.rs
Normal file
34
src/time.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Time {
|
||||||
|
last: Instant,
|
||||||
|
delta: Duration,
|
||||||
|
total: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Time {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let now = Instant::now();
|
||||||
|
Self {
|
||||||
|
last: now,
|
||||||
|
delta: Duration::ZERO,
|
||||||
|
total: Duration::ZERO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
self.delta = now - self.last;
|
||||||
|
self.total += self.delta;
|
||||||
|
self.last = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delta_seconds(&self) -> f32 {
|
||||||
|
self.delta.as_secs_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_seconds(&self) -> f32 {
|
||||||
|
self.total.as_secs_f32()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue