245 lines
6.7 KiB
Rust
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
|
|
}
|
|
}
|