diff --git a/src/main.rs b/src/main.rs index 7da9b4e9..69e4ca6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,23 +8,10 @@ extern crate euclid; mod list_fonts; mod text; +mod renderer; -use std::ffi::CString; -use std::mem::size_of; -use std::ptr; +use renderer::{Glyph, QuadRenderer}; -use euclid::{Rect, Size2D, Point2D}; - -use libc::c_void; - -use gl::types::*; - -use cgmath::Matrix; - -use text::RasterizedGlyph; - -static TEXT_SHADER_V: &'static str = include_str!("../res/text.v.glsl"); -static TEXT_SHADER_F: &'static str = include_str!("../res/text.f.glsl"); fn main() { let window = glutin::Window::new().unwrap(); @@ -39,6 +26,7 @@ fn main() { } let rasterizer = text::Rasterizer::new(); + let glyph_R = Glyph::new(&rasterizer.get_glyph(180., 'R')); let glyph_u = Glyph::new(&rasterizer.get_glyph(180., 'u')); let glyph_s = Glyph::new(&rasterizer.get_glyph(180., 's')); @@ -72,279 +60,3 @@ fn main() { } } -struct QuadRenderer { - program: ShaderProgram, - vao: GLuint, - vbo: GLuint, - ebo: GLuint, -} - -impl QuadRenderer { - // TODO should probably hand this a transform instead of width/height - pub fn new(width: u32, height: u32) -> QuadRenderer { - let program = ShaderProgram::new(width, height); - - let mut vao: GLuint = 0; - let mut vbo: GLuint = 0; - let mut ebo: 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, - (size_of::() * 4 * 4) as GLsizeiptr, - ptr::null(), - gl::DYNAMIC_DRAW - ); - - let indices: [u32; 6] = [0, 1, 3, - 1, 2, 3]; - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, - 6 * size_of::() as isize, - indices.as_ptr() as *const _, - gl::STATIC_DRAW); - - gl::EnableVertexAttribArray(0); - gl::VertexAttribPointer(0, 4, gl::FLOAT, gl::FALSE, 4 * size_of::() as i32, - ptr::null()); - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0); - gl::BindBuffer(gl::ARRAY_BUFFER, 0); - gl::BindVertexArray(0); - } - - QuadRenderer { - program: program, - vao: vao, - vbo: vbo, - ebo: ebo, - } - } - - pub fn render(&self, glyph: &Glyph, x: f32, y: f32) { - self.program.activate(); - unsafe { - // set color - gl::Uniform3f(self.program.u_color, 1., 1., 0.5); - } - - let rect = get_rect(glyph, x, y); - - // top right of character - let vertices: [[f32; 4]; 4] = [ - [rect.max_x(), rect.max_y(), 1., 0.], // top-right - [rect.max_x(), rect.min_y(), 1., 1.], // bottom-right - [rect.min_x(), rect.min_y(), 0., 1.], // bottom-left - [rect.min_x(), rect.max_y(), 0., 0.], // top-left - ]; - - unsafe { - bind_mask_texture(glyph.tex_id); - gl::BindVertexArray(self.vao); - - gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); - gl::BufferSubData( - gl::ARRAY_BUFFER, - 0, - (4 * 4 * size_of::()) as isize, - vertices.as_ptr() as *const _ - ); - gl::BindBuffer(gl::ARRAY_BUFFER, 0); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); - - gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null()); - gl::BindVertexArray(0); - gl::BindTexture(gl::TEXTURE_2D, 0); - } - - self.program.deactivate(); - } -} - -fn get_rect(glyph: &Glyph, x: f32, y: f32) -> Rect { - Rect::new( - Point2D::new(x, y), - Size2D::new(glyph.width as f32, glyph.height as f32) - ) -} - -fn bind_mask_texture(id: u32) { - unsafe { - gl::ActiveTexture(gl::TEXTURE0); - gl::BindTexture(gl::TEXTURE_2D, id); - } -} - -pub struct ShaderProgram { - id: GLuint, - /// projection matrix uniform - u_projection: GLint, - /// color uniform - u_color: GLint, -} - -impl ShaderProgram { - pub fn activate(&self) { - unsafe { - gl::UseProgram(self.id); - } - } - - pub fn deactivate(&self) { - unsafe { - gl::UseProgram(0); - } - } - - pub fn new(width: u32, height: u32) -> ShaderProgram { - let vertex_shader = ShaderProgram::create_vertex_shader(); - let fragment_shader = ShaderProgram::create_fragment_shader(); - let program = ShaderProgram::create_program(vertex_shader, fragment_shader); - - unsafe { - gl::DeleteShader(vertex_shader); - gl::DeleteShader(fragment_shader); - } - - // get uniform locations - let projection_str = CString::new("projection").unwrap(); - let color_str = CString::new("textColor").unwrap(); - - let (projection, color) = unsafe { - ( - gl::GetUniformLocation(program, projection_str.as_ptr()), - gl::GetUniformLocation(program, color_str.as_ptr()) - ) - }; - - assert!(projection != gl::INVALID_VALUE as i32); - assert!(projection != gl::INVALID_OPERATION as i32); - assert!(color != gl::INVALID_VALUE as i32); - assert!(color != gl::INVALID_OPERATION as i32); - - let shader = ShaderProgram { - id: program, - u_projection: projection, - u_color: color, - }; - - // set projection uniform - let ortho = cgmath::ortho(0., width as f32, 0., height as f32, -1., 1.); - let projection: [[f32; 4]; 4] = ortho.into(); - - println!("width: {}, height: {}", width, height); - - shader.activate(); - unsafe { - gl::UniformMatrix4fv(shader.u_projection, - 1, gl::FALSE, projection.as_ptr() as *const _); - } - shader.deactivate(); - - shader - } - - fn create_program(vertex: GLuint, fragment: GLuint) -> GLuint { - - unsafe { - let program = gl::CreateProgram(); - gl::AttachShader(program, vertex); - gl::AttachShader(program, fragment); - gl::LinkProgram(program); - - let mut success: GLint = 0; - gl::GetProgramiv(program, gl::LINK_STATUS, &mut success); - - if success != (gl::TRUE as GLint) { - panic!("failed to link shader program"); - } - program - } - } - - fn create_fragment_shader() -> GLuint { - unsafe { - let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); - let fragment_source = CString::new(TEXT_SHADER_F).unwrap(); - gl::ShaderSource(fragment_shader, 1, &fragment_source.as_ptr(), ptr::null()); - gl::CompileShader(fragment_shader); - - let mut success: GLint = 0; - gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success); - - if success != (gl::TRUE as GLint) { - panic!("failed to compiler fragment shader"); - } - fragment_shader - } - } - - fn create_vertex_shader() -> GLuint { - unsafe { - let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); - let vertex_source = CString::new(TEXT_SHADER_V).unwrap(); - gl::ShaderSource(vertex_shader, 1, &vertex_source.as_ptr(), ptr::null()); - gl::CompileShader(vertex_shader); - - let mut success: GLint = 0; - gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success); - - if success != (gl::TRUE as GLint) { - panic!("failed to compiler vertex shader"); - } - vertex_shader - } - } -} - -struct Glyph { - tex_id: GLuint, - top: i32, - left: i32, - width: i32, - height: i32, -} - -impl Glyph { - pub fn new(rasterized: &RasterizedGlyph) -> Glyph { - let mut id: GLuint = 0; - unsafe { - gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1); - gl::GenTextures(1, &mut id); - gl::BindTexture(gl::TEXTURE_2D, id); - gl::TexImage2D( - gl::TEXTURE_2D, - 0, - gl::RED as i32, - rasterized.width as i32, - rasterized.height as i32, - 0, - gl::RED, - gl::UNSIGNED_BYTE, - rasterized.buf.as_ptr() as *const _ - ); - - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - - gl::BindTexture(gl::TEXTURE_2D, 0); - } - - Glyph { - tex_id: id, - top: rasterized.top as i32, - width: rasterized.width as i32, - height: rasterized.height as i32, - left: rasterized.left as i32, - } - } -} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs new file mode 100644 index 00000000..baf7c5aa --- /dev/null +++ b/src/renderer/mod.rs @@ -0,0 +1,295 @@ +use std::ffi::CString; +use std::mem::size_of; +use std::ptr; + +use gl; +use cgmath; + +use euclid::{Rect, Size2D, Point2D}; + +use gl::types::*; + +use cgmath::Matrix; + +use text::RasterizedGlyph; + +static TEXT_SHADER_V: &'static str = include_str!("../../res/text.v.glsl"); +static TEXT_SHADER_F: &'static str = include_str!("../../res/text.f.glsl"); + +pub struct QuadRenderer { + program: ShaderProgram, + vao: GLuint, + vbo: GLuint, + ebo: GLuint, +} + +impl QuadRenderer { + // TODO should probably hand this a transform instead of width/height + pub fn new(width: u32, height: u32) -> QuadRenderer { + let program = ShaderProgram::new(width, height); + + let mut vao: GLuint = 0; + let mut vbo: GLuint = 0; + let mut ebo: 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, + (size_of::() * 4 * 4) as GLsizeiptr, + ptr::null(), + gl::DYNAMIC_DRAW + ); + + let indices: [u32; 6] = [0, 1, 3, + 1, 2, 3]; + + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); + gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, + 6 * size_of::() as isize, + indices.as_ptr() as *const _, + gl::STATIC_DRAW); + + gl::EnableVertexAttribArray(0); + gl::VertexAttribPointer(0, 4, gl::FLOAT, gl::FALSE, 4 * size_of::() as i32, + ptr::null()); + + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0); + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + gl::BindVertexArray(0); + } + + QuadRenderer { + program: program, + vao: vao, + vbo: vbo, + ebo: ebo, + } + } + + pub fn render(&self, glyph: &Glyph, x: f32, y: f32) { + self.program.activate(); + unsafe { + // set color + gl::Uniform3f(self.program.u_color, 1., 1., 0.5); + } + + let rect = get_rect(glyph, x, y); + + // top right of character + let vertices: [[f32; 4]; 4] = [ + [rect.max_x(), rect.max_y(), 1., 0.], // top-right + [rect.max_x(), rect.min_y(), 1., 1.], // bottom-right + [rect.min_x(), rect.min_y(), 0., 1.], // bottom-left + [rect.min_x(), rect.max_y(), 0., 0.], // top-left + ]; + + unsafe { + bind_mask_texture(glyph.tex_id); + gl::BindVertexArray(self.vao); + + gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); + gl::BufferSubData( + gl::ARRAY_BUFFER, + 0, + (4 * 4 * size_of::()) as isize, + vertices.as_ptr() as *const _ + ); + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); + + gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null()); + gl::BindVertexArray(0); + gl::BindTexture(gl::TEXTURE_2D, 0); + } + + self.program.deactivate(); + } +} + +fn get_rect(glyph: &Glyph, x: f32, y: f32) -> Rect { + Rect::new( + Point2D::new(x, y), + Size2D::new(glyph.width as f32, glyph.height as f32) + ) +} + +fn bind_mask_texture(id: u32) { + unsafe { + gl::ActiveTexture(gl::TEXTURE0); + gl::BindTexture(gl::TEXTURE_2D, id); + } +} + +pub struct ShaderProgram { + id: GLuint, + /// projection matrix uniform + u_projection: GLint, + /// color uniform + u_color: GLint, +} + +impl ShaderProgram { + pub fn activate(&self) { + unsafe { + gl::UseProgram(self.id); + } + } + + pub fn deactivate(&self) { + unsafe { + gl::UseProgram(0); + } + } + + pub fn new(width: u32, height: u32) -> ShaderProgram { + let vertex_shader = ShaderProgram::create_vertex_shader(); + let fragment_shader = ShaderProgram::create_fragment_shader(); + let program = ShaderProgram::create_program(vertex_shader, fragment_shader); + + unsafe { + gl::DeleteShader(vertex_shader); + gl::DeleteShader(fragment_shader); + } + + // get uniform locations + let projection_str = CString::new("projection").unwrap(); + let color_str = CString::new("textColor").unwrap(); + + let (projection, color) = unsafe { + ( + gl::GetUniformLocation(program, projection_str.as_ptr()), + gl::GetUniformLocation(program, color_str.as_ptr()) + ) + }; + + assert!(projection != gl::INVALID_VALUE as i32); + assert!(projection != gl::INVALID_OPERATION as i32); + assert!(color != gl::INVALID_VALUE as i32); + assert!(color != gl::INVALID_OPERATION as i32); + + let shader = ShaderProgram { + id: program, + u_projection: projection, + u_color: color, + }; + + // set projection uniform + let ortho = cgmath::ortho(0., width as f32, 0., height as f32, -1., 1.); + let projection: [[f32; 4]; 4] = ortho.into(); + + println!("width: {}, height: {}", width, height); + + shader.activate(); + unsafe { + gl::UniformMatrix4fv(shader.u_projection, + 1, gl::FALSE, projection.as_ptr() as *const _); + } + shader.deactivate(); + + shader + } + + fn create_program(vertex: GLuint, fragment: GLuint) -> GLuint { + + unsafe { + let program = gl::CreateProgram(); + gl::AttachShader(program, vertex); + gl::AttachShader(program, fragment); + gl::LinkProgram(program); + + let mut success: GLint = 0; + gl::GetProgramiv(program, gl::LINK_STATUS, &mut success); + + if success != (gl::TRUE as GLint) { + panic!("failed to link shader program"); + } + program + } + } + + fn create_fragment_shader() -> GLuint { + unsafe { + let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); + let fragment_source = CString::new(TEXT_SHADER_F).unwrap(); + gl::ShaderSource(fragment_shader, 1, &fragment_source.as_ptr(), ptr::null()); + gl::CompileShader(fragment_shader); + + let mut success: GLint = 0; + gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success); + + if success != (gl::TRUE as GLint) { + panic!("failed to compiler fragment shader"); + } + fragment_shader + } + } + + fn create_vertex_shader() -> GLuint { + unsafe { + let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); + let vertex_source = CString::new(TEXT_SHADER_V).unwrap(); + gl::ShaderSource(vertex_shader, 1, &vertex_source.as_ptr(), ptr::null()); + gl::CompileShader(vertex_shader); + + let mut success: GLint = 0; + gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success); + + if success != (gl::TRUE as GLint) { + panic!("failed to compiler vertex shader"); + } + vertex_shader + } + } +} + +#[allow(dead_code)] +pub struct Glyph { + tex_id: GLuint, + top: i32, + left: i32, + width: i32, + height: i32, +} + +impl Glyph { + pub fn new(rasterized: &RasterizedGlyph) -> Glyph { + let mut id: GLuint = 0; + unsafe { + gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1); + gl::GenTextures(1, &mut id); + gl::BindTexture(gl::TEXTURE_2D, id); + gl::TexImage2D( + gl::TEXTURE_2D, + 0, + gl::RED as i32, + rasterized.width as i32, + rasterized.height as i32, + 0, + gl::RED, + gl::UNSIGNED_BYTE, + rasterized.buf.as_ptr() as *const _ + ); + + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); + + gl::BindTexture(gl::TEXTURE_2D, 0); + } + + Glyph { + tex_id: id, + top: rasterized.top as i32, + width: rasterized.width as i32, + height: rasterized.height as i32, + left: rasterized.left as i32, + } + } +}