use super::DrawingError;
use gl;
use maths::{Vector2f, Vector3f};
use std::{cmp, mem, ptr};
pub const MAX_BATCH_SIZE: usize = 1000;
pub const BATCH_INSTANCE_SIZE: usize = 20;
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Vertex {
pub position: Vector3f,
pub uv: Vector2f,
}
impl Vertex {
pub fn attrib_arrays() {
let stride = mem::size_of::<Self>();
unsafe {
Vertex::attrib_array(stride, 0, 0, 3);
Vertex::attrib_array(stride, 1, mem::size_of::<Vector3f>(), 2);
}
}
unsafe fn attrib_array(stride: usize, location: gl::types::GLuint, offset: usize, length: i32) {
gl::EnableVertexAttribArray(location);
gl::VertexAttribPointer(
location, length, gl::FLOAT,
gl::FALSE, stride as gl::types::GLint, offset as *const gl::types::GLvoid, );
}
}
#[derive(Copy, Clone, Debug)]
pub struct Mesh {
vbo: gl::types::GLuint,
vao: gl::types::GLuint,
ebo: gl::types::GLuint,
batch_vbo: gl::types::GLuint,
vertex_count: usize,
indices_count: usize,
}
impl Mesh {
pub fn vbo(&self) -> gl::types::GLuint {
self.vbo
}
pub fn vao(&self) -> gl::types::GLuint {
self.vao
}
pub fn ebo(&self) -> gl::types::GLuint {
self.ebo
}
pub fn batch_vbo(&self) -> gl::types::GLuint {
self.batch_vbo
}
pub fn vertex_count(&self) -> usize {
self.vertex_count
}
pub fn indices_count(&self) -> usize {
self.indices_count
}
pub fn check(&self) -> Result<(), DrawingError> {
if self.ebo == 0 {
return Err(DrawingError::MeshEBONotInitialized);
}
if self.vao == 0 {
return Err(DrawingError::MeshVAONotInitialized);
}
Ok(())
}
}
impl PartialEq for Mesh {
fn eq(&self, other: &Self) -> bool {
self.ebo == other.ebo && self.vao == other.vao
}
}
impl Eq for Mesh {}
impl PartialOrd for Mesh {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Mesh {
fn cmp(&self, other: &Self) -> cmp::Ordering {
if self.ebo != other.ebo {
self.ebo.cmp(&other.ebo)
} else {
self.vao.cmp(&other.vao)
}
}
}
#[derive(Debug)]
pub struct MeshBuilder {
pub vertices: Vec<Vertex>,
pub indices: Vec<gl::types::GLuint>,
}
impl Default for MeshBuilder {
fn default() -> Self {
Self::new()
}
}
impl MeshBuilder {
pub fn new() -> MeshBuilder {
MeshBuilder {
vertices: Vec::new(),
indices: Vec::new(),
}
}
pub fn build(&self) -> Mesh {
let mut vao: gl::types::GLuint = 0;
let mut vbo: gl::types::GLuint = 0;
let mut ebo: gl::types::GLuint = 0;
unsafe {
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,
(self.vertices.len() * mem::size_of::<Vertex>()) as gl::types::GLsizeiptr, self.vertices.as_ptr() as *const gl::types::GLvoid, gl::STATIC_DRAW,
);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(self.indices.len() * mem::size_of::<gl::types::GLuint>()) as gl::types::GLsizeiptr, self.indices.as_ptr() as *const gl::types::GLvoid, gl::STATIC_DRAW,
);
}
Vertex::attrib_arrays();
let batch_vbo = Self::empty_vbo(MAX_BATCH_SIZE);
Self::add_instanced_attribute(vao, batch_vbo, 2, 4, BATCH_INSTANCE_SIZE as i32, 0); Self::add_instanced_attribute(vao, batch_vbo, 3, 4, BATCH_INSTANCE_SIZE as i32, 4); Self::add_instanced_attribute(vao, batch_vbo, 4, 4, BATCH_INSTANCE_SIZE as i32, 8); Self::add_instanced_attribute(vao, batch_vbo, 5, 4, BATCH_INSTANCE_SIZE as i32, 12); Self::add_instanced_attribute(vao, batch_vbo, 6, 4, BATCH_INSTANCE_SIZE as i32, 16);
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
}
Mesh {
vbo,
vao,
ebo,
batch_vbo,
vertex_count: self.vertices.len(),
indices_count: self.indices.len(),
}
}
fn empty_vbo(floats: usize) -> gl::types::GLuint {
let mut vbo = 0;
unsafe {
gl::GenBuffers(1, &mut vbo);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(floats * mem::size_of::<f32>()) as gl::types::GLsizeiptr,
ptr::null(),
gl::STREAM_DRAW,
);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
}
vbo
}
fn add_instanced_attribute(
vao: gl::types::GLuint,
vbo: gl::types::GLuint,
location: gl::types::GLuint,
size: gl::types::GLint,
stride: gl::types::GLsizei,
offset: usize,
) {
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BindVertexArray(vao);
gl::EnableVertexAttribArray(location);
gl::VertexAttribPointer(
location,
size,
gl::FLOAT,
gl::FALSE,
stride * mem::size_of::<f32>() as i32,
(offset * mem::size_of::<f32>()) as *const gl::types::GLvoid,
);
gl::VertexAttribDivisor(location, 1);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
}
}
}