use draw::{BlendMode, Context, Drawable, DrawableMut, Drawer, IDENTITY};
use gl;
use gl::types::*;
use resources::Resource;
use shader::*;
use std;
use std::ops::{Index, IndexMut};
use texture::Texture;
use vertex::*;
#[derive(Debug, Clone, PartialEq)]
pub struct VertexBuffer {
id: u32,
texture: Option<Resource<Texture>>,
array: VertexArray,
primitive: GLenum,
len: usize,
}
#[derive(Debug, Clone, PartialEq, Copy, Hash)]
pub enum Primitive {
Triangles,
Quads,
TrianglesStrip,
TriangleFan,
Points,
Lines,
}
impl Primitive {
pub fn get_gl_type(self) -> GLenum {
match self {
Primitive::Quads => gl::QUADS,
Primitive::Triangles => gl::TRIANGLES,
Primitive::Points => gl::POINTS,
Primitive::Lines => gl::LINES,
Primitive::TrianglesStrip => gl::TRIANGLE_STRIP,
Primitive::TriangleFan => gl::TRIANGLE_STRIP,
}
}
pub fn get_primitive(prim: GLenum) -> Primitive {
match prim {
gl::QUADS => Primitive::Quads,
gl::TRIANGLES => Primitive::Triangles,
gl::TRIANGLE_STRIP => Primitive::TrianglesStrip,
gl::LINES => Primitive::Lines,
gl::TRIANGLE_FAN => Primitive::TriangleFan,
_ => Primitive::Points,
}
}
}
impl Into<GLenum> for Primitive {
fn into(self) -> GLenum {
self.get_gl_type()
}
}
impl VertexBuffer {
fn clear_gl() {
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
}
}
pub fn clear(&mut self) {
self.array.clear();
}
pub fn new(t: Primitive, vertice: VertexArray) -> VertexBuffer {
let mut buffer_id: u32 = 0;
unsafe {
gl::GenBuffers(1, &mut buffer_id);
vertice.bind();
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_id);
gl::BufferData(
gl::ARRAY_BUFFER,
(std::mem::size_of::<GLfloat>() * vertice.len() * 8) as GLsizeiptr,
vertice.get_ptr(),
gl::STATIC_DRAW,
);
vertice.active();
};
let vertex_buffer = VertexBuffer {
id: buffer_id,
texture: None,
primitive: Self::get_gl_type(t),
len: vertice.len(),
array: vertice,
};
Self::clear_gl();
vertex_buffer
}
pub fn append(&mut self, vertices: &[Vertex]) {
self.array.array_mut().append(&mut Vec::from(vertices));
}
fn get_gl_type(prim: Primitive) -> GLenum {
match prim {
Primitive::Quads => gl::QUADS,
Primitive::Triangles => gl::TRIANGLES,
Primitive::Points => gl::POINTS,
Primitive::Lines => gl::LINES,
Primitive::TrianglesStrip => gl::TRIANGLE_STRIP,
Primitive::TriangleFan => gl::TRIANGLE_FAN,
}
}
fn set_texture(&mut self, texture: &Resource<Texture>) {
self.texture = Some(Resource::clone(texture));
}
pub fn get_primitive(&self) -> Primitive {
match self.primitive {
gl::QUADS => Primitive::Quads,
gl::TRIANGLES => Primitive::Triangles,
gl::TRIANGLE_STRIP => Primitive::TrianglesStrip,
gl::LINES => Primitive::Lines,
gl::TRIANGLE_FAN => Primitive::TriangleFan,
_ => Primitive::Points,
}
}
pub fn set_geometry(&mut self, vertice: &[Vertex]) {
self.array = VertexArray::from(vertice);
}
#[inline]
pub fn bind(&self) {
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, self.id);
}
}
#[inline]
pub fn unbind(&self) {
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
}
}
}
impl DrawableMut for VertexBuffer {
fn draw_mut<T: Drawer>(&mut self, target: &mut T) {
self.update();
self.draw(target);
}
fn draw_with_context_mut(&mut self, context: &mut Context) {
self.update();
self.draw_with_context(context);
}
}
impl Drawable for VertexBuffer {
fn draw<T: Drawer>(&self, target: &mut T) {
let texture = if let Some(ref rc_texture) = self.texture {
Some(rc_texture.as_ref())
} else {
None
};
let mut context = Context::new(
texture,
if texture.is_none() {
&*NO_TEXTURE_SHADER
} else {
&*DEFAULT_SHADER
},
vec![
("transform".to_string(), &*IDENTITY),
("projection".to_string(), target.projection()),
],
BlendMode::Alpha,
);
unsafe {
self.setup_draw(&mut context);
self.array.bind();
self.bind();
gl::DrawArrays(self.primitive, 0, self.array.len() as i32);
self.unbind();
}
}
fn draw_with_context(&self, context: &mut Context) {
unsafe {
self.setup_draw(context);
self.array.bind();
self.bind();
gl::DrawArrays(self.primitive, 0, self.array.len() as i32);
self.unbind();
}
}
fn update(&mut self) {
unsafe {
self.array.bind();
self.bind();
if self.len != self.array.len() {
gl::BufferData(
gl::ARRAY_BUFFER,
(std::mem::size_of::<GLfloat>() * self.array.len() * 8) as GLsizeiptr,
self.array.get_ptr(),
gl::STATIC_DRAW,
);
self.len = self.array.len();
}
gl::BufferSubData(
gl::ARRAY_BUFFER,
0,
(std::mem::size_of::<GLfloat>() * self.array.len() * 8) as GLsizeiptr,
self.array.get_ptr(),
);
self.array.active();
self.unbind();
self.array.unbind();
}
}
}
impl Index<usize> for VertexBuffer {
type Output = Vertex;
fn index(&self, vertex_index: usize) -> &Vertex {
&self.array[vertex_index]
}
}
impl IndexMut<usize> for VertexBuffer {
fn index_mut(&mut self, index: usize) -> &mut Vertex {
&mut self.array[index]
}
}
impl Default for VertexBuffer {
fn default() -> Self {
VertexBuffer::new(Primitive::Triangles, VertexArray::new())
}
}
impl Drop for VertexBuffer {
fn drop(&mut self) {
unsafe {
gl::DeleteBuffers(1, &[self.id] as *const _);
}
println!("Buffer {} deleted.", self.id);
}
}