use gl;
use gl::types::{GLboolean, GLchar, GLenum, GLint, GLsizeiptr, GLuint};
use std::ffi::CString;
use std::{ptr, mem};
pub struct DynamicAttribute {
vbo: GLuint,
size: i32,
location: GLuint,
normalize: GLboolean,
ty: GLenum,
}
impl Drop for DynamicAttribute {
fn drop(&mut self) {
unsafe {
gl::DeleteBuffers(1, &self.vbo);
}
}
}
impl DynamicAttribute {
fn bind_vao(&self, vao: GLuint) {
let stride = 0;
unsafe {
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::VertexAttribPointer(self.location,
self.size,
self.ty,
self.normalize,
stride,
ptr::null());
}
}
fn new(program: GLuint,
name: &str,
size: i32,
normalize: GLboolean,
ty: GLenum,
vao: GLuint)
-> Result<Self, String> {
let location = try!(attribute_location(program, name));
let mut vbo = 0;
unsafe {
gl::GenBuffers(1, &mut vbo);
}
let res = DynamicAttribute {
vbo: vbo,
size: size,
location: location,
normalize: normalize,
ty: ty,
};
res.bind_vao(vao);
Ok(res)
}
pub fn xyz(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
}
pub fn xy(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
}
pub fn rgb(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
}
pub fn rgba(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT, vao)
}
pub fn uv(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
}
pub unsafe fn set<T>(&self, data: &[T]) {
gl::EnableVertexAttribArray(self.location);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BufferData(gl::ARRAY_BUFFER,
data.len() as GLsizeiptr * mem::size_of::<T>() as GLsizeiptr,
mem::transmute(data.as_ptr()),
gl::DYNAMIC_DRAW);
}
}
pub fn compile_shader(shader_type: GLenum, source: &str) -> Result<GLuint, String> {
unsafe {
let shader = gl::CreateShader(shader_type);
let c_source = match CString::new(source) {
Ok(x) => x,
Err(err) => return Err(format!("compile_shader: {}", err)),
};
gl::ShaderSource(shader, 1, &c_source.as_ptr(), ptr::null());
drop(source);
gl::CompileShader(shader);
let mut status = gl::FALSE as GLint;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
if status == (gl::TRUE as GLint) {
Ok(shader)
} else {
let mut len = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
if len == 0 {
Err("Compilation failed with no log. \
The OpenGL context might have been created on another thread, \
or not have been created."
.to_string())
} else {
let mut buf = vec![0; len as usize - 1];
gl::GetShaderInfoLog(shader,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar);
gl::DeleteShader(shader);
Err(String::from_utf8(buf).ok().expect("ShaderInfoLog not valid utf8"))
}
}
}
}
pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
unsafe {
let c_name = match CString::new(name) {
Ok(x) => x,
Err(err) => return Err(format!("attribute_location: {}", err)),
};
let id = gl::GetAttribLocation(program, c_name.as_ptr());
drop(c_name);
if id < 0 {
Err(format!("Attribute '{}' does not exists in shader", name))
} else {
Ok(id as GLuint)
}
}
}
pub fn uniform_location(program: GLuint, name: &str) -> Result<GLuint, String> {
unsafe {
let c_name = match CString::new(name) {
Ok(x) => x,
Err(err) => return Err(format!("uniform_location: {}", err)),
};
let id = gl::GetUniformLocation(program, c_name.as_ptr());
drop(c_name);
if id < 0 {
Err(format!("Uniform '{}' does not exists in shader", name))
} else {
Ok(id as GLuint)
}
}
}