opengl_sdk/src/backend/opengl/gl_renderer.rs

245 lines
6.7 KiB
Rust

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<D: GlDisplay>(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::<u32>()) 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
}
}