use super::shader::Shader;
use crate::text::TextRenderer;
use nalgebra_glm as glm;
pub struct Renderer {
shader: Shader,
texture_shader: Shader,
text_renderer: TextRenderer,
screen_width: f32,
screen_height: f32,
background_color: [f32; 4],
vao: u32,
vbo: u32,
circle_vertices: Vec<f32>, }
const CIRCLE_SEGMENTS: usize = 32;
impl Renderer {
pub fn new(font_data: Vec<u8>) -> Self {
let vertex_source = include_str!("shaders/basic.vert");
let fragment_source = include_str!("shaders/basic.frag");
let shader = Shader::new(vertex_source, fragment_source);
let texture_shader = Shader::new(
include_str!("shaders/texture.vert"),
include_str!("shaders/texture.frag")
);
let text_renderer = TextRenderer::new(font_data);
let mut vao = 0;
let mut vbo = 0;
unsafe {
gl::GenVertexArrays(1, &mut vao);
gl::GenBuffers(1, &mut vbo);
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, 0, std::ptr::null());
gl::EnableVertexAttribArray(0);
}
let mut circle_vertices = Vec::with_capacity(CIRCLE_SEGMENTS * 3 * 3);
for i in 0..CIRCLE_SEGMENTS {
let angle = (2.0 * std::f32::consts::PI * (i as f32)) / (CIRCLE_SEGMENTS as f32);
let x = angle.cos();
let y = angle.sin();
circle_vertices.extend_from_slice(&[x, y, 0.0]);
}
Self {
shader,
texture_shader,
text_renderer,
screen_width: 0.0,
screen_height: 0.0,
background_color: [0.1, 0.35, 0.33, 1.0],
vao,
vbo,
circle_vertices,
}
}
pub fn set_background_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
self.background_color = [r, g, b, a];
}
pub fn render(&mut self, screen_width: f32, screen_height: f32) {
self.screen_width = screen_width;
self.screen_height = screen_height;
unsafe {
gl::ClearColor(
self.background_color[0],
self.background_color[1],
self.background_color[2],
self.background_color[3]
);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
}
self.shader.use_program();
let projection = glm::ortho(0.0, screen_width, screen_height, 0.0, -1.0, 1.0);
self.shader.set_mat4("projection", &projection);
}
pub fn text_renderer(&self) -> &TextRenderer {
&self.text_renderer
}
pub fn draw_rect(&mut self, x: f32, y: f32, width: f32, height: f32, color: [f32; 4]) {
let current_shader = unsafe {
let mut current_program = 0;
gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
current_program
};
self.shader.use_program();
self.shader.set_vec4("uColor", &color);
let x_ndc = (x / self.screen_width) * 2.0 - 1.0;
let y_ndc = 1.0 - (y / self.screen_height) * 2.0;
let x2_ndc = ((x + width) / self.screen_width) * 2.0 - 1.0;
let y2_ndc = 1.0 - ((y + height) / self.screen_height) * 2.0;
let vertices: [f32; 12] = [
x_ndc,
y_ndc,
0.0, x2_ndc,
y_ndc,
0.0, x2_ndc,
y2_ndc,
0.0, x_ndc,
y2_ndc,
0.0, ];
let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
unsafe {
let mut vao = 0;
let mut vbo = 0;
let mut ebo = 0;
gl::GenVertexArrays(1, &mut vao);
gl::GenBuffers(1, &mut vbo);
gl::GenBuffers(1, &mut ebo);
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(vertices.len() * std::mem::size_of::<f32>()) as isize,
vertices.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(indices.len() * std::mem::size_of::<u32>()) as isize,
indices.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
(3 * std::mem::size_of::<f32>()) as i32,
std::ptr::null()
);
gl::EnableVertexAttribArray(0);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
gl::DeleteVertexArrays(1, &vao);
gl::DeleteBuffers(1, &vbo);
gl::DeleteBuffers(1, &ebo);
if current_shader != 0 {
gl::UseProgram(current_shader as u32);
}
}
}
pub fn text_renderer_mut(&mut self) -> &mut TextRenderer {
&mut self.text_renderer
}
pub fn draw_textured_rect(
&self,
x: f32,
y: f32,
width: f32,
height: f32,
texture_id: u32,
opacity: f32
) {
self.texture_shader.use_program();
let projection = glm::ortho(0.0, self.screen_width, self.screen_height, 0.0, -1.0, 1.0);
self.texture_shader.set_mat4("projection", &projection);
self.texture_shader.set_float("uOpacity", opacity);
unsafe {
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, texture_id);
let vertices: [f32; 20] = [
x,
y,
0.0,
0.0,
1.0,
x + width,
y,
0.0,
1.0,
1.0,
x + width,
y + height,
0.0,
1.0,
0.0,
x,
y + height,
0.0,
0.0,
0.0,
];
let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
let mut vao = 0;
let mut vbo = 0;
let mut ebo = 0;
gl::GenVertexArrays(1, &mut vao);
gl::GenBuffers(1, &mut vbo);
gl::GenBuffers(1, &mut ebo);
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(vertices.len() * std::mem::size_of::<f32>()) as isize,
vertices.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(indices.len() * std::mem::size_of::<u32>()) as isize,
indices.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
(5 * std::mem::size_of::<f32>()) as i32,
std::ptr::null()
);
gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(
1,
2,
gl::FLOAT,
gl::FALSE,
(5 * std::mem::size_of::<f32>()) as i32,
(3 * std::mem::size_of::<f32>()) as *const _
);
gl::EnableVertexAttribArray(1);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
gl::DeleteVertexArrays(1, &vao);
gl::DeleteBuffers(1, &vbo);
gl::DeleteBuffers(1, &ebo);
}
}
pub fn draw_triangle(&mut self, vertices: [(f32, f32); 3], color: [f32; 4]) {
let current_shader = unsafe {
let mut current_program = 0;
gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
current_program
};
self.shader.use_program();
self.shader.set_vec4("uColor", &color);
let vertices_ndc: Vec<f32> = vertices
.iter()
.flat_map(|(x, y)| {
let x_ndc = (x / self.screen_width) * 2.0 - 1.0;
let y_ndc = 1.0 - (y / self.screen_height) * 2.0;
vec![x_ndc, y_ndc, 0.0]
})
.collect();
unsafe {
gl::BindVertexArray(self.vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(vertices_ndc.len() * std::mem::size_of::<f32>()) as isize,
vertices_ndc.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::DrawArrays(gl::TRIANGLES, 0, 3);
}
unsafe {
gl::UseProgram(current_shader as u32);
}
}
pub fn draw_circle(&mut self, center_x: f32, center_y: f32, radius: f32, color: [f32; 4]) {
let current_shader = unsafe {
let mut current_program = 0;
gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
current_program
};
self.shader.use_program();
self.shader.set_vec4("uColor", &color);
let vertices_transformed: Vec<f32> = self.circle_vertices
.chunks(3)
.flat_map(|v| {
let x = center_x + v[0] * radius;
let y = center_y + v[1] * radius;
vec![(x / self.screen_width) * 2.0 - 1.0, 1.0 - (y / self.screen_height) * 2.0, 0.0]
})
.collect();
unsafe {
gl::BindVertexArray(self.vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(vertices_transformed.len() * std::mem::size_of::<f32>()) as isize,
vertices_transformed.as_ptr() as *const _,
gl::STATIC_DRAW
);
gl::DrawArrays(gl::TRIANGLE_FAN, 0, CIRCLE_SEGMENTS as i32);
}
unsafe {
gl::UseProgram(current_shader as u32);
}
}
}