use std::{
ffi::{c_void, CString},
mem, ptr, str,
};
use gl::types::*;
const POSITION_ATTR: GLuint = 0;
const VBO_OFFSET: *const c_void = ptr::null();
#[rustfmt::skip]
static VERTEX_DATA: [GLfloat; 8] = [
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0
];
const VERTEX: &str = include_str!("vertex.glsl");
const FRAGMENT: &str = include_str!("fragment.glsl");
fn compile_shader(src: &str, ty: GLenum) -> GLuint {
let shader;
unsafe {
shader = gl::CreateShader(ty);
if shader == 0 {
bug!("gl::CreateShader failed");
}
let src_ptr: *const _ = &src;
gl::ShaderSource(shader, 1, src_ptr.cast(), &(src.len() as GLint));
gl::CompileShader(shader);
let mut status = GLint::from(gl::FALSE);
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
if status != GLint::from(gl::TRUE) {
let mut len = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
let mut buf: Vec<u8> = Vec::with_capacity(len as usize);
gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr().cast());
buf.set_len((len as usize) - 1);
bug!(
"{}",
str::from_utf8(&buf).expect("ShaderInfoLog not valid utf8")
);
}
}
shader
}
fn compile_program(vertex: &str, fragment: &str) -> GLuint {
let vs = compile_shader(vertex, gl::VERTEX_SHADER);
let fs = compile_shader(fragment, gl::FRAGMENT_SHADER);
unsafe {
let program = gl::CreateProgram();
if program == 0 {
bug!("gl::CreateShader failed");
}
gl::AttachShader(program, vs);
gl::AttachShader(program, fs);
gl::LinkProgram(program);
let mut status = GLint::from(gl::FALSE);
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
if status != GLint::from(gl::TRUE) {
let mut len: GLint = 0;
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
gl::GetProgramInfoLog(
program,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar,
);
buf.set_len(len as usize - 1);
bug!(
"{}",
str::from_utf8(&buf).expect("ProgramInfoLog not valid utf8")
);
}
gl::DetachShader(program, fs);
gl::DeleteShader(fs);
gl::DetachShader(program, vs);
gl::DeleteShader(vs);
super::update_program(program);
let color_str = CString::new("color").unwrap();
gl::BindFragDataLocation(program, 0, color_str.as_ptr());
program
}
}
fn init_vertex_buffer(vbo: GLuint, data: &[GLfloat]) {
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
mem::size_of_val(data) as GLsizeiptr,
data.as_ptr().cast(),
gl::STATIC_DRAW,
);
let gl_error = gl::GetError();
match gl_error {
gl::NO_ERROR => (),
gl::OUT_OF_MEMORY => {
panic!("OpenGl is out of memory and in an invalid state");
}
e => bug!("unexpected error: {}", e),
}
}
}
fn get_uniform_id(program: GLuint, name_str: &str) -> GLint {
let name = CString::new(name_str).unwrap();
let id = unsafe { gl::GetUniformLocation(program, name.as_ptr()) };
if id == -1 {
bug!("unknown uniform in program {}: {}", program, name_str)
} else {
id
}
}
#[derive(Debug)]
pub struct Program {
pub id: GLuint,
pub vao: GLuint,
vbo: GLuint,
}
impl Program {
pub fn new() -> (Self, Uniforms) {
let program = compile_program(VERTEX, FRAGMENT);
let mut vao = 0;
let mut vbo = 0;
unsafe {
gl::GenVertexArrays(1, &mut vao);
gl::GenBuffers(1, &mut vbo);
gl::BindVertexArray(vao);
init_vertex_buffer(vbo, &VERTEX_DATA);
gl::EnableVertexAttribArray(POSITION_ATTR);
gl::VertexAttribPointer(
POSITION_ATTR,
2,
gl::FLOAT,
gl::FALSE as GLboolean,
0,
VBO_OFFSET,
);
}
let prog = Program {
id: program,
vao,
vbo,
};
let uniforms = prog.get_uniforms();
(prog, uniforms)
}
pub fn get_uniforms(&self) -> Uniforms {
Uniforms {
source: get_uniform_id(self.id, "source"),
color_modulation: get_uniform_id(self.id, "color_modulation"),
invert_color: get_uniform_id(self.id, "invert_color"),
flip_vertically: get_uniform_id(self.id, "flip_vertically"),
flip_horizontally: get_uniform_id(self.id, "flip_horizontally"),
target_dimensions: get_uniform_id(self.id, "target_dimensions"),
source_texture_dimensions: get_uniform_id(self.id, "source_texture_dimensions"),
source_texture_offset: get_uniform_id(self.id, "source_texture_offset"),
source_dimensions: get_uniform_id(self.id, "source_dimensions"),
source_position: get_uniform_id(self.id, "source_position"),
source_scale: get_uniform_id(self.id, "source_scale"),
source_rotation: get_uniform_id(self.id, "source_rotation"),
depth: get_uniform_id(self.id, "depth"),
}
}
}
impl Drop for Program {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.id);
gl::DeleteBuffers(1, &self.vbo);
gl::DeleteVertexArrays(1, &self.vao);
}
}
}
#[derive(Debug, Clone)]
pub struct Uniforms {
pub source: GLint,
pub color_modulation: GLint,
pub invert_color: GLint,
pub flip_vertically: GLint,
pub flip_horizontally: GLint,
pub target_dimensions: GLint,
pub source_texture_dimensions: GLint,
pub source_texture_offset: GLint,
pub source_dimensions: GLint,
pub source_position: GLint,
pub source_scale: GLint,
pub source_rotation: GLint,
pub depth: GLint,
}
#[rustfmt::skip]
static LINES_VERTEX_DATA: [GLfloat; 8] = [
1.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 1.0,
];
#[rustfmt::skip]
static RECTANGLES_VERTEX_DATA: [GLfloat; 20] = [
1.0, 1.0, 0.0, 0.0,
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 1.0, 0.0,
1.0, 1.0, 0.0, 0.0,
];
#[derive(Debug)]
pub struct DebugProgram {
pub id: GLuint,
pub vao: [GLuint; 2],
pub vbo: [GLuint; 2],
}
impl DebugProgram {
pub fn new() -> (Self, DebugUniforms) {
let program = compile_program(
include_str!("vertex_debug.glsl"),
include_str!("fragment_debug.glsl"),
);
let mut vao = [0; 2];
let mut vbo = [0; 2];
unsafe {
gl::GenVertexArrays(2, vao.as_mut_ptr());
gl::GenBuffers(2, vbo.as_mut_ptr());
gl::BindVertexArray(vao[0]);
init_vertex_buffer(vbo[0], &LINES_VERTEX_DATA);
gl::EnableVertexAttribArray(POSITION_ATTR);
gl::VertexAttribPointer(
POSITION_ATTR,
4,
gl::FLOAT,
gl::FALSE as GLboolean,
0,
ptr::null(),
);
gl::BindVertexArray(vao[1]);
init_vertex_buffer(vbo[1], &RECTANGLES_VERTEX_DATA);
gl::EnableVertexAttribArray(POSITION_ATTR);
gl::VertexAttribPointer(
POSITION_ATTR,
4,
gl::FLOAT,
gl::FALSE as GLboolean,
0,
ptr::null(),
);
}
let line_color_uniform = get_uniform_id(program, "line_color");
let start_end = get_uniform_id(program, "start_end");
(
Self {
id: program,
vao,
vbo,
},
DebugUniforms {
line_color: line_color_uniform,
start_end,
},
)
}
}
impl Drop for DebugProgram {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.id);
gl::DeleteBuffers(2, self.vbo.as_ptr());
gl::DeleteVertexArrays(2, self.vao.as_ptr());
}
}
}
#[derive(Debug)]
pub struct DebugUniforms {
pub line_color: GLint,
pub start_end: GLint,
}