@@ -1483,10 +1483,22 @@ dependencies = [ | |||
"hashbrown", | |||
"mopa", | |||
"rayon", | |||
"shred-derive", | |||
"smallvec", | |||
"tynm", | |||
] | |||
[[package]] | |||
name = "shred-derive" | |||
version = "0.6.2" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
checksum = "a1f37080f2751fbf091dbdebaa95bd6cf9dbf74ad1d50396b1908518a1747fdf" | |||
dependencies = [ | |||
"proc-macro2", | |||
"quote", | |||
"syn", | |||
] | |||
[[package]] | |||
name = "shrev" | |||
version = "1.1.1" | |||
@@ -1539,6 +1551,7 @@ dependencies = [ | |||
"num_cpus", | |||
"ordered-float 2.0.0", | |||
"rand", | |||
"shred", | |||
"shrev", | |||
"specs", | |||
"thiserror", | |||
@@ -15,6 +15,7 @@ log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn | |||
num_cpus = "1.13" | |||
ordered-float = "2.0" | |||
rand = "0.7" | |||
shred = "0.10" | |||
shrev = "1.1" | |||
specs = "0.16" | |||
thiserror = "1.0" | |||
@@ -1,69 +0,0 @@ | |||
#![allow(dead_code)] | |||
use std::mem::take; | |||
use std::time::Instant; | |||
pub struct FrameCounter { | |||
last_frame: Instant, | |||
time: f32, | |||
count: usize, | |||
fps: usize, | |||
delta: f32, | |||
updated: bool, | |||
} | |||
impl FrameCounter { | |||
pub fn updated(&self) -> bool { | |||
self.updated | |||
} | |||
#[inline] | |||
pub fn fps(&self) -> usize { | |||
self.fps | |||
} | |||
#[inline] | |||
pub fn delta(&self) -> f32 { | |||
self.delta | |||
} | |||
#[inline] | |||
pub fn next(&mut self) -> bool { | |||
let now = Instant::now(); | |||
self.delta = (now - self.last_frame).as_secs_f32(); | |||
self.last_frame = now; | |||
self.time += self.delta; | |||
self.count += 1; | |||
self.updated = if self.time >= 2.0 { | |||
self.time = 0.0; | |||
self.fps = 0; | |||
self.count = 0; | |||
true | |||
} else if self.time >= 1.0 { | |||
self.time -= 1.0; | |||
self.fps = take(&mut self.count); | |||
true | |||
} else { | |||
false | |||
}; | |||
self.updated | |||
} | |||
} | |||
impl Default for FrameCounter { | |||
fn default() -> Self { | |||
Self { | |||
last_frame: Instant::now(), | |||
time: 0.0, | |||
count: 0, | |||
fps: 0, | |||
delta: 0.0, | |||
updated: false, | |||
} | |||
} | |||
} |
@@ -1,41 +1,9 @@ | |||
pub mod camera; | |||
pub mod events; | |||
pub mod frame_counter; | |||
pub mod geometry; | |||
pub mod text; | |||
pub mod vfs; | |||
pub mod window; | |||
use std::iter::Iterator; | |||
use std::ops::Deref; | |||
use self::vfs::Vfs; | |||
use glc::{ | |||
shader::{Program, Shader, Type}, | |||
texture::{Data, Target, Texture}, | |||
}; | |||
use crate::Error; | |||
pub fn load_program<I>(vfs: &Vfs, iter: I) -> Result<Program, Error> | |||
where | |||
I: IntoIterator<Item = (Type, &'static str)>, | |||
{ | |||
Program::from_shaders_result(iter.into_iter().map(|(t, p)| { | |||
let mut file = vfs.deref().join(p)?.open_file()?; | |||
let shader = Shader::from_reader(t, &mut file)?; | |||
Ok(shader) | |||
})) | |||
} | |||
pub fn load_texture(vfs: &Vfs, path: &str) -> Result<Texture, Error> { | |||
let mut file = vfs.deref().join(path)?.open_file()?; | |||
let mut data = Data::from_reader(&mut file)?; | |||
data.convert_to(data.format().info().format_gl_support)?; | |||
let mut texture = Texture::new(Target::Texture2D)?; | |||
texture.upload(&data, true)?; | |||
Ok(texture) | |||
} | |||
mod events; | |||
mod text; | |||
mod window; | |||
mod world; | |||
pub use events::{Events, WindowEvent}; | |||
pub use text::{Text, TextCache, TextManager}; | |||
pub use window::Window; | |||
pub use world::WorldHelper; |
@@ -6,6 +6,7 @@ use std::collections::HashMap; | |||
use std::fmt::Display; | |||
use std::hash::{Hash, Hasher}; | |||
use std::mem::size_of; | |||
use std::ops::Deref; | |||
use std::ptr::null; | |||
use std::rc::{Rc, Weak}; | |||
@@ -25,10 +26,11 @@ use glyph_brush::{ | |||
}; | |||
use log::warn; | |||
use ordered_float::OrderedFloat; | |||
use specs::World; | |||
use crate::Error; | |||
use super::{load_program, vfs::Vfs}; | |||
use super::super::{misc::WorldHelper, resources::Vfs}; | |||
/* TextManager */ | |||
@@ -42,15 +44,12 @@ struct TextManagerInner { | |||
} | |||
impl TextManager { | |||
pub fn new(vfs: &Vfs) -> Result<Self, Error> { | |||
let vfs = vfs.clone(); | |||
let program = load_program( | |||
&vfs, | |||
vec![ | |||
(Type::Vertex, "resources/shader/text.vert"), | |||
(Type::Fragment, "resources/shader/text.frag"), | |||
], | |||
)?; | |||
pub fn new(world: &World) -> Result<Self, Error> { | |||
let vfs = world.resource::<Vfs>()?.deref().clone(); | |||
let program = world.load_program(vec![ | |||
(Type::Vertex, "resources/shader/text.vert"), | |||
(Type::Fragment, "resources/shader/text.frag"), | |||
])?; | |||
let program = Rc::new(program); | |||
Ok(Self(Rc::new(RefCell::new(TextManagerInner { | |||
@@ -109,8 +108,12 @@ pub struct TextCacheInner { | |||
texts: HashMap<usize, Weak<RefCell<TextInner>>>, | |||
text_ids: Vec<usize>, | |||
next_text_id: usize, | |||
update_counter: usize, | |||
update_requested: bool, | |||
} | |||
pub struct UpdateGuard(TextCache); | |||
impl TextCache { | |||
fn new(manager: &TextManager) -> Result<Self, Error> { | |||
let mut texture = Texture::new(TextureTarget::Texture2D)?; | |||
@@ -125,6 +128,8 @@ impl TextCache { | |||
texts: HashMap::new(), | |||
text_ids: Vec::new(), | |||
next_text_id: 0, | |||
update_counter: 0, | |||
update_requested: false, | |||
})))) | |||
} | |||
@@ -132,6 +137,34 @@ impl TextCache { | |||
TextBuilder::new(self) | |||
} | |||
pub fn begin_update(&self) -> UpdateGuard { | |||
self.0.borrow_mut().update_counter += 1; | |||
UpdateGuard(self.clone()) | |||
} | |||
fn end_update(&self) { | |||
let mut inner = self.0.borrow_mut(); | |||
if inner.update_counter > 0 { | |||
inner.update_counter -= 1; | |||
} | |||
if inner.update_counter > 0 { | |||
return; | |||
} | |||
let update_requested = inner.update_requested; | |||
drop(inner); | |||
if update_requested { | |||
if let Err(err) = self.update() { | |||
warn!("Unable to update text cache: {}", err); | |||
} | |||
} | |||
} | |||
fn font_by_name(&self, name: &str) -> Result<FontId, Error> { | |||
let mut inner = self.0.borrow_mut(); | |||
@@ -186,6 +219,12 @@ impl TextCache { | |||
let mut inner = self.0.borrow_mut(); | |||
let inner = &mut *inner; | |||
if inner.update_counter > 0 { | |||
inner.update_requested = true; | |||
return Ok(()); | |||
} | |||
/* remove droped texts */ | |||
let texts = &mut inner.texts; | |||
let text_ids = &mut inner.text_ids; | |||
@@ -251,6 +290,8 @@ impl TextCache { | |||
} | |||
} | |||
inner.update_requested = false; | |||
Ok(()) | |||
} | |||
} | |||
@@ -287,6 +328,12 @@ impl TextCacheInner { | |||
} | |||
} | |||
impl Drop for UpdateGuard { | |||
fn drop(&mut self) { | |||
self.0.end_update() | |||
} | |||
} | |||
fn resize_texture(new_size: (u32, u32), cur_size: Option<(u32, u32)>) -> Result<(u32, u32), Error> { | |||
let mut max_size = 0; | |||
gl::get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_size); | |||
@@ -0,0 +1,86 @@ | |||
use std::any::type_name; | |||
use std::iter::Iterator; | |||
use glc::{ | |||
shader::{Program, Shader, Type}, | |||
texture::{Data, Target, Texture}, | |||
}; | |||
use shred::{Fetch, FetchMut, Resource}; | |||
use shrev::{Event, EventChannel, ReaderId}; | |||
use specs::World; | |||
use crate::Error; | |||
use super::super::resources::Vfs; | |||
pub trait WorldHelper { | |||
fn resource<R>(&self) -> Result<Fetch<R>, Error> | |||
where | |||
R: Resource; | |||
fn resource_mut<R>(&self) -> Result<FetchMut<R>, Error> | |||
where | |||
R: Resource; | |||
fn register_event_reader<E>(&self) -> Result<ReaderId<E>, Error> | |||
where | |||
E: Event; | |||
fn load_program<I>(&self, iter: I) -> Result<Program, Error> | |||
where | |||
I: IntoIterator<Item = (Type, &'static str)>; | |||
fn load_texture(&self, path: &str) -> Result<Texture, Error>; | |||
} | |||
impl WorldHelper for World { | |||
fn resource<R>(&self) -> Result<Fetch<R>, Error> | |||
where | |||
R: Resource, | |||
{ | |||
self.try_fetch::<R>() | |||
.ok_or_else(|| Error::ResourceNotRegistered(type_name::<R>())) | |||
} | |||
fn resource_mut<R>(&self) -> Result<FetchMut<R>, Error> | |||
where | |||
R: Resource, | |||
{ | |||
self.try_fetch_mut::<R>() | |||
.ok_or_else(|| Error::ResourceNotRegistered(type_name::<R>())) | |||
} | |||
fn register_event_reader<E>(&self) -> Result<ReaderId<E>, Error> | |||
where | |||
E: Event, | |||
{ | |||
Ok(self.resource_mut::<EventChannel<E>>()?.register_reader()) | |||
} | |||
fn load_program<I>(&self, iter: I) -> Result<Program, Error> | |||
where | |||
I: IntoIterator<Item = (Type, &'static str)>, | |||
{ | |||
let vfs = self.fetch::<Vfs>(); | |||
Program::from_shaders_result(iter.into_iter().map(|(t, p)| { | |||
let mut file = vfs.join(p)?.open_file()?; | |||
let shader = Shader::from_reader(t, &mut file)?; | |||
Ok(shader) | |||
})) | |||
} | |||
fn load_texture(&self, path: &str) -> Result<Texture, Error> { | |||
let vfs = self.fetch::<Vfs>(); | |||
let mut file = vfs.join(path)?.open_file()?; | |||
let mut data = Data::from_reader(&mut file)?; | |||
data.convert_to(data.format().info().format_gl_support)?; | |||
let mut texture = Texture::new(Target::Texture2D)?; | |||
texture.upload(&data, true)?; | |||
Ok(texture) | |||
} | |||
} |
@@ -1,14 +1,16 @@ | |||
mod misc; | |||
mod render; | |||
mod resources; | |||
mod systems; | |||
use specs::{Dispatcher, DispatcherBuilder, World}; | |||
use crate::Error; | |||
use misc::{events::Events, geometry::Geometry, text::TextManager, vfs::Vfs, window::Window}; | |||
use misc::{Events, TextManager, Window}; | |||
use render::{Debug, Init, Test}; | |||
use systems::{State, StateUpdate}; | |||
use resources::{Camera, Geometry, State, Vfs}; | |||
use systems::StateUpdate; | |||
pub struct App<'a, 'b> { | |||
is_running: bool, | |||
@@ -19,19 +21,21 @@ pub struct App<'a, 'b> { | |||
impl<'a, 'b> App<'a, 'b> { | |||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||
let vfs = Vfs::new()?; | |||
let events = Events::new(world); | |||
let window = Window::new(events.handle())?; | |||
let text_manager = TextManager::new(&vfs)?; | |||
world.insert(vfs); | |||
world.insert(Vfs::new()?); | |||
world.insert(Camera::new()?); | |||
world.insert(Geometry::new()?); | |||
world.insert(State::default()); | |||
let text_manager = TextManager::new(world)?; | |||
let mut dispatcher = DispatcherBuilder::new() | |||
.with(StateUpdate::new(world), "state_update", &[]) | |||
.with(StateUpdate::new(world)?, "state_update", &[]) | |||
.with_thread_local(Init::new(world)?) | |||
.with_thread_local(Test::new(world)?) | |||
.with_thread_local(Debug::new(text_manager)?) | |||
.with_thread_local(Debug::new(&text_manager)?) | |||
.build(); | |||
dispatcher.setup(world); | |||
@@ -1,23 +1,28 @@ | |||
use std::string::ToString; | |||
use log::warn; | |||
use specs::{ReadExpect, System}; | |||
use crate::Error; | |||
use crate::{server::resources::Global, Error}; | |||
use super::{ | |||
super::misc::text::{Text, TextManager}, | |||
Global, | |||
use super::super::{ | |||
misc::{Text, TextCache, TextManager}, | |||
resources::State, | |||
}; | |||
/* Debug */ | |||
pub struct Debug { | |||
cache: TextCache, | |||
text: Text, | |||
fps: usize, | |||
resolution: (u32, u32), | |||
} | |||
impl Debug { | |||
pub fn new(manager: TextManager) -> Result<Self, Error> { | |||
let text = manager | |||
.create_cache()? | |||
pub fn new(text_manager: &TextManager) -> Result<Self, Error> { | |||
let cache = text_manager.create_cache()?; | |||
let text = cache | |||
.new_text() | |||
.scale(12.0) | |||
.font_name("DroidSansMono.ttf") | |||
@@ -30,23 +35,50 @@ impl Debug { | |||
.text("1280 x 720") | |||
.build()?; | |||
Ok(Self { text }) | |||
Ok(Self { | |||
cache, | |||
text, | |||
fps: 0, | |||
resolution: (0, 0), | |||
}) | |||
} | |||
} | |||
impl<'a> System<'a> for Debug { | |||
type SystemData = ReadExpect<'a, Global>; | |||
fn run(&mut self, global: Self::SystemData) { | |||
if global.frame_counter.updated() { | |||
let ret = self | |||
.text | |||
.update(2, format!("{}", global.frame_counter.fps())); | |||
if let Err(err) = ret { | |||
warn!("Unable to update debug text: {}", err); | |||
} | |||
} | |||
type SystemData = (ReadExpect<'a, Global>, ReadExpect<'a, State>); | |||
fn run(&mut self, (global, state): Self::SystemData) { | |||
let guard = self.cache.begin_update(); | |||
update_text( | |||
&mut self.text, | |||
2, | |||
&mut self.fps, | |||
&global.fps, | |||
ToString::to_string, | |||
); | |||
update_text( | |||
&mut self.text, | |||
4, | |||
&mut self.resolution, | |||
&state.resolution, | |||
|(w, h)| format!("{} x {}", w, h), | |||
); | |||
drop(guard); | |||
self.text.render(true); | |||
} | |||
} | |||
fn update_text<T, F>(text: &mut Text, pos: usize, old: &mut T, new: &T, update: F) | |||
where | |||
T: PartialEq + Copy, | |||
F: FnOnce(&T) -> String, | |||
{ | |||
if old != new { | |||
*old = *new; | |||
let s = update(old); | |||
if let Err(err) = text.update(pos, s) { | |||
warn!("Unable to update debug text: {}", err); | |||
} | |||
} | |||
} |
@@ -3,87 +3,53 @@ use glc::{ | |||
shader::{Program, Type}, | |||
}; | |||
use log::error; | |||
use shrev::{EventChannel, ReaderId}; | |||
use specs::{ReadExpect, System, World, WriteExpect}; | |||
use crate::Error; | |||
use super::super::misc::{ | |||
camera::Camera, events::WindowEvent, frame_counter::FrameCounter, geometry::Geometry, | |||
load_program, vfs::Vfs, | |||
use super::super::{ | |||
misc::WorldHelper, | |||
resources::{Camera, Geometry, State}, | |||
}; | |||
/* Global */ | |||
pub struct Global { | |||
pub camera: Camera, | |||
pub frame_counter: FrameCounter, | |||
} | |||
impl Global { | |||
pub fn new() -> Result<Self, Error> { | |||
Ok(Self { | |||
camera: Camera::new()?, | |||
frame_counter: FrameCounter::default(), | |||
}) | |||
} | |||
} | |||
/* Init */ | |||
pub struct Init { | |||
program: Program, | |||
window_events_id: ReaderId<WindowEvent>, | |||
resolution: (u32, u32), | |||
} | |||
impl Init { | |||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||
world.insert(Global::new()?); | |||
let vfs = world.fetch::<Vfs>(); | |||
let program = load_program( | |||
&vfs, | |||
vec![ | |||
(Type::Vertex, "resources/shader/noise.vert"), | |||
(Type::Fragment, "resources/shader/noise.frag"), | |||
], | |||
)?; | |||
let window_events_id = world | |||
.fetch_mut::<EventChannel<WindowEvent>>() | |||
.register_reader(); | |||
let program = world.load_program(vec![ | |||
(Type::Vertex, "resources/shader/noise.vert"), | |||
(Type::Fragment, "resources/shader/noise.frag"), | |||
])?; | |||
Ok(Self { | |||
program, | |||
window_events_id, | |||
resolution: (0, 0), | |||
}) | |||
} | |||
} | |||
impl<'a> System<'a> for Init { | |||
type SystemData = ( | |||
WriteExpect<'a, Global>, | |||
WriteExpect<'a, Camera>, | |||
ReadExpect<'a, State>, | |||
ReadExpect<'a, Geometry>, | |||
ReadExpect<'a, EventChannel<WindowEvent>>, | |||
); | |||
fn run(&mut self, (mut global, geometry, window_events): Self::SystemData) { | |||
let events = window_events.read(&mut self.window_events_id); | |||
for event in events { | |||
if let WindowEvent::Resize(w, h) = event { | |||
gl::viewport(0, 0, *w as gl::GLsizei, *h as gl::GLsizei); | |||
fn run(&mut self, (mut camera, state, geometry): Self::SystemData) { | |||
if self.resolution != state.resolution { | |||
self.resolution = state.resolution; | |||
let w = *w as f32; | |||
let h = *h as f32; | |||
gl::viewport(0, 0, self.resolution.0 as _, self.resolution.1 as _); | |||
if let Err(err) = global.camera.resize(w, h) { | |||
error!("Error while updating camera: {}", err); | |||
panic!("Error while updating camera: {}", err); | |||
} | |||
if let Err(err) = camera.resize(self.resolution.0 as _, self.resolution.1 as _) { | |||
error!("Error while updating camera: {}", err); | |||
panic!("Error while updating camera: {}", err); | |||
} | |||
} | |||
global.frame_counter.next(); | |||
self.program.bind(); | |||
geometry.render_quad(); | |||
self.program.unbind(); | |||
@@ -3,5 +3,5 @@ mod init; | |||
mod test; | |||
pub use debug::Debug; | |||
pub use init::{Global, Init}; | |||
pub use init::Init; | |||
pub use test::Test; |
@@ -7,42 +7,38 @@ use glc::{ | |||
}; | |||
use specs::{ReadExpect, System, World}; | |||
use crate::Error; | |||
use crate::{server::resources::Global, Error}; | |||
use super::{ | |||
super::misc::{geometry::Geometry, load_program, load_texture, vfs::Vfs}, | |||
init::Global, | |||
use super::super::{ | |||
misc::WorldHelper, | |||
resources::{Camera, Geometry}, | |||
}; | |||
pub struct Test { | |||
model: Matrix4f, | |||
program: Program, | |||
texture: Texture, | |||
model: Matrix4f, | |||
} | |||
impl Test { | |||
pub fn new(world: &World) -> Result<Self, Error> { | |||
let vfs = world.fetch::<Vfs>(); | |||
let program = load_program( | |||
&vfs, | |||
vec![ | |||
(Type::Vertex, "resources/shader/quad.vert"), | |||
(Type::Fragment, "resources/shader/quad.frag"), | |||
], | |||
)?; | |||
let mut texture = load_texture(&vfs, "resources/textures/test.png")?; | |||
texture.set_filter(FilterMin::LinearMipmapLinear, FilterMag::Linear)?; | |||
let model = Matrix4f::scale(Vector3f::new(500.0, 500.0, 500.0)); | |||
let global = world.fetch::<Global>(); | |||
let program = world.load_program(vec![ | |||
(Type::Vertex, "resources/shader/quad.vert"), | |||
(Type::Fragment, "resources/shader/quad.frag"), | |||
])?; | |||
program.bind(); | |||
global.camera.bind(0)?; | |||
world.resource::<Camera>()?.bind(0)?; | |||
program.unbind(); | |||
let mut texture = world.load_texture("resources/textures/test.png")?; | |||
texture.set_filter(FilterMin::LinearMipmapLinear, FilterMag::Linear)?; | |||
Ok(Self { | |||
model, | |||
program, | |||
texture, | |||
model: Matrix4f::scale(Vector3f::new(500.0, 500.0, 500.0)), | |||
}) | |||
} | |||
} | |||
@@ -54,7 +50,7 @@ impl<'a> System<'a> for Test { | |||
self.model = self.model | |||
* Matrix4f::rotate( | |||
Vector3f::new(0.0, 0.0, 1.0), | |||
Angle::Deg(10.0 * global.frame_counter.delta()), | |||
Angle::Deg(10.0 * global.delta), | |||
); | |||
self.texture.bind(); | |||
@@ -0,0 +1,9 @@ | |||
mod camera; | |||
mod geometry; | |||
mod state; | |||
mod vfs; | |||
pub use self::vfs::Vfs; | |||
pub use camera::Camera; | |||
pub use geometry::Geometry; | |||
pub use state::State; |
@@ -0,0 +1,6 @@ | |||
#[derive(Default)] | |||
pub struct State { | |||
pub resolution: (u32, u32), | |||
pub close_requested: bool, | |||
} |
@@ -1,3 +1,3 @@ | |||
mod state; | |||
mod state_update; | |||
pub use state::{State, StateUpdate}; | |||
pub use state_update::StateUpdate; |
@@ -1,31 +1,22 @@ | |||
use shrev::{EventChannel, ReaderId}; | |||
use specs::{ReadExpect, System, World, Write}; | |||
use super::super::misc::events::WindowEvent; | |||
use crate::Error; | |||
/* State */ | |||
#[derive(Default)] | |||
pub struct State { | |||
pub close_requested: bool, | |||
pub window_width: u32, | |||
pub window_height: u32, | |||
} | |||
/* StateUpdate*/ | |||
use super::super::{ | |||
misc::{WindowEvent, WorldHelper}, | |||
resources::State, | |||
}; | |||
pub struct StateUpdate { | |||
window_events_id: ReaderId<WindowEvent>, | |||
} | |||
impl StateUpdate { | |||
pub fn new(world: &mut World) -> Self { | |||
let window_events_id = world | |||
.fetch_mut::<EventChannel<WindowEvent>>() | |||
.register_reader(); | |||
pub fn new(world: &mut World) -> Result<Self, Error> { | |||
let window_events_id = world.register_event_reader::<WindowEvent>()?; | |||
Self { window_events_id } | |||
Ok(Self { window_events_id }) | |||
} | |||
} | |||
@@ -37,8 +28,7 @@ impl<'a> System<'a> for StateUpdate { | |||
for event in events { | |||
match event { | |||
WindowEvent::Resize(w, h) => { | |||
state.window_width = *w; | |||
state.window_height = *h; | |||
state.resolution = (*w, *h); | |||
} | |||
WindowEvent::Close => { | |||
state.close_requested = true; |
@@ -30,6 +30,9 @@ pub enum Error { | |||
#[error("Invalid Font: {0}")] | |||
InvalidFont(InvalidFont), | |||
#[error("Resource is not registered: {0}!")] | |||
ResourceNotRegistered(&'static str), | |||
#[error("Unable to create OpenGL context!")] | |||
CreateContext, | |||
@@ -1,15 +1,28 @@ | |||
use specs::World; | |||
pub mod resources; | |||
pub mod systems; | |||
pub struct Server {} | |||
use specs::{Dispatcher, DispatcherBuilder, World}; | |||
impl Server { | |||
use resources::Global; | |||
use systems::Process; | |||
pub struct Server<'a, 'b> { | |||
dispatcher: Dispatcher<'a, 'b>, | |||
} | |||
impl<'a, 'b> Server<'a, 'b> { | |||
pub fn new(world: &mut World) -> Self { | |||
let _world = world; | |||
world.insert(Global::default()); | |||
let mut dispatcher = DispatcherBuilder::new() | |||
.with(Process::default(), "process", &[]) | |||
.build(); | |||
dispatcher.setup(world); | |||
Self {} | |||
Self { dispatcher } | |||
} | |||
pub fn process(&mut self, world: &World) { | |||
let _world = world; | |||
self.dispatcher.dispatch(world); | |||
} | |||
} |
@@ -0,0 +1,5 @@ | |||
#[derive(Default)] | |||
pub struct Global { | |||
pub fps: usize, | |||
pub delta: f32, | |||
} |
@@ -0,0 +1,3 @@ | |||
mod global; | |||
pub use global::Global; |
@@ -0,0 +1,3 @@ | |||
mod process; | |||
pub use process::Process; |
@@ -0,0 +1,48 @@ | |||
#![allow(dead_code)] | |||
use std::mem::take; | |||
use std::time::Instant; | |||
use specs::{System, Write}; | |||
use super::super::resources::Global; | |||
pub struct Process { | |||
last_frame: Instant, | |||
time: f32, | |||
count: usize, | |||
} | |||
impl Default for Process { | |||
fn default() -> Self { | |||
Self { | |||
last_frame: Instant::now(), | |||
time: 0.0, | |||
count: 0, | |||
} | |||
} | |||
} | |||
impl<'a> System<'a> for Process { | |||
type SystemData = Write<'a, Global>; | |||
fn run(&mut self, mut global: Self::SystemData) { | |||
let now = Instant::now(); | |||
global.delta = (now - self.last_frame).as_secs_f32(); | |||
self.last_frame = now; | |||
self.time += global.delta; | |||
self.count += 1; | |||
if self.time >= 2.0 { | |||
self.time = 0.0; | |||
self.count = 0; | |||
global.fps = 0; | |||
} else if self.time >= 1.0 { | |||
self.time -= 1.0; | |||
global.fps = take(&mut self.count); | |||
} | |||
} | |||
} |