use crate::{
assets::Sprite,
math::{Mat4, Vec2, Vec3, Vec4},
};
mod opengl;
pub(crate) use opengl::OpenGLRenderer;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Vertex {
pub pos: Vec3,
pub col: Vec4,
pub uv: Vec2,
pub texture_slot: f32,
}
impl Vertex {
pub const fn new(pos: Vec3, col: Vec4) -> Self {
Self {
pos,
col,
uv: Vec2::new(1., 1.),
texture_slot: 0.,
}
}
}
pub enum RenderCommand {
RenderQuads {
offset: usize,
num_quads: usize,
transform: Mat4,
},
ClearScreen {
color: Vec4,
},
}
pub struct RenderCommands {
vert_buffer_id: u32,
index_buffer_id: u32,
white_texture_id: u32,
vertices: Vec<Vertex>,
textures: Vec<u32>,
commands: Vec<RenderCommand>,
}
impl RenderCommands {
fn new(
vert_buffer_id: u32,
index_buffer_id: u32,
white_texture_id: u32,
max_quads: usize,
) -> Self {
let vertices = Vec::with_capacity(max_quads * 4);
let commands = Vec::new();
let textures = Vec::new();
Self {
vert_buffer_id,
index_buffer_id,
white_texture_id,
vertices,
textures,
commands,
}
}
fn reset(&mut self) {
self.vertices.clear();
self.commands.clear();
self.textures.clear();
self.textures.push(self.white_texture_id);
}
pub fn begin_render_layer(&mut self, transform: Mat4) -> RenderLayer<'_> {
RenderLayer::new(self, transform)
}
pub fn with_render_layer<F: FnMut(&mut RenderLayer<'_>)>(&mut self, transform: Mat4, mut f: F) {
let mut render_layer = self.begin_render_layer(transform);
f(&mut render_layer);
render_layer.end();
}
fn push(&mut self, command: RenderCommand) {
self.commands.push(command);
}
}
pub struct RenderLayer<'rc> {
commands: &'rc mut RenderCommands,
transform: Mat4,
offset: usize,
num_quads: usize,
}
impl<'rc> RenderLayer<'rc> {
fn new(commands: &'rc mut RenderCommands, transform: Mat4) -> Self {
let offset = commands.vertices.len();
Self {
commands,
transform,
offset,
num_quads: 0,
}
}
pub fn clear_screen(&mut self, color: Vec4) {
self.commands.push(RenderCommand::ClearScreen { color });
}
pub fn push_quad(&mut self, pos: Vec2, dim: Vec2, color: Vec4) {
self.push_tex_quad(pos, dim, color, self.commands.white_texture_id);
}
fn get_texture_slot(&mut self, texture_id: u32) -> f32 {
if let Some(loc) = self.commands.textures.iter().position(|i| *i == texture_id) {
loc as f32
} else {
let loc = self.commands.textures.len();
self.commands.textures.push(texture_id);
loc as f32
}
}
pub fn push_tex_quad(&mut self, pos: Vec2, dim: Vec2, color: Vec4, texture_id: u32) {
let Vec2 {
x: half_width,
y: half_height,
} = dim * 0.5;
let bottom_left = pos + Vec2::new(-half_width, -half_height);
let bottom_right = pos + Vec2::new(half_width, -half_height);
let top_right = pos + Vec2::new(half_width, half_height);
let top_left = pos + Vec2::new(-half_width, half_height);
let to_vec3 = |Vec2 { x, y }: Vec2| -> Vec3 { Vec3::new(x, y, 0.) };
let texture_slot = self.get_texture_slot(texture_id);
let mut bottom_left = Vertex::new(to_vec3(bottom_left), color);
bottom_left.uv = Vec2::new(0., 1.);
bottom_left.texture_slot = texture_slot;
let mut bottom_right = Vertex::new(to_vec3(bottom_right), color);
bottom_right.uv = Vec2::new(1., 1.);
bottom_right.texture_slot = texture_slot;
let mut top_right = Vertex::new(to_vec3(top_right), color);
top_right.uv = Vec2::new(1., 0.);
top_right.texture_slot = texture_slot;
let mut top_left = Vertex::new(to_vec3(top_left), color);
top_left.uv = Vec2::new(0., 0.);
top_left.texture_slot = texture_slot;
self.store_quad([bottom_left, bottom_right, top_right, top_left]);
}
pub fn push_sprite(&mut self, pos: Vec2, dim: Vec2, color: Vec4, sprite: Sprite) {
let Vec2 {
x: half_width,
y: half_height,
} = dim * 0.5;
let bottom_left = pos + Vec2::new(-half_width, -half_height);
let bottom_right = pos + Vec2::new(half_width, -half_height);
let top_right = pos + Vec2::new(half_width, half_height);
let top_left = pos + Vec2::new(-half_width, half_height);
let to_vec3 = |Vec2 { x, y }: Vec2| -> Vec3 { Vec3::new(x, y, 0.) };
let texture_slot = self.get_texture_slot(sprite.texture_id);
let mut bottom_left = Vertex::new(to_vec3(bottom_left), color);
bottom_left.uv = sprite.uv[3];
bottom_left.texture_slot = texture_slot;
let mut bottom_right = Vertex::new(to_vec3(bottom_right), color);
bottom_right.uv = sprite.uv[2];
bottom_right.texture_slot = texture_slot;
let mut top_right = Vertex::new(to_vec3(top_right), color);
top_right.uv = sprite.uv[1];
top_right.texture_slot = texture_slot;
let mut top_left = Vertex::new(to_vec3(top_left), color);
top_left.uv = sprite.uv[0];
top_left.texture_slot = texture_slot;
self.store_quad([bottom_left, bottom_right, top_right, top_left]);
}
fn store_quad(&mut self, [v1, v2, v3, v4]: [Vertex; 4]) {
self.num_quads += 1;
self.commands.vertices.push(v1);
self.commands.vertices.push(v2);
self.commands.vertices.push(v3);
self.commands.vertices.push(v4);
}
pub fn end(self) -> &'rc mut RenderCommands {
let Self {
offset,
num_quads,
transform,
..
} = self;
if num_quads > 0 {
self.commands.push(RenderCommand::RenderQuads {
offset,
num_quads,
transform,
});
}
self.commands
}
}