diff --git a/Cargo.lock b/Cargo.lock index 13d7f77..11dd0a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.3.8" @@ -87,6 +93,27 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "calloop" version = "0.6.5" @@ -252,6 +279,15 @@ dependencies = [ "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]] name = "crossbeam-channel" version = "0.5.0" @@ -406,6 +442,18 @@ dependencies = [ "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]] name = "fnv" version = "1.0.7" @@ -817,6 +865,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + [[package]] name = "mio" version = "0.6.22" @@ -1348,6 +1405,8 @@ dependencies = [ "specs", "thiserror", "tokio", + "vfs", + "vfs-zip", ] [[package]] @@ -1473,6 +1532,26 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "walkdir" version = "2.3.1" @@ -1689,3 +1768,16 @@ name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" 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", +] diff --git a/glc/src/shader.rs b/glc/src/shader.rs index 37f1653..d68b033 100644 --- a/glc/src/shader.rs +++ b/glc/src/shader.rs @@ -1,4 +1,5 @@ -use std::fs::read_to_string; +use std::fs::File; +use std::io::Read; use std::path::Path; use std::ptr::{null, null_mut}; use std::str::from_utf8; @@ -24,9 +25,10 @@ impl Program { Self::from_shaders_result(iter.into_iter().map(Ok)) } - pub fn from_shaders_result(iter: I) -> Result + pub fn from_shaders_result(iter: I) -> Result where - I: IntoIterator>, + I: IntoIterator>, + E: From, { let id = gl::create_program(); let id = Error::err_if(&0, id)?; @@ -59,9 +61,9 @@ impl Program { 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 }) @@ -140,13 +142,23 @@ impl Shader { Ok(Self { id }) } + pub fn from_reader(type_: Type, mut reader: R) -> Result + where + R: Read, + { + let mut source = String::new(); + reader.read_to_string(&mut source)?; + + Self::from_string(type_, source) + } + pub fn from_file

(type_: Type, path: P) -> Result where P: AsRef, { - 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), Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile { code: format!("{}\n{}", path.as_ref().display(), code), diff --git a/space-crush/Cargo.toml b/space-crush/Cargo.toml index c2e48e8..f0b9f06 100644 --- a/space-crush/Cargo.toml +++ b/space-crush/Cargo.toml @@ -17,3 +17,5 @@ shrev = "1.1" specs = "0.16" thiserror = "1.0" tokio = "0.3" +vfs = "0.4" +vfs-zip = "0.2" diff --git a/space-crush/src/app/render/shader/noise.frag b/space-crush/resources/shader/noise.frag similarity index 100% rename from space-crush/src/app/render/shader/noise.frag rename to space-crush/resources/shader/noise.frag diff --git a/space-crush/src/app/render/shader/noise.vert b/space-crush/resources/shader/noise.vert similarity index 100% rename from space-crush/src/app/render/shader/noise.vert rename to space-crush/resources/shader/noise.vert diff --git a/space-crush/src/app/render/shader/quad.frag b/space-crush/resources/shader/quad.frag similarity index 100% rename from space-crush/src/app/render/shader/quad.frag rename to space-crush/resources/shader/quad.frag diff --git a/space-crush/src/app/render/shader/quad.vert b/space-crush/resources/shader/quad.vert similarity index 100% rename from space-crush/src/app/render/shader/quad.vert rename to space-crush/resources/shader/quad.vert diff --git a/space-crush/src/app/misc/mod.rs b/space-crush/src/app/misc/mod.rs index b7554ac..cf15123 100644 --- a/space-crush/src/app/misc/mod.rs +++ b/space-crush/src/app/misc/mod.rs @@ -2,4 +2,25 @@ pub mod camera; pub mod events; pub mod frame_counter; pub mod geometry; +pub mod vfs; 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(vfs: &Vfs, iter: I) -> Result +where + I: IntoIterator, +{ + 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) + })) +} diff --git a/space-crush/src/app/misc/vfs.rs b/space-crush/src/app/misc/vfs.rs new file mode 100644 index 0000000..c3f25e7 --- /dev/null +++ b/space-crush/src/app/misc/vfs.rs @@ -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 { + 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 + } +} diff --git a/space-crush/src/app/misc/window.rs b/space-crush/src/app/misc/window.rs index 2de22c4..140108c 100644 --- a/space-crush/src/app/misc/window.rs +++ b/space-crush/src/app/misc/window.rs @@ -19,7 +19,7 @@ impl Window { let context = loop { let multisampling = match multisampling.next() { Some(multisampling) => multisampling, - None => return Err(Error::UnableToCreateContext), + None => return Err(Error::CreateContext), }; info!("Create OpenGL context (multisampling={})", multisampling); diff --git a/space-crush/src/app/mod.rs b/space-crush/src/app/mod.rs index 38afb8b..cd687dd 100644 --- a/space-crush/src/app/mod.rs +++ b/space-crush/src/app/mod.rs @@ -6,7 +6,7 @@ use specs::{Dispatcher, DispatcherBuilder, World}; 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 systems::{State, StateUpdate}; @@ -22,6 +22,9 @@ impl<'a, 'b> App<'a, 'b> { let events = Events::new(world); let window = Window::new(events.handle())?; + world.insert(Vfs::new()?); + world.insert(Geometry::new()?); + let mut dispatcher = DispatcherBuilder::new() .with(StateUpdate::new(world), "state_update", &[]) .with_thread_local(Init::new(world)?) @@ -29,8 +32,6 @@ impl<'a, 'b> App<'a, 'b> { .build(); dispatcher.setup(world); - world.insert(Geometry::new()?); - Ok(Self { is_running: true, events, diff --git a/space-crush/src/app/render/init.rs b/space-crush/src/app/render/init.rs index 1fc0ecd..24224e5 100644 --- a/space-crush/src/app/render/init.rs +++ b/space-crush/src/app/render/init.rs @@ -1,6 +1,6 @@ use glc::{ misc::Bindable, - shader::{Program, Shader, Type}, + shader::{Program, Type}, }; use log::{error, info}; use shrev::{EventChannel, ReaderId}; @@ -10,6 +10,7 @@ use crate::Error; use super::super::misc::{ camera::Camera, events::WindowEvent, frame_counter::FrameCounter, geometry::Geometry, + load_program, vfs::Vfs, }; /* Global */ @@ -37,21 +38,20 @@ pub struct Init { impl Init { pub fn new(world: &mut World) -> Result { - 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::(); + 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::>() .register_reader(); - world.insert(Global::new()?); - Ok(Self { program, window_events_id, diff --git a/space-crush/src/app/render/test.rs b/space-crush/src/app/render/test.rs index 162e481..c77061f 100644 --- a/space-crush/src/app/render/test.rs +++ b/space-crush/src/app/render/test.rs @@ -1,14 +1,17 @@ use glc::{ matrix::{Angle, Matrix4f}, misc::Bindable, - shader::{Program, Shader, Type, Uniform}, + shader::{Program, Type, Uniform}, vector::Vector3f, }; use specs::{ReadExpect, System, World}; 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 { program: Program, @@ -17,14 +20,13 @@ pub struct Test { impl Test { pub fn new(world: &World) -> Result { - 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::(); + let program = load_program( + &vfs, + vec![ + (Type::Vertex, "resources/shader/quad.vert"), + (Type::Fragment, "resources/shader/quad.frag"), + ], )?; let global = world.fetch::(); diff --git a/space-crush/src/error.rs b/space-crush/src/error.rs index 94cf572..18bce4d 100644 --- a/space-crush/src/error.rs +++ b/space-crush/src/error.rs @@ -1,9 +1,22 @@ +use std::io::Error as IoError; + use glc::error::Error as GlcError; use glutin::{ContextError as GlutinContextError, CreationError as GlutinCreationError}; use thiserror::Error; +use vfs::VfsError; +use vfs_zip::Error as VfsZipError; #[derive(Debug, 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}")] GlcError(GlcError), @@ -14,7 +27,28 @@ pub enum Error { GlutinCreationError(GlutinCreationError), #[error("Unable to create OpenGL context")] - UnableToCreateContext, + CreateContext, + + #[error("Unable to initialize VFS")] + InitVFS, +} + +impl From for Error { + fn from(err: IoError) -> Self { + Self::IoError(err) + } +} + +impl From for Error { + fn from(err: VfsError) -> Self { + Self::VfsError(err) + } +} + +impl From for Error { + fn from(err: VfsZipError) -> Self { + Self::VfsZipError(err) + } } impl From for Error {