opengl_sdk/src/backend/opengl/gl_renderer.rs

241 lines
7.0 KiB
Rust

use glutin::prelude::GlDisplay;
use crate::backend::opengl::{self, 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 };
/// Render state containing camera and lighting information
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([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 ([0.0, 0.0, 0.0]),
Vec3 ([0.0, 1.0, 0.0]),
),
model: Mat4::identity(),
camera_pos,
light_pos: Vec3([3.0, 5.0, 3.0]),
light_color: Vec3([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: std::rc::Rc<opengl::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());
}
// Enable depth testing for 3D rendering
unsafe {
gl.Enable(gl::DEPTH_TEST);
gl.DepthFunc(gl::LESS);
}
Self {
gl: std::rc::Rc::new(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);
// Upload vertex data (position, normal, texcoord)
let mut vbo = std::mem::zeroed();
gl.GenBuffers(1, &mut vbo);
gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
// Vertex layout: [pos.x, pos.y, pos.z, norm.x, norm.y, norm.z, tex.x, tex.y]
let vertex_data: Vec<f32> = mesh.vertices
.iter()
.flat_map(|v| [
v.position.x(), v.position.y(), v.position.z(),
v.normal.x(), v.normal.y(), v.normal.z(),
v.tex_coord.x(), v.tex_coord.y(),
])
.collect();
gl.BufferData(
gl::ARRAY_BUFFER,
(vertex_data.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr,
vertex_data.as_ptr() as *const _,
gl::STATIC_DRAW,
);
let stride = 8 * std::mem::size_of::<f32>() as gl::types::GLsizei;
// Position attribute (location = 0)
gl.VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, stride, std::ptr::null());
gl.EnableVertexAttribArray(0);
// Normal attribute (location = 1)
gl.VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, stride, (3 * std::mem::size_of::<f32>()) as *const _);
gl.EnableVertexAttribArray(1);
// Texcoord attribute (location = 2)
gl.VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, stride, (6 * std::mem::size_of::<f32>()) as *const _);
gl.EnableVertexAttribArray(2);
// Upload index data if available
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.vertices.len() 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.0, name.as_ptr());
self.Uniform1f(location, value);
}
Uniform::Int(name, value) => {
let location = self.GetUniformLocation(shader.0, name.as_ptr());
self.Uniform1i(location, value);
}
Uniform::Vec3(name, value) => {
let location = self.GetUniformLocation(shader.0, name.as_ptr());
self.Uniform3f(location, value[0], value[1], value[2]);
}
Uniform::Vec4(name, value) => {
let location = self.GetUniformLocation(shader.0, name.as_ptr());
self.Uniform4f(location, value[0], value[1], value[2], value[3]);
}
Uniform::Mat4(name, value) => {
let location = self.GetUniformLocation(shader.0, name.as_ptr());
self.UniformMatrix4fv(location, 1, gl::FALSE, value.as_ptr());
}
Uniform::None => {}
}
}
}
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
}
}