| @@ -1483,10 +1483,22 @@ dependencies = [ | |||||
| "hashbrown", | "hashbrown", | ||||
| "mopa", | "mopa", | ||||
| "rayon", | "rayon", | ||||
| "shred-derive", | |||||
| "smallvec", | "smallvec", | ||||
| "tynm", | "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]] | [[package]] | ||||
| name = "shrev" | name = "shrev" | ||||
| version = "1.1.1" | version = "1.1.1" | ||||
| @@ -1539,6 +1551,7 @@ dependencies = [ | |||||
| "num_cpus", | "num_cpus", | ||||
| "ordered-float 2.0.0", | "ordered-float 2.0.0", | ||||
| "rand", | "rand", | ||||
| "shred", | |||||
| "shrev", | "shrev", | ||||
| "specs", | "specs", | ||||
| "thiserror", | "thiserror", | ||||
| @@ -15,6 +15,7 @@ log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn | |||||
| num_cpus = "1.13" | num_cpus = "1.13" | ||||
| ordered-float = "2.0" | ordered-float = "2.0" | ||||
| rand = "0.7" | rand = "0.7" | ||||
| shred = "0.10" | |||||
| shrev = "1.1" | shrev = "1.1" | ||||
| specs = "0.16" | specs = "0.16" | ||||
| thiserror = "1.0" | 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::fmt::Display; | ||||
| use std::hash::{Hash, Hasher}; | use std::hash::{Hash, Hasher}; | ||||
| use std::mem::size_of; | use std::mem::size_of; | ||||
| use std::ops::Deref; | |||||
| use std::ptr::null; | use std::ptr::null; | ||||
| use std::rc::{Rc, Weak}; | use std::rc::{Rc, Weak}; | ||||
| @@ -25,10 +26,11 @@ use glyph_brush::{ | |||||
| }; | }; | ||||
| use log::warn; | use log::warn; | ||||
| use ordered_float::OrderedFloat; | use ordered_float::OrderedFloat; | ||||
| use specs::World; | |||||
| use crate::Error; | use crate::Error; | ||||
| use super::{load_program, vfs::Vfs}; | |||||
| use super::super::{misc::WorldHelper, resources::Vfs}; | |||||
| /* TextManager */ | /* TextManager */ | ||||
| @@ -42,15 +44,12 @@ struct TextManagerInner { | |||||
| } | } | ||||
| impl TextManager { | 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); | let program = Rc::new(program); | ||||
| Ok(Self(Rc::new(RefCell::new(TextManagerInner { | Ok(Self(Rc::new(RefCell::new(TextManagerInner { | ||||
| @@ -109,8 +108,12 @@ pub struct TextCacheInner { | |||||
| texts: HashMap<usize, Weak<RefCell<TextInner>>>, | texts: HashMap<usize, Weak<RefCell<TextInner>>>, | ||||
| text_ids: Vec<usize>, | text_ids: Vec<usize>, | ||||
| next_text_id: usize, | next_text_id: usize, | ||||
| update_counter: usize, | |||||
| update_requested: bool, | |||||
| } | } | ||||
| pub struct UpdateGuard(TextCache); | |||||
| impl TextCache { | impl TextCache { | ||||
| fn new(manager: &TextManager) -> Result<Self, Error> { | fn new(manager: &TextManager) -> Result<Self, Error> { | ||||
| let mut texture = Texture::new(TextureTarget::Texture2D)?; | let mut texture = Texture::new(TextureTarget::Texture2D)?; | ||||
| @@ -125,6 +128,8 @@ impl TextCache { | |||||
| texts: HashMap::new(), | texts: HashMap::new(), | ||||
| text_ids: Vec::new(), | text_ids: Vec::new(), | ||||
| next_text_id: 0, | next_text_id: 0, | ||||
| update_counter: 0, | |||||
| update_requested: false, | |||||
| })))) | })))) | ||||
| } | } | ||||
| @@ -132,6 +137,34 @@ impl TextCache { | |||||
| TextBuilder::new(self) | 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> { | fn font_by_name(&self, name: &str) -> Result<FontId, Error> { | ||||
| let mut inner = self.0.borrow_mut(); | let mut inner = self.0.borrow_mut(); | ||||
| @@ -186,6 +219,12 @@ impl TextCache { | |||||
| let mut inner = self.0.borrow_mut(); | let mut inner = self.0.borrow_mut(); | ||||
| let inner = &mut *inner; | let inner = &mut *inner; | ||||
| if inner.update_counter > 0 { | |||||
| inner.update_requested = true; | |||||
| return Ok(()); | |||||
| } | |||||
| /* remove droped texts */ | /* remove droped texts */ | ||||
| let texts = &mut inner.texts; | let texts = &mut inner.texts; | ||||
| let text_ids = &mut inner.text_ids; | let text_ids = &mut inner.text_ids; | ||||
| @@ -251,6 +290,8 @@ impl TextCache { | |||||
| } | } | ||||
| } | } | ||||
| inner.update_requested = false; | |||||
| Ok(()) | 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> { | fn resize_texture(new_size: (u32, u32), cur_size: Option<(u32, u32)>) -> Result<(u32, u32), Error> { | ||||
| let mut max_size = 0; | let mut max_size = 0; | ||||
| gl::get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_size); | 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 misc; | ||||
| mod render; | mod render; | ||||
| mod resources; | |||||
| mod systems; | mod systems; | ||||
| use specs::{Dispatcher, DispatcherBuilder, World}; | use specs::{Dispatcher, DispatcherBuilder, World}; | ||||
| use crate::Error; | 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 render::{Debug, Init, Test}; | ||||
| use systems::{State, StateUpdate}; | |||||
| use resources::{Camera, Geometry, State, Vfs}; | |||||
| use systems::StateUpdate; | |||||
| pub struct App<'a, 'b> { | pub struct App<'a, 'b> { | ||||
| is_running: bool, | is_running: bool, | ||||
| @@ -19,19 +21,21 @@ pub struct App<'a, 'b> { | |||||
| impl<'a, 'b> App<'a, 'b> { | impl<'a, 'b> App<'a, 'b> { | ||||
| pub fn new(world: &mut World) -> Result<Self, Error> { | pub fn new(world: &mut World) -> Result<Self, Error> { | ||||
| let vfs = Vfs::new()?; | |||||
| let events = Events::new(world); | let events = Events::new(world); | ||||
| let window = Window::new(events.handle())?; | 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(Geometry::new()?); | ||||
| world.insert(State::default()); | |||||
| let text_manager = TextManager::new(world)?; | |||||
| let mut dispatcher = DispatcherBuilder::new() | 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(Init::new(world)?) | ||||
| .with_thread_local(Test::new(world)?) | .with_thread_local(Test::new(world)?) | ||||
| .with_thread_local(Debug::new(text_manager)?) | |||||
| .with_thread_local(Debug::new(&text_manager)?) | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -1,23 +1,28 @@ | |||||
| use std::string::ToString; | |||||
| use log::warn; | use log::warn; | ||||
| use specs::{ReadExpect, System}; | 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 */ | /* Debug */ | ||||
| pub struct Debug { | pub struct Debug { | ||||
| cache: TextCache, | |||||
| text: Text, | text: Text, | ||||
| fps: usize, | |||||
| resolution: (u32, u32), | |||||
| } | } | ||||
| impl Debug { | 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() | .new_text() | ||||
| .scale(12.0) | .scale(12.0) | ||||
| .font_name("DroidSansMono.ttf") | .font_name("DroidSansMono.ttf") | ||||
| @@ -30,23 +35,50 @@ impl Debug { | |||||
| .text("1280 x 720") | .text("1280 x 720") | ||||
| .build()?; | .build()?; | ||||
| Ok(Self { text }) | |||||
| Ok(Self { | |||||
| cache, | |||||
| text, | |||||
| fps: 0, | |||||
| resolution: (0, 0), | |||||
| }) | |||||
| } | } | ||||
| } | } | ||||
| impl<'a> System<'a> for Debug { | 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); | 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}, | shader::{Program, Type}, | ||||
| }; | }; | ||||
| use log::error; | use log::error; | ||||
| use shrev::{EventChannel, ReaderId}; | |||||
| use specs::{ReadExpect, System, World, WriteExpect}; | use specs::{ReadExpect, System, World, WriteExpect}; | ||||
| use crate::Error; | 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 { | pub struct Init { | ||||
| program: Program, | program: Program, | ||||
| window_events_id: ReaderId<WindowEvent>, | |||||
| resolution: (u32, u32), | |||||
| } | } | ||||
| impl Init { | impl Init { | ||||
| pub fn new(world: &mut World) -> Result<Self, Error> { | 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 { | Ok(Self { | ||||
| program, | program, | ||||
| window_events_id, | |||||
| resolution: (0, 0), | |||||
| }) | }) | ||||
| } | } | ||||
| } | } | ||||
| impl<'a> System<'a> for Init { | impl<'a> System<'a> for Init { | ||||
| type SystemData = ( | type SystemData = ( | ||||
| WriteExpect<'a, Global>, | |||||
| WriteExpect<'a, Camera>, | |||||
| ReadExpect<'a, State>, | |||||
| ReadExpect<'a, Geometry>, | 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(); | self.program.bind(); | ||||
| geometry.render_quad(); | geometry.render_quad(); | ||||
| self.program.unbind(); | self.program.unbind(); | ||||
| @@ -3,5 +3,5 @@ mod init; | |||||
| mod test; | mod test; | ||||
| pub use debug::Debug; | pub use debug::Debug; | ||||
| pub use init::{Global, Init}; | |||||
| pub use init::Init; | |||||
| pub use test::Test; | pub use test::Test; | ||||
| @@ -7,42 +7,38 @@ use glc::{ | |||||
| }; | }; | ||||
| use specs::{ReadExpect, System, World}; | 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 { | pub struct Test { | ||||
| model: Matrix4f, | |||||
| program: Program, | program: Program, | ||||
| texture: Texture, | texture: Texture, | ||||
| model: Matrix4f, | |||||
| } | } | ||||
| impl Test { | impl Test { | ||||
| pub fn new(world: &World) -> Result<Self, Error> { | 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(); | program.bind(); | ||||
| global.camera.bind(0)?; | |||||
| world.resource::<Camera>()?.bind(0)?; | |||||
| program.unbind(); | program.unbind(); | ||||
| let mut texture = world.load_texture("resources/textures/test.png")?; | |||||
| texture.set_filter(FilterMin::LinearMipmapLinear, FilterMag::Linear)?; | |||||
| Ok(Self { | Ok(Self { | ||||
| model, | |||||
| program, | program, | ||||
| texture, | 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 | self.model = self.model | ||||
| * Matrix4f::rotate( | * Matrix4f::rotate( | ||||
| Vector3f::new(0.0, 0.0, 1.0), | 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(); | 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 shrev::{EventChannel, ReaderId}; | ||||
| use specs::{ReadExpect, System, World, Write}; | 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 { | pub struct StateUpdate { | ||||
| window_events_id: ReaderId<WindowEvent>, | window_events_id: ReaderId<WindowEvent>, | ||||
| } | } | ||||
| impl StateUpdate { | 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 { | for event in events { | ||||
| match event { | match event { | ||||
| WindowEvent::Resize(w, h) => { | WindowEvent::Resize(w, h) => { | ||||
| state.window_width = *w; | |||||
| state.window_height = *h; | |||||
| state.resolution = (*w, *h); | |||||
| } | } | ||||
| WindowEvent::Close => { | WindowEvent::Close => { | ||||
| state.close_requested = true; | state.close_requested = true; | ||||
| @@ -30,6 +30,9 @@ pub enum Error { | |||||
| #[error("Invalid Font: {0}")] | #[error("Invalid Font: {0}")] | ||||
| InvalidFont(InvalidFont), | InvalidFont(InvalidFont), | ||||
| #[error("Resource is not registered: {0}!")] | |||||
| ResourceNotRegistered(&'static str), | |||||
| #[error("Unable to create OpenGL context!")] | #[error("Unable to create OpenGL context!")] | ||||
| CreateContext, | 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 { | 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) { | 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); | |||||
| } | |||||
| } | |||||
| } | |||||