| @@ -6,6 +6,12 @@ version = "0.1.4" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" | checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" | ||||
| [[package]] | |||||
| name = "adler32" | |||||
| version = "1.2.0" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" | |||||
| [[package]] | [[package]] | ||||
| name = "ahash" | name = "ahash" | ||||
| version = "0.3.8" | version = "0.3.8" | ||||
| @@ -87,6 +93,27 @@ version = "1.3.4" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | ||||
| [[package]] | |||||
| name = "bzip2" | |||||
| version = "0.3.3" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" | |||||
| dependencies = [ | |||||
| "bzip2-sys", | |||||
| "libc", | |||||
| ] | |||||
| [[package]] | |||||
| name = "bzip2-sys" | |||||
| version = "0.1.9+1.0.8" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "ad3b39a260062fca31f7b0b12f207e8f2590a67d32ec7d59c20484b07ea7285e" | |||||
| dependencies = [ | |||||
| "cc", | |||||
| "libc", | |||||
| "pkg-config", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "calloop" | name = "calloop" | ||||
| version = "0.6.5" | version = "0.6.5" | ||||
| @@ -252,6 +279,15 @@ dependencies = [ | |||||
| "objc", | "objc", | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "crc32fast" | |||||
| version = "1.2.1" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" | |||||
| dependencies = [ | |||||
| "cfg-if 1.0.0", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "crossbeam-channel" | name = "crossbeam-channel" | ||||
| version = "0.5.0" | version = "0.5.0" | ||||
| @@ -406,6 +442,18 @@ dependencies = [ | |||||
| "termcolor", | "termcolor", | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "flate2" | |||||
| version = "1.0.14" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" | |||||
| dependencies = [ | |||||
| "cfg-if 0.1.10", | |||||
| "crc32fast", | |||||
| "libc", | |||||
| "miniz_oxide", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "fnv" | name = "fnv" | ||||
| version = "1.0.7" | version = "1.0.7" | ||||
| @@ -817,6 +865,15 @@ dependencies = [ | |||||
| "autocfg", | "autocfg", | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "miniz_oxide" | |||||
| version = "0.3.7" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" | |||||
| dependencies = [ | |||||
| "adler32", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "mio" | name = "mio" | ||||
| version = "0.6.22" | version = "0.6.22" | ||||
| @@ -1348,6 +1405,8 @@ dependencies = [ | |||||
| "specs", | "specs", | ||||
| "thiserror", | "thiserror", | ||||
| "tokio", | "tokio", | ||||
| "vfs", | |||||
| "vfs-zip", | |||||
| ] | ] | ||||
| [[package]] | [[package]] | ||||
| @@ -1473,6 +1532,26 @@ version = "0.9.2" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" | ||||
| [[package]] | |||||
| name = "vfs" | |||||
| version = "0.4.0" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "7cc382cffc2f8d6f9490b96d42a7408d6aa719c2db86491b8b31780208ac9d86" | |||||
| dependencies = [ | |||||
| "thiserror", | |||||
| ] | |||||
| [[package]] | |||||
| name = "vfs-zip" | |||||
| version = "0.2.1" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "d68a369614cd12ca384ca6e75ab0a5ac3a5acbfe8003622e20808a322b9bb652" | |||||
| dependencies = [ | |||||
| "flate2", | |||||
| "vfs", | |||||
| "zip", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "walkdir" | name = "walkdir" | ||||
| version = "2.3.1" | version = "2.3.1" | ||||
| @@ -1689,3 +1768,16 @@ name = "xml-rs" | |||||
| version = "0.8.3" | version = "0.8.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" | checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" | ||||
| [[package]] | |||||
| name = "zip" | |||||
| version = "0.5.8" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| checksum = "543adf038106b64cfca4711c82c917d785e3540e04f7996554488f988ec43124" | |||||
| dependencies = [ | |||||
| "byteorder", | |||||
| "bzip2", | |||||
| "crc32fast", | |||||
| "flate2", | |||||
| "thiserror", | |||||
| ] | |||||
| @@ -1,4 +1,5 @@ | |||||
| use std::fs::read_to_string; | |||||
| use std::fs::File; | |||||
| use std::io::Read; | |||||
| use std::path::Path; | use std::path::Path; | ||||
| use std::ptr::{null, null_mut}; | use std::ptr::{null, null_mut}; | ||||
| use std::str::from_utf8; | use std::str::from_utf8; | ||||
| @@ -24,9 +25,10 @@ impl Program { | |||||
| Self::from_shaders_result(iter.into_iter().map(Ok)) | Self::from_shaders_result(iter.into_iter().map(Ok)) | ||||
| } | } | ||||
| pub fn from_shaders_result<I>(iter: I) -> Result<Self, Error> | |||||
| pub fn from_shaders_result<I, E>(iter: I) -> Result<Self, E> | |||||
| where | where | ||||
| I: IntoIterator<Item = Result<Shader, Error>>, | |||||
| I: IntoIterator<Item = Result<Shader, E>>, | |||||
| E: From<Error>, | |||||
| { | { | ||||
| let id = gl::create_program(); | let id = gl::create_program(); | ||||
| let id = Error::err_if(&0, id)?; | let id = Error::err_if(&0, id)?; | ||||
| @@ -59,9 +61,9 @@ impl Program { | |||||
| buffer.as_mut_ptr() as *mut gl::types::GLchar, | buffer.as_mut_ptr() as *mut gl::types::GLchar, | ||||
| ); | ); | ||||
| let msg = from_utf8(&buffer)?; | |||||
| let msg = from_utf8(&buffer).map_err(Error::Utf8Error)?; | |||||
| return Err(Error::ShaderLink(msg.into())); | |||||
| return Err(Error::ShaderLink(msg.into()).into()); | |||||
| } | } | ||||
| Ok(Program { id }) | Ok(Program { id }) | ||||
| @@ -140,13 +142,23 @@ impl Shader { | |||||
| Ok(Self { id }) | Ok(Self { id }) | ||||
| } | } | ||||
| pub fn from_reader<R>(type_: Type, mut reader: R) -> Result<Self, Error> | |||||
| where | |||||
| R: Read, | |||||
| { | |||||
| let mut source = String::new(); | |||||
| reader.read_to_string(&mut source)?; | |||||
| Self::from_string(type_, source) | |||||
| } | |||||
| pub fn from_file<P>(type_: Type, path: P) -> Result<Self, Error> | pub fn from_file<P>(type_: Type, path: P) -> Result<Self, Error> | ||||
| where | where | ||||
| P: AsRef<Path>, | P: AsRef<Path>, | ||||
| { | { | ||||
| let source = read_to_string(&path)?; | |||||
| let source = File::open(&path)?; | |||||
| match Self::from_string(type_, source) { | |||||
| match Self::from_reader(type_, source) { | |||||
| Ok(v) => Ok(v), | Ok(v) => Ok(v), | ||||
| Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile { | Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile { | ||||
| code: format!("{}\n{}", path.as_ref().display(), code), | code: format!("{}\n{}", path.as_ref().display(), code), | ||||
| @@ -17,3 +17,5 @@ shrev = "1.1" | |||||
| specs = "0.16" | specs = "0.16" | ||||
| thiserror = "1.0" | thiserror = "1.0" | ||||
| tokio = "0.3" | tokio = "0.3" | ||||
| vfs = "0.4" | |||||
| vfs-zip = "0.2" | |||||
| @@ -2,4 +2,25 @@ pub mod camera; | |||||
| pub mod events; | pub mod events; | ||||
| pub mod frame_counter; | pub mod frame_counter; | ||||
| pub mod geometry; | pub mod geometry; | ||||
| pub mod vfs; | |||||
| pub mod window; | pub mod window; | ||||
| use std::iter::Iterator; | |||||
| use std::ops::Deref; | |||||
| use self::vfs::Vfs; | |||||
| use glc::shader::{Program, Shader, Type}; | |||||
| 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 file = vfs.deref().join(p)?.open_file()?; | |||||
| let shader = Shader::from_reader(t, file)?; | |||||
| Ok(shader) | |||||
| })) | |||||
| } | |||||
| @@ -0,0 +1,72 @@ | |||||
| use std::collections::HashSet; | |||||
| use std::env::{current_dir, current_exe}; | |||||
| use std::fs::File; | |||||
| use std::ops::Deref; | |||||
| use log::info; | |||||
| use vfs::{ | |||||
| impls::{overlay::OverlayFS, physical::PhysicalFS}, | |||||
| VfsPath, | |||||
| }; | |||||
| use vfs_zip::ZipReadOnly as ZipFS; | |||||
| use crate::Error; | |||||
| pub struct Vfs(pub VfsPath); | |||||
| impl Vfs { | |||||
| pub fn new() -> Result<Self, Error> { | |||||
| let dirs = vec![ | |||||
| current_exe() | |||||
| .ok() | |||||
| .as_ref() | |||||
| .and_then(|p| p.parent()) | |||||
| .map(|p| p.to_owned()), | |||||
| current_exe() | |||||
| .ok() | |||||
| .as_ref() | |||||
| .and_then(|p| p.parent()) | |||||
| .map(|p| p.join("space-crush")), | |||||
| current_dir().ok(), | |||||
| current_dir().ok().map(|p| p.join("space-crush")), | |||||
| ] | |||||
| .into_iter() | |||||
| .filter_map(|d| d); | |||||
| let mut paths = HashSet::new(); | |||||
| let mut layers = Vec::new(); | |||||
| for dir in dirs.clone() { | |||||
| if paths.insert(dir.clone()) { | |||||
| info!("Adding layer to VFS: {}", dir.display()); | |||||
| let layer = VfsPath::new(PhysicalFS::new(dir)); | |||||
| layers.push(layer); | |||||
| } | |||||
| } | |||||
| for dir in dirs { | |||||
| let path = dir.join("resources.bin"); | |||||
| if path.is_file() && paths.insert(path.to_owned()) { | |||||
| info!("Adding layer to VFS: {}", dir.display()); | |||||
| let zip = File::open(path)?; | |||||
| let layer = VfsPath::new(ZipFS::new_relaxed(zip)?); | |||||
| layers.push(layer); | |||||
| } | |||||
| } | |||||
| Ok(Self(VfsPath::new(OverlayFS::new(&layers)))) | |||||
| } | |||||
| } | |||||
| impl Deref for Vfs { | |||||
| type Target = VfsPath; | |||||
| fn deref(&self) -> &Self::Target { | |||||
| &self.0 | |||||
| } | |||||
| } | |||||
| @@ -19,7 +19,7 @@ impl Window { | |||||
| let context = loop { | let context = loop { | ||||
| let multisampling = match multisampling.next() { | let multisampling = match multisampling.next() { | ||||
| Some(multisampling) => multisampling, | Some(multisampling) => multisampling, | ||||
| None => return Err(Error::UnableToCreateContext), | |||||
| None => return Err(Error::CreateContext), | |||||
| }; | }; | ||||
| info!("Create OpenGL context (multisampling={})", multisampling); | info!("Create OpenGL context (multisampling={})", multisampling); | ||||
| @@ -6,7 +6,7 @@ use specs::{Dispatcher, DispatcherBuilder, World}; | |||||
| use crate::Error; | use crate::Error; | ||||
| use misc::{events::Events, geometry::Geometry, window::Window}; | |||||
| use misc::{events::Events, geometry::Geometry, vfs::Vfs, window::Window}; | |||||
| use render::{Init, Test}; | use render::{Init, Test}; | ||||
| use systems::{State, StateUpdate}; | use systems::{State, StateUpdate}; | ||||
| @@ -22,6 +22,9 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let events = Events::new(world); | let events = Events::new(world); | ||||
| let window = Window::new(events.handle())?; | let window = Window::new(events.handle())?; | ||||
| world.insert(Vfs::new()?); | |||||
| world.insert(Geometry::new()?); | |||||
| 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)?) | ||||
| @@ -29,8 +32,6 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| world.insert(Geometry::new()?); | |||||
| Ok(Self { | Ok(Self { | ||||
| is_running: true, | is_running: true, | ||||
| events, | events, | ||||
| @@ -1,6 +1,6 @@ | |||||
| use glc::{ | use glc::{ | ||||
| misc::Bindable, | misc::Bindable, | ||||
| shader::{Program, Shader, Type}, | |||||
| shader::{Program, Type}, | |||||
| }; | }; | ||||
| use log::{error, info}; | use log::{error, info}; | ||||
| use shrev::{EventChannel, ReaderId}; | use shrev::{EventChannel, ReaderId}; | ||||
| @@ -10,6 +10,7 @@ use crate::Error; | |||||
| use super::super::misc::{ | use super::super::misc::{ | ||||
| camera::Camera, events::WindowEvent, frame_counter::FrameCounter, geometry::Geometry, | camera::Camera, events::WindowEvent, frame_counter::FrameCounter, geometry::Geometry, | ||||
| load_program, vfs::Vfs, | |||||
| }; | }; | ||||
| /* Global */ | /* Global */ | ||||
| @@ -37,21 +38,20 @@ pub struct Init { | |||||
| impl Init { | impl Init { | ||||
| pub fn new(world: &mut World) -> Result<Self, Error> { | pub fn new(world: &mut World) -> Result<Self, Error> { | ||||
| let shaders = vec![ | |||||
| (Type::Vertex, include_str!("shader/noise.vert")), | |||||
| (Type::Fragment, include_str!("shader/noise.frag")), | |||||
| ]; | |||||
| let program = Program::from_shaders_result( | |||||
| shaders | |||||
| .into_iter() | |||||
| .map(|(t, s)| Shader::from_string(t, s.into())), | |||||
| 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 | let window_events_id = world | ||||
| .fetch_mut::<EventChannel<WindowEvent>>() | .fetch_mut::<EventChannel<WindowEvent>>() | ||||
| .register_reader(); | .register_reader(); | ||||
| world.insert(Global::new()?); | |||||
| Ok(Self { | Ok(Self { | ||||
| program, | program, | ||||
| window_events_id, | window_events_id, | ||||
| @@ -1,14 +1,17 @@ | |||||
| use glc::{ | use glc::{ | ||||
| matrix::{Angle, Matrix4f}, | matrix::{Angle, Matrix4f}, | ||||
| misc::Bindable, | misc::Bindable, | ||||
| shader::{Program, Shader, Type, Uniform}, | |||||
| shader::{Program, Type, Uniform}, | |||||
| vector::Vector3f, | vector::Vector3f, | ||||
| }; | }; | ||||
| use specs::{ReadExpect, System, World}; | use specs::{ReadExpect, System, World}; | ||||
| use crate::Error; | use crate::Error; | ||||
| use super::{super::misc::geometry::Geometry, init::Global}; | |||||
| use super::{ | |||||
| super::misc::{geometry::Geometry, load_program, vfs::Vfs}, | |||||
| init::Global, | |||||
| }; | |||||
| pub struct Test { | pub struct Test { | ||||
| program: Program, | program: Program, | ||||
| @@ -17,14 +20,13 @@ pub struct Test { | |||||
| impl Test { | impl Test { | ||||
| pub fn new(world: &World) -> Result<Self, Error> { | pub fn new(world: &World) -> Result<Self, Error> { | ||||
| let shaders = vec![ | |||||
| (Type::Vertex, include_str!("shader/quad.vert")), | |||||
| (Type::Fragment, include_str!("shader/quad.frag")), | |||||
| ]; | |||||
| let program = Program::from_shaders_result( | |||||
| shaders | |||||
| .into_iter() | |||||
| .map(|(t, s)| Shader::from_string(t, s.into())), | |||||
| let vfs = world.fetch::<Vfs>(); | |||||
| let program = load_program( | |||||
| &vfs, | |||||
| vec![ | |||||
| (Type::Vertex, "resources/shader/quad.vert"), | |||||
| (Type::Fragment, "resources/shader/quad.frag"), | |||||
| ], | |||||
| )?; | )?; | ||||
| let global = world.fetch::<Global>(); | let global = world.fetch::<Global>(); | ||||
| @@ -1,9 +1,22 @@ | |||||
| use std::io::Error as IoError; | |||||
| use glc::error::Error as GlcError; | use glc::error::Error as GlcError; | ||||
| use glutin::{ContextError as GlutinContextError, CreationError as GlutinCreationError}; | use glutin::{ContextError as GlutinContextError, CreationError as GlutinCreationError}; | ||||
| use thiserror::Error; | use thiserror::Error; | ||||
| use vfs::VfsError; | |||||
| use vfs_zip::Error as VfsZipError; | |||||
| #[derive(Debug, Error)] | #[derive(Debug, Error)] | ||||
| pub enum Error { | pub enum Error { | ||||
| #[error("IO Error: {0}")] | |||||
| IoError(IoError), | |||||
| #[error("VFS Error: {0}")] | |||||
| VfsError(VfsError), | |||||
| #[error("VFS ZIP Error: {0}")] | |||||
| VfsZipError(VfsZipError), | |||||
| #[error("GLC Error: {0}")] | #[error("GLC Error: {0}")] | ||||
| GlcError(GlcError), | GlcError(GlcError), | ||||
| @@ -14,7 +27,28 @@ pub enum Error { | |||||
| GlutinCreationError(GlutinCreationError), | GlutinCreationError(GlutinCreationError), | ||||
| #[error("Unable to create OpenGL context")] | #[error("Unable to create OpenGL context")] | ||||
| UnableToCreateContext, | |||||
| CreateContext, | |||||
| #[error("Unable to initialize VFS")] | |||||
| InitVFS, | |||||
| } | |||||
| impl From<IoError> for Error { | |||||
| fn from(err: IoError) -> Self { | |||||
| Self::IoError(err) | |||||
| } | |||||
| } | |||||
| impl From<VfsError> for Error { | |||||
| fn from(err: VfsError) -> Self { | |||||
| Self::VfsError(err) | |||||
| } | |||||
| } | |||||
| impl From<VfsZipError> for Error { | |||||
| fn from(err: VfsZipError) -> Self { | |||||
| Self::VfsZipError(err) | |||||
| } | |||||
| } | } | ||||
| impl From<GlcError> for Error { | impl From<GlcError> for Error { | ||||