diff --git a/glc/src/array_buffer.rs b/glc/src/array_buffer.rs index 095eb0f..3c38233 100644 --- a/glc/src/array_buffer.rs +++ b/glc/src/array_buffer.rs @@ -107,6 +107,14 @@ impl ArrayBuffer { }) } + pub fn bind_buffer_base(&self, index: gl::GLuint) { + gl::bind_buffer_base(self.target.as_enum(), index, self.id); + } + + pub fn bind_buffer_range(&self, index: gl::GLuint, offset: gl::GLintptr, size: gl::GLsizeiptr) { + gl::bind_buffer_range(self.target.as_enum(), index, self.id, offset, size); + } + fn inner_map(&self, access: gl::GLenum) -> Result<&mut [T], Error> { let ptr = Error::err_if(&null(), gl::map_named_buffer(self.id, access))?; let count = self.size / size_of::(); diff --git a/glc/src/error.rs b/glc/src/error.rs index 952a6ca..5724212 100644 --- a/glc/src/error.rs +++ b/glc/src/error.rs @@ -14,8 +14,8 @@ pub enum Error { #[error("OpenGL Error: {0}")] GlError(gl::GLenum), - #[error("Error while compiling shader object: {0}")] - ShaderCompile(String), + #[error("Error while compiling shader object\n{code:}\n{error:}")] + ShaderCompile { code: String, error: String }, #[error("Error while linking shader program: {0}")] ShaderLink(String), diff --git a/glc/src/matrix.rs b/glc/src/matrix.rs index fa3f14d..000d9c0 100644 --- a/glc/src/matrix.rs +++ b/glc/src/matrix.rs @@ -6,6 +6,12 @@ use std::ops::{Deref, DerefMut, Mul}; use super::vector::{Element, Vector2, Vector3, Vector4}; +macro_rules! first_ptr { + ($this:ident, $first:ident $(,$other:ident)*) => { + unsafe { $this.$first.as_ptr() } + }; +} + macro_rules! define_mat { ($Name:ident, $Vector:ident, $size:tt, $($T:ident => $i:tt => $f:ident),*) => { #[repr(C, packed)] @@ -18,6 +24,10 @@ macro_rules! define_mat { pub fn new($($f: $Vector,)+) -> Self { Self { $($f,)+ } } + + pub fn as_ptr(&self) -> * const T { + first_ptr!(self $(,$f)+) + } } impl Default for $Name @@ -139,17 +149,17 @@ define_mat!(Matrix2, Vector2, 2, T => 0 => axis_x, T => 1 => axis_y); define_mat!(Matrix3, Vector3, 3, T => 0 => axis_x, T => 1 => axis_y, T => 2 => axis_z); define_mat!(Matrix4, Vector4, 4, T => 0 => axis_x, T => 1 => axis_y, T => 2 => axis_z, T => 3 => position); -type Matrix2f = Matrix2; -type Matrix3f = Matrix3; -type Matrix4f = Matrix4; +pub type Matrix2f = Matrix2; +pub type Matrix3f = Matrix3; +pub type Matrix4f = Matrix4; -type Matrix2d = Matrix2; -type Matrix3d = Matrix3; -type Matrix4d = Matrix4; +pub type Matrix2d = Matrix2; +pub type Matrix3d = Matrix3; +pub type Matrix4d = Matrix4; -type Matrix2i = Matrix2; -type Matrix3i = Matrix3; -type Matrix4i = Matrix4; +pub type Matrix2i = Matrix2; +pub type Matrix3i = Matrix3; +pub type Matrix4i = Matrix4; /* Matrix2 */ diff --git a/glc/src/shader.rs b/glc/src/shader.rs index 96247e4..37f1653 100644 --- a/glc/src/shader.rs +++ b/glc/src/shader.rs @@ -5,6 +5,7 @@ use std::str::from_utf8; use crate::{ error::Error, + matrix::Matrix4f, misc::{AsEnum, Bindable}, }; @@ -69,6 +70,12 @@ impl Program { pub fn id(&self) -> gl::GLuint { self.id } + + pub fn uniform(&self, location: gl::GLint, uniform: Uniform<'_>) { + match uniform { + Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()), + } + } } impl Drop for Program { @@ -99,10 +106,10 @@ impl Shader { let id = Error::err_if(&0, id)?; source.push('\0'); - let source = source.as_ptr() as *const i8; - let source: *const *const i8 = &source; + let source_ptr = source.as_ptr() as *const i8; + let source_ptr: *const *const i8 = &source_ptr; - gl::shader_source(id, 1, source, null()); + gl::shader_source(id, 1, source_ptr, null()); gl::compile_shader(id); let mut success = 1; @@ -124,7 +131,10 @@ impl Shader { let msg = from_utf8(&buffer)?; - return Err(Error::ShaderCompile(msg.into())); + return Err(Error::ShaderCompile { + code: source, + error: msg.into(), + }); } Ok(Self { id }) @@ -134,9 +144,16 @@ impl Shader { where P: AsRef, { - let source = read_to_string(path)?; - - Self::from_string(type_, source) + let source = read_to_string(&path)?; + + match Self::from_string(type_, source) { + Ok(v) => Ok(v), + Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile { + code: format!("{}\n{}", path.as_ref().display(), code), + error, + }), + Err(err) => Err(err), + } } pub fn id(&self) -> gl::GLuint { @@ -174,3 +191,9 @@ impl AsEnum for Type { } } } + +/* Uniform */ + +pub enum Uniform<'a> { + Matrix4f(&'a Matrix4f), +} diff --git a/glc/src/vector.rs b/glc/src/vector.rs index 36830f9..eae4f40 100644 --- a/glc/src/vector.rs +++ b/glc/src/vector.rs @@ -4,6 +4,12 @@ use std::convert::{AsMut, AsRef}; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::ops::{Add, Deref, DerefMut, Div, Mul, Neg, Sub}; +macro_rules! first_ptr { + ($this:ident, $first:ident $(,$other:ident)*) => { + unsafe { &$this.$first } + }; +} + macro_rules! define_vec { ($Name:ident, $size:tt, $($T:ident => $i:tt => $f:ident),*) => { #[repr(C, packed)] @@ -16,6 +22,11 @@ macro_rules! define_vec { pub fn new($($f: T,)+) -> Self { Self { $($f,)+ } } + + #[inline] + pub fn as_ptr(&self) -> * const T { + first_ptr!(self $(, $f)+) + } } impl Default for $Name @@ -139,17 +150,17 @@ define_vec!(Vector2, 2, T => 0 => x, T => 1 => y); define_vec!(Vector3, 3, T => 0 => x, T => 1 => y, T => 2 => z); define_vec!(Vector4, 4, T => 0 => x, T => 1 => y, T => 2 => z, T => 3 => w); -type Vector2f = Vector2; -type Vector3f = Vector3; -type Vector4f = Vector4; +pub type Vector2f = Vector2; +pub type Vector3f = Vector3; +pub type Vector4f = Vector4; -type Vector2d = Vector2; -type Vector3d = Vector3; -type Vector4d = Vector4; +pub type Vector2d = Vector2; +pub type Vector3d = Vector3; +pub type Vector4d = Vector4; -type Vector2i = Vector2; -type Vector3i = Vector3; -type Vector4i = Vector4; +pub type Vector2i = Vector2; +pub type Vector3i = Vector3; +pub type Vector4i = Vector4; /* Vector2 */ diff --git a/space-crush/src/app/misc/camera.rs b/space-crush/src/app/misc/camera.rs new file mode 100644 index 0000000..8a04745 --- /dev/null +++ b/space-crush/src/app/misc/camera.rs @@ -0,0 +1,125 @@ +#![allow(dead_code)] + +use glc::{ + array_buffer::{ArrayBuffer, Target, Usage}, + error::Error, + matrix::{Angle, Matrix4f}, + vector::Vector4f, +}; + +pub struct Camera { + buffer: ArrayBuffer, +} + +impl Camera { + pub fn new() -> Result { + let mut buffer = ArrayBuffer::new(Target::UniformBuffer)?; + buffer.buffer_data( + Usage::StaticDraw, + &[Data { + projection: Matrix4f::identity(), + view: Matrix4f::identity(), + }], + )?; + + Ok(Self { buffer }) + } + + pub fn ortho( + &mut self, + left: f32, + right: f32, + bottom: f32, + top: f32, + near: f32, + far: f32, + ) -> Result<(), Error> { + let mut data = self.buffer.map_mut::(true)?; + data[0].projection = Matrix4f::new( + Vector4f::new(2.0 / (right - left), 0.0, 0.0, 0.0), + Vector4f::new(0.0, 2.0 / (top - bottom), 0.0, 0.0), + Vector4f::new(0.0, 0.0, -2.0 / (far - near), 0.0), + Vector4f::new( + -(right + left) / (right - left), + -(top + bottom) / (top - bottom), + -(far + near) / (far - near), + 1.0, + ), + ); + + Ok(()) + } + + pub fn perspective( + &mut self, + fov: Angle, + ratio: f32, + near: f32, + far: f32, + ) -> Result<(), Error> { + let top = near * fov.into_rad().into_inner().tan(); + let bottom = -top; + let right = ratio * top; + let left = -right; + + let mut data = self.buffer.map_mut::(true)?; + data[0].projection = Matrix4f::new( + Vector4f::new(2.0 * near / (right - left), 0.0, 0.0, 0.0), + Vector4f::new(0.0, 2.0 * near / (top - bottom), 0.0, 0.0), + Vector4f::new( + (right + left) / (right - left), + (top + bottom) / (top - bottom), + -(far + near) / (far - near), + -1.0, + ), + Vector4f::new(0.0, 0.0, -2.0 * far * near / (far - near), 0.0), + ); + + Ok(()) + } + + pub fn set_projection(&mut self, m: Matrix4f) -> Result<(), Error> { + let mut data = self.buffer.map_mut::(false)?; + data[0].projection = m; + + Ok(()) + } + + pub fn set_view(&mut self, m: Matrix4f) -> Result<(), Error> { + let mut data = self.buffer.map_mut::(false)?; + data[0].view = m; + + Ok(()) + } + + pub fn update_view(&mut self, m: Matrix4f) -> Result<(), Error> { + let mut data = self.buffer.map_mut::(false)?; + data[0].view = data[0].view * m; + + Ok(()) + } + + pub fn projection(&self) -> Result { + let data = self.buffer.map::()?; + let ret = data[0].view; + + Ok(ret) + } + + pub fn view(&self) -> Result { + let data = self.buffer.map::()?; + let ret = data[0].view; + + Ok(ret) + } + + pub fn bind(&self, index: gl::GLuint) -> Result<(), Error> { + Error::checked(|| self.buffer.bind_buffer_base(index)) + } +} + +#[repr(C, packed)] +struct Data { + projection: Matrix4f, + view: Matrix4f, +} diff --git a/space-crush/src/app/misc/mod.rs b/space-crush/src/app/misc/mod.rs index f4aeec5..4ef7910 100644 --- a/space-crush/src/app/misc/mod.rs +++ b/space-crush/src/app/misc/mod.rs @@ -1,3 +1,4 @@ +pub mod camera; pub mod events; pub mod geometry; pub mod window; diff --git a/space-crush/src/app/misc/window.rs b/space-crush/src/app/misc/window.rs index 19bc9c0..ccae6e5 100644 --- a/space-crush/src/app/misc/window.rs +++ b/space-crush/src/app/misc/window.rs @@ -5,6 +5,7 @@ use glutin::{ window::WindowBuilder, Api, ContextBuilder, ContextWrapper, GlProfile, GlRequest, PossiblyCurrent, }; +use log::info; use crate::Error; @@ -14,18 +15,33 @@ pub struct Window { impl Window { pub fn new(event_loop: &EventLoop) -> Result { - let window_builder = WindowBuilder::new() - .with_title("space-crush") - .with_visible(true) - .with_inner_size(PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)); - let context = ContextBuilder::new() - .with_double_buffer(Some(true)) - .with_hardware_acceleration(Some(true)) - .with_pixel_format(24, 8) - .with_vsync(true) - .with_gl(GlRequest::Specific(Api::OpenGl, (4, 5))) - .with_gl_profile(GlProfile::Core) - .build_windowed(window_builder, event_loop)?; + let mut multisampling = [16u16, 8, 4, 2, 1, 0].iter(); + let context = loop { + let multisampling = match multisampling.next() { + Some(multisampling) => multisampling, + None => return Err(Error::UnableToCreateContext), + }; + + info!("Create OpenGL context (multisampling={})", multisampling); + + let window_builder = WindowBuilder::new() + .with_title("space-crush") + .with_visible(true) + .with_inner_size(PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)); + let ret = ContextBuilder::new() + .with_double_buffer(Some(true)) + .with_hardware_acceleration(Some(true)) + .with_pixel_format(24, 8) + .with_multisampling(*multisampling) + .with_vsync(true) + .with_gl(GlRequest::Specific(Api::OpenGl, (4, 5))) + .with_gl_profile(GlProfile::Core) + .build_windowed(window_builder, event_loop); + + if let Ok(context) = ret { + break context; + } + }; let window = context.window(); let window_size = window.outer_size(); diff --git a/space-crush/src/app/mod.rs b/space-crush/src/app/mod.rs index da6ad0d..38afb8b 100644 --- a/space-crush/src/app/mod.rs +++ b/space-crush/src/app/mod.rs @@ -7,7 +7,7 @@ use specs::{Dispatcher, DispatcherBuilder, World}; use crate::Error; use misc::{events::Events, geometry::Geometry, window::Window}; -use render::Background; +use render::{Init, Test}; use systems::{State, StateUpdate}; pub struct App<'a, 'b> { @@ -24,7 +24,8 @@ impl<'a, 'b> App<'a, 'b> { let mut dispatcher = DispatcherBuilder::new() .with(StateUpdate::new(world), "state_update", &[]) - .with_thread_local(Background::new(world)?) + .with_thread_local(Init::new(world)?) + .with_thread_local(Test::new(world)?) .build(); dispatcher.setup(world); diff --git a/space-crush/src/app/render/background.rs b/space-crush/src/app/render/init.rs similarity index 56% rename from space-crush/src/app/render/background.rs rename to space-crush/src/app/render/init.rs index e715ae6..c3a2f1c 100644 --- a/space-crush/src/app/render/background.rs +++ b/space-crush/src/app/render/init.rs @@ -2,19 +2,36 @@ use glc::{ misc::Bindable, shader::{Program, Shader, Type}, }; +use log::error; use shrev::{EventChannel, ReaderId}; -use specs::{ReadExpect, System, World}; +use specs::{ReadExpect, System, World, WriteExpect}; use crate::Error; -use super::super::misc::{events::WindowEvent, geometry::Geometry}; +use super::super::misc::{camera::Camera, events::WindowEvent, geometry::Geometry}; -pub struct Background { +/* Global */ + +pub struct Global { + pub camera: Camera, +} + +impl Global { + pub fn new() -> Result { + Ok(Self { + camera: Camera::new()?, + }) + } +} + +/* Init */ + +pub struct Init { program: Program, window_events_id: ReaderId, } -impl Background { +impl Init { pub fn new(world: &mut World) -> Result { let shaders = vec![ (Type::Vertex, include_str!("shader/noise.vert")), @@ -29,6 +46,8 @@ impl Background { .fetch_mut::>() .register_reader(); + world.insert(Global::new()?); + Ok(Self { program, window_events_id, @@ -36,17 +55,30 @@ impl Background { } } -impl<'a> System<'a> for Background { +impl<'a> System<'a> for Init { type SystemData = ( + WriteExpect<'a, Global>, ReadExpect<'a, Geometry>, ReadExpect<'a, EventChannel>, ); - fn run(&mut self, (geometry, window_events): Self::SystemData) { + 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); + + let w = *w as f32; + let h = *h as f32; + + if let Err(err) = + global + .camera + .ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0) + { + error!("Error while updating camera: {}", err); + panic!("Error while updating camera: {}", err); + } } } diff --git a/space-crush/src/app/render/mod.rs b/space-crush/src/app/render/mod.rs index 4053fcf..bdbacf3 100644 --- a/space-crush/src/app/render/mod.rs +++ b/space-crush/src/app/render/mod.rs @@ -1,3 +1,5 @@ -mod background; +mod init; +mod test; -pub use background::Background; +pub use init::{Global, Init}; +pub use test::Test; diff --git a/space-crush/src/app/render/shader/quad.frag b/space-crush/src/app/render/shader/quad.frag new file mode 100644 index 0000000..89f42f6 --- /dev/null +++ b/space-crush/src/app/render/shader/quad.frag @@ -0,0 +1,8 @@ +#version 450 core + +out vec4 Color; + +void main() +{ + Color = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/space-crush/src/app/render/shader/quad.vert b/space-crush/src/app/render/shader/quad.vert new file mode 100644 index 0000000..bc82524 --- /dev/null +++ b/space-crush/src/app/render/shader/quad.vert @@ -0,0 +1,16 @@ +#version 450 core + +layout (location = 0) in vec3 Position; + +layout (std140, binding = 0) uniform Camera +{ + mat4 projection; + mat4 view; +}; + +layout (location = 1) uniform mat4 model; + +void main() +{ + gl_Position = projection * view * model * vec4(Position, 1.0); +} diff --git a/space-crush/src/app/render/test.rs b/space-crush/src/app/render/test.rs new file mode 100644 index 0000000..aad947a --- /dev/null +++ b/space-crush/src/app/render/test.rs @@ -0,0 +1,55 @@ +use glc::{ + matrix::{Angle, Matrix4f}, + misc::Bindable, + shader::{Program, Shader, Type, Uniform}, + vector::Vector3f, +}; +use specs::{ReadExpect, System, World}; + +use crate::Error; + +use super::{super::misc::geometry::Geometry, init::Global}; + +pub struct Test { + program: Program, + model: Matrix4f, +} + +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 global = world.fetch::(); + program.bind(); + global.camera.bind(0)?; + program.unbind(); + + Ok(Self { + program, + model: Matrix4f::scale(Vector3f::new(100.0, 100.0, 100.0)), + }) + } +} + +impl<'a> System<'a> for Test { + type SystemData = ReadExpect<'a, Geometry>; + + fn run(&mut self, geometry: Self::SystemData) { + self.model = self.model * Matrix4f::rotate(Vector3f::new(0.0, 0.0, 1.0), Angle::Deg(0.01)); + + self.program.bind(); + self.program.uniform(1, Uniform::Matrix4f(&self.model)); + + geometry.render_quad(); + + self.program.unbind(); + } +} diff --git a/space-crush/src/error.rs b/space-crush/src/error.rs index 830df9c..94cf572 100644 --- a/space-crush/src/error.rs +++ b/space-crush/src/error.rs @@ -12,6 +12,9 @@ pub enum Error { #[error("glutin Creation Error: {0}")] GlutinCreationError(GlutinCreationError), + + #[error("Unable to create OpenGL context")] + UnableToCreateContext, } impl From for Error {