| @@ -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<T>(&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::<T>(); | |||
| @@ -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), | |||
| @@ -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<T>,)+) -> Self { | |||
| Self { $($f,)+ } | |||
| } | |||
| pub fn as_ptr(&self) -> * const T { | |||
| first_ptr!(self $(,$f)+) | |||
| } | |||
| } | |||
| impl<T> Default for $Name<T> | |||
| @@ -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<gl::GLfloat>; | |||
| type Matrix3f = Matrix3<gl::GLfloat>; | |||
| type Matrix4f = Matrix4<gl::GLfloat>; | |||
| pub type Matrix2f = Matrix2<gl::GLfloat>; | |||
| pub type Matrix3f = Matrix3<gl::GLfloat>; | |||
| pub type Matrix4f = Matrix4<gl::GLfloat>; | |||
| type Matrix2d = Matrix2<gl::GLdouble>; | |||
| type Matrix3d = Matrix3<gl::GLdouble>; | |||
| type Matrix4d = Matrix4<gl::GLdouble>; | |||
| pub type Matrix2d = Matrix2<gl::GLdouble>; | |||
| pub type Matrix3d = Matrix3<gl::GLdouble>; | |||
| pub type Matrix4d = Matrix4<gl::GLdouble>; | |||
| type Matrix2i = Matrix2<gl::GLint>; | |||
| type Matrix3i = Matrix3<gl::GLint>; | |||
| type Matrix4i = Matrix4<gl::GLint>; | |||
| pub type Matrix2i = Matrix2<gl::GLint>; | |||
| pub type Matrix3i = Matrix3<gl::GLint>; | |||
| pub type Matrix4i = Matrix4<gl::GLint>; | |||
| /* Matrix2 */ | |||
| @@ -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<Path>, | |||
| { | |||
| 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), | |||
| } | |||
| @@ -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<T> Default for $Name<T> | |||
| @@ -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<gl::GLfloat>; | |||
| type Vector3f = Vector3<gl::GLfloat>; | |||
| type Vector4f = Vector4<gl::GLfloat>; | |||
| pub type Vector2f = Vector2<gl::GLfloat>; | |||
| pub type Vector3f = Vector3<gl::GLfloat>; | |||
| pub type Vector4f = Vector4<gl::GLfloat>; | |||
| type Vector2d = Vector2<gl::GLdouble>; | |||
| type Vector3d = Vector3<gl::GLdouble>; | |||
| type Vector4d = Vector4<gl::GLdouble>; | |||
| pub type Vector2d = Vector2<gl::GLdouble>; | |||
| pub type Vector3d = Vector3<gl::GLdouble>; | |||
| pub type Vector4d = Vector4<gl::GLdouble>; | |||
| type Vector2i = Vector2<gl::GLint>; | |||
| type Vector3i = Vector3<gl::GLint>; | |||
| type Vector4i = Vector4<gl::GLint>; | |||
| pub type Vector2i = Vector2<gl::GLint>; | |||
| pub type Vector3i = Vector3<gl::GLint>; | |||
| pub type Vector4i = Vector4<gl::GLint>; | |||
| /* Vector2 */ | |||
| @@ -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<Self, Error> { | |||
| 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::<Data>(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<f32>, | |||
| 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::<Data>(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::<Data>(false)?; | |||
| data[0].projection = m; | |||
| Ok(()) | |||
| } | |||
| pub fn set_view(&mut self, m: Matrix4f) -> Result<(), Error> { | |||
| let mut data = self.buffer.map_mut::<Data>(false)?; | |||
| data[0].view = m; | |||
| Ok(()) | |||
| } | |||
| pub fn update_view(&mut self, m: Matrix4f) -> Result<(), Error> { | |||
| let mut data = self.buffer.map_mut::<Data>(false)?; | |||
| data[0].view = data[0].view * m; | |||
| Ok(()) | |||
| } | |||
| pub fn projection(&self) -> Result<Matrix4f, Error> { | |||
| let data = self.buffer.map::<Data>()?; | |||
| let ret = data[0].view; | |||
| Ok(ret) | |||
| } | |||
| pub fn view(&self) -> Result<Matrix4f, Error> { | |||
| let data = self.buffer.map::<Data>()?; | |||
| 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, | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| pub mod camera; | |||
| pub mod events; | |||
| pub mod geometry; | |||
| pub mod window; | |||
| @@ -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<T>(event_loop: &EventLoop<T>) -> Result<Self, Error> { | |||
| 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(); | |||
| @@ -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); | |||
| @@ -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<Self, Error> { | |||
| Ok(Self { | |||
| camera: Camera::new()?, | |||
| }) | |||
| } | |||
| } | |||
| /* Init */ | |||
| pub struct Init { | |||
| program: Program, | |||
| window_events_id: ReaderId<WindowEvent>, | |||
| } | |||
| impl Background { | |||
| impl Init { | |||
| pub fn new(world: &mut World) -> Result<Self, Error> { | |||
| let shaders = vec![ | |||
| (Type::Vertex, include_str!("shader/noise.vert")), | |||
| @@ -29,6 +46,8 @@ impl Background { | |||
| .fetch_mut::<EventChannel<WindowEvent>>() | |||
| .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<WindowEvent>>, | |||
| ); | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,3 +1,5 @@ | |||
| mod background; | |||
| mod init; | |||
| mod test; | |||
| pub use background::Background; | |||
| pub use init::{Global, Init}; | |||
| pub use test::Test; | |||
| @@ -0,0 +1,8 @@ | |||
| #version 450 core | |||
| out vec4 Color; | |||
| void main() | |||
| { | |||
| Color = vec4(1.0, 1.0, 1.0, 1.0); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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<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 global = world.fetch::<Global>(); | |||
| 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(); | |||
| } | |||
| } | |||
| @@ -12,6 +12,9 @@ pub enum Error { | |||
| #[error("glutin Creation Error: {0}")] | |||
| GlutinCreationError(GlutinCreationError), | |||
| #[error("Unable to create OpenGL context")] | |||
| UnableToCreateContext, | |||
| } | |||
| impl From<GlcError> for Error { | |||