use gl::types::GLfloat; use glutin::prelude::GlDisplay; use crate::backend::{opengl, Uniform}; use crate::math::{ Mat4, Vec3 }; use crate::{geometry, backend::gpu::GpuMesh}; use crate::shaders::Shader; use std::ops::Deref; use std::ffi::{ CStr, CString, c_void }; use super::gl::Gl; #[derive(Debug)] pub struct RenderState { pub projection: Mat4, pub view: Mat4, pub model: Mat4, pub camera_pos: Vec3, pub light_pos: Vec3, pub light_color: Vec3, } impl RenderState { pub fn new(aspect_ratio: f32) -> Self { let camera_pos = Vec3::new(0.0, 2.0, 5.0); Self { projection: Mat4::perspective(45.0_f32.to_radians(), aspect_ratio, 0.1, 100.0), view: Mat4::look_at( camera_pos, Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 1.0, 0.0), ), model: Mat4::identity(), camera_pos, light_pos: Vec3::new(3.0, 5.0, 3.0), light_color: Vec3::new(1.0, 1.0, 1.0), } } } fn get_gl_string(gl: &opengl::Gl, variant: gl::types::GLenum) -> Option<&'static CStr>{ unsafe { let s = gl.GetString(variant); (!s.is_null()).then(|| CStr::from_ptr(s.cast())) } } pub struct GlRenderer { gl: Gl, } impl GlRenderer { pub fn new(gl_display: &D) -> Self { let gl = opengl::Gl::load_with(|symbol| { let symbol = CString::new(symbol).unwrap(); gl_display.get_proc_address(symbol.as_c_str()).cast() }); if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) { println!("Running on {}", renderer.to_string_lossy()); } if let Some(version) = get_gl_string(&gl, gl::VERSION) { println!("OpenGL version {}", version.to_string_lossy()); } if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) { println!("Shaders version on {}", shaders_version.to_string_lossy()); } unsafe { gl.Enable(gl::DEPTH_TEST); gl.DepthFunc(gl::LESS); gl.Enable(gl::MULTISAMPLE); } Self { gl } } pub fn upload_mesh(&self, mesh: &geometry::Mesh) -> GpuMesh { unsafe { let gl = &self.gl; let mut vao = std::mem::zeroed(); gl.GenVertexArrays(1, &mut vao); gl.BindVertexArray(vao); let mut vbo = std::mem::zeroed(); gl.GenBuffers(1, &mut vbo); gl.BindBuffer(gl::ARRAY_BUFFER, vbo); for attribute in &mesh.format.attributes { let location = attribute.semantic.location(); gl.VertexAttribPointer( attribute.semantic.location(), attribute.comp_count as i32, attribute.data_type.to_gpu(), attribute.normalized as u8, mesh.format.stride as i32, attribute.offset as *const c_void, ); gl.EnableVertexAttribArray(location); } gl.BufferData( gl::ARRAY_BUFFER, mesh.vertex_data.len() as gl::types::GLsizeiptr, mesh.vertex_data.as_ptr() as *const _, gl::STATIC_DRAW, ); let (ebo, index_count) = if !mesh.indices.is_empty() { let mut ebo = std::mem::zeroed(); gl.GenBuffers(1, &mut ebo); gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); gl.BufferData( gl::ELEMENT_ARRAY_BUFFER, (mesh.indices.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, mesh.indices.as_ptr() as *const _, gl::STATIC_DRAW, ); (Some(ebo), Some(mesh.indices.len() as i32)) } else { (None, None) }; gl.BindVertexArray(0); GpuMesh { vao, vbo, ebo, vertex_count: mesh.vertex_count as i32, index_count, } } } fn compile_shader(gl: &opengl::Gl, shader: gl::types::GLenum, source: &[u8]) -> gl::types::GLuint { unsafe { let shader = gl.CreateShader(shader); gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null()); gl.CompileShader(shader); shader } } pub fn create_shader(&self, shader_src: &opengl::GlShaderSource) -> Shader { unsafe { let vertex_shader = Self::compile_shader( &self.gl, gl::VERTEX_SHADER, shader_src.vertex_src.as_bytes() ); let fragment_shader = Self::compile_shader( &self.gl, gl::FRAGMENT_SHADER, shader_src.fragment_src.as_bytes() ); let program = self.gl.CreateProgram(); self.gl.AttachShader(program, vertex_shader); self.gl.AttachShader(program, fragment_shader); self.gl.LinkProgram(program); self.gl.DeleteShader(vertex_shader); self.gl.DeleteShader(fragment_shader); Shader(program) } } pub fn apply_uniform(&self, shader: Shader, uniform: &Uniform) { unsafe { match uniform { Uniform::Float(name, value) => { let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform1f(location, *value); } Uniform::Int(name, value) => { let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform1i(location, *value); } Uniform::Vec2(name, value) => { let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform2f(location, value[0], value[1]); } Uniform::Vec3(name, value) => { let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform3f(location, value[0], value[1], value[2]); } Uniform::Vec4(name, value) => { let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform4f(location, value[0], value[1], value[2], value[3]); } Uniform::Mat4(name, value) => { let location = self.GetUniformLocation(*shader, name.as_ptr()); self.UniformMatrix4fv(location, 1, gl::FALSE, value.as_ptr()); } } } } pub fn clear(&self, red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) { unsafe { self.ClearColor(red, green, blue, alpha); self.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); } } pub fn use_shader(&self, shader: Shader) { unsafe { self.UseProgram(*shader) }; } pub fn resize(&self, width: i32, height: i32) { unsafe { self.gl.Viewport(0, 0, width, height); } } pub fn draw_mesh(&self, mesh: &GpuMesh) { unsafe { self.BindVertexArray(mesh.vao); if let (Some(ebo), Some(count)) = (mesh.ebo, mesh.index_count) { self.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); self.DrawElements(gl::TRIANGLES, count, gl::UNSIGNED_INT, std::ptr::null()); } else { self.DrawArrays(gl::TRIANGLES, 0, mesh.vertex_count); } } } } impl Deref for GlRenderer { type Target = opengl::Gl; fn deref(&self) -> &Self::Target { &self.gl } }