diff --git a/Cargo.lock b/Cargo.lock index e398d32..1b7986e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,6 +588,14 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glc" +version = "0.1.0" +dependencies = [ + "gl", + "thiserror", +] + [[package]] name = "glutin" version = "0.25.1" @@ -1361,6 +1369,7 @@ dependencies = [ "env_logger", "futures", "gl", + "glc", "glutin", "log", "num_cpus", diff --git a/Cargo.toml b/Cargo.toml index 5394c40..d3a4621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,9 @@ members = [ "asparit", "async-ecs", "async-ecs-derive", - "space-crush", "gl", + "glc", + "space-crush", ] default-members = [ "space-crush", @@ -14,5 +15,6 @@ default-members = [ asparit = { path = "./asparit" } async-ecs = { path = "./async-ecs" } async-ecs-derive = { path = "./async-ecs-derive" } -space-crush = { path = "./space-crush" } gl = { path = "./gl" } +glc = { path = "./glc" } +space-crush = { path = "./space-crush" } diff --git a/gl/src/gl.rs b/gl/src/gl.rs index 107e0f7..6f33297 100644 --- a/gl/src/gl.rs +++ b/gl/src/gl.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use std::rc::Rc; -use super::bindings::InnerGl; +use super::bindings::{types, Gl as InnerGl}; #[derive(Clone)] pub struct Gl(Rc); diff --git a/gl/src/lib.rs b/gl/src/lib.rs index b4150a0..7039c48 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -2,6 +2,7 @@ mod bindings; #[cfg(feature = "generate_struct")] mod gl; +pub use bindings::types::*; #[cfg(feature = "generate_struct")] pub use bindings::Gl as InnerGl; pub use bindings::*; diff --git a/glc/Cargo.toml b/glc/Cargo.toml new file mode 100644 index 0000000..e6248c8 --- /dev/null +++ b/glc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "glc" +version = "0.1.0" +authors = ["Bergmann89 "] +edition = "2018" + +[dependencies] +gl = { version = "0.1", features = [ "generate_global" ] } +thiserror = "1.0" diff --git a/glc/src/error.rs b/glc/src/error.rs new file mode 100644 index 0000000..7e2b451 --- /dev/null +++ b/glc/src/error.rs @@ -0,0 +1,47 @@ +use std::io::Error as IoError; +use std::str::Utf8Error; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("IO Error: {0}")] + IoError(IoError), + + #[error("UTF-8 Error: {0}")] + Utf8Error(Utf8Error), + + #[error("OpenGL Error: {0}")] + GlError(gl::GLenum), + + #[error("Error while compiling shader object: {0}")] + ShaderCompile(String), + + #[error("Error while linking shader program: {0}")] + ShaderLink(String), +} + +impl Error { + pub fn err_if(err: &T, value: T) -> Result + where + T: PartialEq, + { + if value.eq(err) { + Err(Error::GlError(gl::get_error())) + } else { + Ok(value) + } + } +} + +impl From for Error { + fn from(err: IoError) -> Self { + Self::IoError(err) + } +} + +impl From for Error { + fn from(err: Utf8Error) -> Self { + Self::Utf8Error(err) + } +} diff --git a/glc/src/lib.rs b/glc/src/lib.rs new file mode 100644 index 0000000..70fb73b --- /dev/null +++ b/glc/src/lib.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod shader; diff --git a/glc/src/shader.rs b/glc/src/shader.rs new file mode 100644 index 0000000..b889573 --- /dev/null +++ b/glc/src/shader.rs @@ -0,0 +1,237 @@ +use std::fs::read_to_string; +use std::iter::FromIterator; +use std::path::Path; +use std::ptr::{null, null_mut}; +use std::str::from_utf8; + +use crate::error::Error; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Type { + Vertex, + Compute, + Fragment, + Geometry, + TesslationControl, + TesslationEvaluation, +} + +/* Shader */ + +pub struct Shader { + id: Option, + type_: Type, + source: String, + compiled: bool, +} + +impl Shader { + pub fn from_string(type_: Type, source: String) -> Self { + Self { + id: None, + type_, + source, + compiled: false, + } + } + + pub fn from_file

(type_: Type, path: P) -> Result + where + P: AsRef, + { + let source = read_to_string(path)?; + + Ok(Self::from_string(type_, source)) + } + + pub fn id(&mut self) -> Result { + if self.id.is_none() { + let type_ = match self.type_ { + Type::Vertex => gl::VERTEX_SHADER, + Type::Compute => gl::COMPUTE_SHADER, + Type::Fragment => gl::FRAGMENT_SHADER, + Type::Geometry => gl::GEOMETRY_SHADER, + Type::TesslationControl => gl::TESS_CONTROL_SHADER, + Type::TesslationEvaluation => gl::TESS_EVALUATION_SHADER, + }; + + let id = gl::create_shader(type_); + let id = Error::err_if(&0, id)?; + self.id = Some(id) + } + + Ok(self.id.unwrap()) + } + + pub fn id_opt(&self) -> Option { + self.id + } + + pub fn type_(&self) -> Type { + self.type_ + } + + pub fn compile(&mut self) -> Result<(), Error> { + if self.compiled { + return Ok(()); + } + + let id = self.id()?; + let source = self.source.as_ptr() as *const i8; + let source: *const *const i8 = &source; + + gl::shader_source(id, 1, source, null()); + gl::compile_shader(id); + + let mut success = 1; + gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success); + + if success != 1 { + let mut len = 0; + gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len); + + let mut buffer = Vec::::with_capacity(len as usize + 1); + buffer.resize(len as usize, 0); + + gl::get_shader_info_log( + id, + len, + null_mut(), + buffer.as_mut_ptr() as *mut gl::types::GLchar, + ); + + let msg = from_utf8(&buffer)?; + + return Err(Error::ShaderCompile(msg.into())); + } + + self.compiled = true; + + Ok(()) + } +} + +impl Drop for Shader { + fn drop(&mut self) { + if let Some(id) = &self.id { + gl::delete_shader(*id); + } + } +} + +/* Programm */ + +#[derive(Default)] +pub struct Program { + id: Option, + shaders: Vec, + compiled: bool, +} + +impl Program { + pub fn id(&mut self) -> Result { + if self.id.is_none() { + let id = gl::create_program(); + let id = Error::err_if(&0, id)?; + self.id = Some(id) + } + + Ok(self.id.unwrap()) + } + + pub fn id_opt(&self) -> Option { + self.id + } + + pub fn bind(&self) { + if let Some(id) = &self.id { + gl::use_program(*id); + } else { + gl::use_program(0); + } + } + + pub fn unbind(&self) { + gl::use_program(0); + } + + pub fn link(&mut self, keep_shaders: bool) -> Result<(), Error> { + if self.compiled { + return Ok(()); + } + + let id = self.id()?; + + for shader in &mut self.shaders { + shader.compile()?; + } + + for shader in &mut self.shaders { + gl::attach_shader(id, shader.id()?); + } + + let mut success = 1; + gl::get_program_iv(id, gl::LINK_STATUS, &mut success); + + if success != 1 { + let mut len = 0; + gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len); + + let mut buffer = Vec::::with_capacity(len as usize + 1); + buffer.resize(len as usize, 0); + + gl::get_program_info_log( + id, + len, + null_mut(), + buffer.as_mut_ptr() as *mut gl::types::GLchar, + ); + + let msg = from_utf8(&buffer)?; + + return Err(Error::ShaderLink(msg.into())); + } + + for shader in &mut self.shaders { + gl::detach_shader(id, shader.id()?); + } + + if !keep_shaders { + self.shaders.clear(); + } + + self.compiled = true; + + Ok(()) + } +} + +impl Extend for Program { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + self.shaders.extend(iter); + } +} + +impl FromIterator for Program { + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + Self { + id: None, + shaders: iter.into_iter().collect(), + compiled: false, + } + } +} + +impl Drop for Program { + fn drop(&mut self) { + if let Some(id) = &self.id { + gl::delete_program(*id); + } + } +} diff --git a/space-crush/Cargo.toml b/space-crush/Cargo.toml index b4eadbd..4b10f43 100644 --- a/space-crush/Cargo.toml +++ b/space-crush/Cargo.toml @@ -10,9 +10,10 @@ async-ecs = "0.1" async-ecs-derive = "0.1" env_logger = "0.8" futures = "0.3" -gl = { version = "0.1", features = [ "use_log_crate", "generate_global" ] } +gl = { version = "0.1", features = [ "use_log_crate" ] } +glc = "0.1" glutin = "0.25" -log = { version = "0.4", features = [ "max_level_debug", "release_max_level_warn" ] } +log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } num_cpus = "1.13" rand = "0.7" thiserror = "1.0" diff --git a/space-crush/src/app.rs b/space-crush/src/app.rs new file mode 100644 index 0000000..abb2c96 --- /dev/null +++ b/space-crush/src/app.rs @@ -0,0 +1,20 @@ +use async_ecs::World; + +pub struct App { + world: World, +} + +impl App { + pub fn new() -> Self { + let world = World::default(); + + Self { world } + } + + pub fn progress(&mut self) { + let _world = &mut self.world; + + gl::clear_color(0.1, 0.1, 0.1, 1.0); + gl::clear(gl::COLOR_BUFFER_BIT); + } +} diff --git a/space-crush/src/main.rs b/space-crush/src/main.rs index daf3241..13de20b 100644 --- a/space-crush/src/main.rs +++ b/space-crush/src/main.rs @@ -1,3 +1,5 @@ +mod app; + use std::io::Error as IoError; use glutin::{ @@ -12,9 +14,11 @@ use log::info; use thiserror::Error; use tokio::{runtime::Builder, task::LocalSet}; +use app::App; + fn main() -> Result<(), Error> { env_logger::builder() - .filter_level(log::LevelFilter::Debug) + .filter_level(log::LevelFilter::Trace) .format_timestamp_nanos() .init(); @@ -49,9 +53,10 @@ async fn run() -> Result<(), Error> { )); let context = unsafe { context.make_current().unwrap() }; - gl::load_with(|s| context.get_proc_address(s)); + let mut run = true; + let mut app = App::new(); loop { event_loop.run_return(|event, _target, flow_control| { @@ -76,8 +81,7 @@ async fn run() -> Result<(), Error> { break; } - gl::clear_color(0.1, 0.1, 0.1, 1.0); - gl::clear(gl::COLOR_BUFFER_BIT); + app.progress(); context.swap_buffers().unwrap(); }