diff --git a/src/rendergl.rs b/src/rendergl.rs index 5ed3200..7ec053d 100755 --- a/src/rendergl.rs +++ b/src/rendergl.rs @@ -11,12 +11,12 @@ use color::Color; use layers::Layer; use layers; use scene::Scene; -use texturegl::{Flip, Linear, Nearest, NoFlip, VerticalFlip}; +use texturegl::{Linear, Nearest, VerticalFlip}; use texturegl::{Texture, TextureTarget2D, TextureTargetRectangle}; use tiling::Tile; use platform::surface::NativeCompositingGraphicsContext; -use geom::matrix::{Matrix4, ortho}; +use geom::matrix::{Matrix4, identity, ortho}; use geom::size::Size2D; use libc::c_int; use opengles::gl2::{ARRAY_BUFFER, BLEND, COLOR_BUFFER_BIT, COMPILE_STATUS, FRAGMENT_SHADER}; @@ -26,40 +26,24 @@ use opengles::gl2::{LINE_STRIP, TRIANGLE_STRIP, VERTEX_SHADER, GLenum, GLfloat, use opengles::gl2::{GLuint, active_texture, attach_shader, bind_buffer, bind_texture, blend_func}; use opengles::gl2::{buffer_data, create_program, clear, clear_color, compile_shader}; use opengles::gl2::{create_shader, draw_arrays, enable, enable_vertex_attrib_array, disable_vertex_attrib_array}; -use opengles::gl2::{gen_buffers, get_attrib_location, get_program_iv}; +use opengles::gl2::{gen_buffers, get_attrib_location, get_program_info_log, get_program_iv}; use opengles::gl2::{get_shader_info_log, get_shader_iv, get_uniform_location, line_width}; -use opengles::gl2::{link_program, shader_source, uniform_1i, uniform_2f, uniform_4f}; +use opengles::gl2::{link_program, shader_source, uniform_1i, uniform_4f}; use opengles::gl2::{uniform_matrix_4fv, use_program, vertex_attrib_pointer_f32, viewport}; +use std::fmt; use std::num::Zero; use std::rc::Rc; -static FRAGMENT_2D_SHADER_SOURCE: &'static str = " +static FRAGMENT_SHADER_SOURCE: &'static str = " #ifdef GL_ES precision mediump float; #endif varying vec2 vTextureCoord; - - uniform sampler2D uSampler; - - void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord); - } -"; - -#[cfg(not(target_os="android"))] -static FRAGMENT_RECTANGLE_SHADER_SOURCE: &'static str = " - #ifdef GL_ES - precision mediump float; - #endif - - varying vec2 vTextureCoord; - - uniform sampler2DRect uSampler; - uniform vec2 uSize; + uniform samplerType uSampler; void main(void) { - gl_FragColor = texture2DRect(uSampler, vTextureCoord * uSize); + gl_FragColor = samplerFunction(uSampler, vTextureCoord); } "; @@ -75,47 +59,33 @@ static SOLID_COLOR_FRAGMENT_SHADER_SOURCE: &'static str = " "; static VERTEX_SHADER_SOURCE: &'static str = " - attribute vec3 aVertexPosition; - attribute vec2 aTextureCoord; + attribute vec2 aVertexPosition; uniform mat4 uMVMatrix; uniform mat4 uPMatrix; + uniform mat4 uTextureSpaceTransform; varying vec2 vTextureCoord; void main(void) { - gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); - vTextureCoord = aTextureCoord; + gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 0.0, 1.0); + vTextureCoord = (uTextureSpaceTransform * vec4(aVertexPosition, 0., 1.)).xy; } "; -static TEXTURED_QUAD_VERTICES: [f32, ..12] = [ - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 1.0, 0.0, 0.0, - 1.0, 1.0, 0.0, -]; - -static TEXTURE_COORDINATES: [f32, ..8] = [ +static TEXTURED_QUAD_VERTICES: [f32, ..8] = [ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, ]; -static FLIPPED_TEXTURE_COORDINATES: [f32, ..8] = [ - 0.0, 1.0, +static LINE_QUAD_VERTICES: [f32, ..10] = [ 0.0, 0.0, + 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, -]; - -static LINE_QUAD_VERTICES: [f32, ..15] = [ - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, 0.0, 0.0, - 0.0, 0.0, 0.0, + 0.0, 0.0, ]; static TILE_DEBUG_BORDER_COLOR: Color = Color { r: 0., g: 1., b: 1., a: 1.0 }; @@ -123,168 +93,139 @@ static TILE_DEBUG_BORDER_THICKNESS: uint = 1; static LAYER_DEBUG_BORDER_COLOR: Color = Color { r: 1., g: 0.5, b: 0., a: 1.0 }; static LAYER_DEBUG_BORDER_THICKNESS: uint = 2; -pub fn load_shader(source_string: &str, shader_type: GLenum) -> GLuint { - let shader_id = create_shader(shader_type); - shader_source(shader_id, [ source_string.as_bytes() ]); - compile_shader(shader_id); - - if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) { - debug!("shader info log: {:s}", get_shader_info_log(shader_id)); - fail!("failed to compile shader"); - } - - return shader_id; -} - struct Buffers { textured_quad_vertex_buffer: GLuint, - texture_coordinate_buffer: GLuint, - flipped_texture_coordinate_buffer: GLuint, line_quad_vertex_buffer: GLuint, } -struct Texture2DProgram { +struct ShaderProgram { id: GLuint, - vertex_position_attr: c_int, - texture_coord_attr: c_int, - modelview_uniform: c_int, - projection_uniform: c_int, - sampler_uniform: c_int, } -impl Texture2DProgram { - fn new() -> Texture2DProgram { - let vertex_shader = load_shader(VERTEX_SHADER_SOURCE, VERTEX_SHADER); - let fragment_shader = load_shader(FRAGMENT_2D_SHADER_SOURCE, FRAGMENT_SHADER); - let program_id = init_program(vertex_shader, fragment_shader); - - Texture2DProgram { - id: program_id, - vertex_position_attr: get_attrib_location(program_id, "aVertexPosition"), - texture_coord_attr: get_attrib_location(program_id, "aTextureCoord"), - modelview_uniform: get_uniform_location(program_id, "uMVMatrix"), - projection_uniform: get_uniform_location(program_id, "uPMatrix"), - sampler_uniform: get_uniform_location(program_id, "uSampler"), +impl ShaderProgram { + pub fn new(vertex_shader_source: &str, fragment_shader_source: &str) -> ShaderProgram { + let id = create_program(); + attach_shader(id, ShaderProgram::compile_shader(fragment_shader_source, FRAGMENT_SHADER)); + attach_shader(id, ShaderProgram::compile_shader(vertex_shader_source, VERTEX_SHADER)); + link_program(id); + if get_program_iv(id, LINK_STATUS) == (0 as GLint) { + fail!("Failed to compile shader program: {:s}", get_program_info_log(id)); } - } - fn bind_uniforms_and_attributes(&self, - texture: &Texture, - transform: &Matrix4, - projection_matrix: &Matrix4, - buffers: &Buffers) { - uniform_1i(self.sampler_uniform, 0); - uniform_matrix_4fv(self.modelview_uniform, false, transform.to_array()); - uniform_matrix_4fv(self.projection_uniform, false, projection_matrix.to_array()); + ShaderProgram { + id: id, + } + } - bind_buffer(ARRAY_BUFFER, buffers.textured_quad_vertex_buffer); - vertex_attrib_pointer_f32(self.vertex_position_attr as GLuint, 3, false, 0, 0); + pub fn compile_shader(source_string: &str, shader_type: GLenum) -> GLuint { + let id = create_shader(shader_type); + shader_source(id, [ source_string.as_bytes() ]); + compile_shader(id); + if get_shader_iv(id, COMPILE_STATUS) == (0 as GLint) { + fail!("Failed to compile shader: {:s}", get_shader_info_log(id)); + } - bind_texture_coordinate_buffer(buffers, texture.flip); - vertex_attrib_pointer_f32(self.texture_coord_attr as GLuint, 2, false, 0, 0); + return id; } - fn enable_attribute_arrays(&self) { - enable_vertex_attrib_array(self.vertex_position_attr as GLuint); - enable_vertex_attrib_array(self.texture_coord_attr as GLuint); + pub fn get_attribute_location(&self, name: &str) -> GLint { + get_attrib_location(self.id, name) } - fn disable_attribute_arrays(&self) { - disable_vertex_attrib_array(self.vertex_position_attr as GLuint); - disable_vertex_attrib_array(self.texture_coord_attr as GLuint); + pub fn get_uniform_location(&self, name: &str) -> GLint { + get_uniform_location(self.id, name) } } -struct TextureRectangleProgram { - id: GLuint, +struct TextureProgram { + program: ShaderProgram, vertex_position_attr: c_int, - texture_coord_attr: c_int, modelview_uniform: c_int, projection_uniform: c_int, sampler_uniform: c_int, - size_uniform: c_int, + texture_space_transform_uniform: c_int, } -impl TextureRectangleProgram { - #[cfg(not(target_os="android"))] - fn new() -> TextureRectangleProgram { - let vertex_shader = load_shader(VERTEX_SHADER_SOURCE, VERTEX_SHADER); - let fragment_shader = load_shader(FRAGMENT_RECTANGLE_SHADER_SOURCE, FRAGMENT_SHADER); - let program_id = init_program(vertex_shader, fragment_shader); - - TextureRectangleProgram { - id: program_id, - vertex_position_attr: get_attrib_location(program_id, "aVertexPosition"), - texture_coord_attr: get_attrib_location(program_id, "aTextureCoord"), - modelview_uniform: get_uniform_location(program_id, "uMVMatrix"), - projection_uniform: get_uniform_location(program_id, "uPMatrix"), - sampler_uniform: get_uniform_location(program_id, "uSampler"), - size_uniform: get_uniform_location(program_id, "uSize"), +impl TextureProgram { + fn new(sampler_function: &str, sampler_type: &str) -> TextureProgram { + let fragment_shader_source + = format_args!(fmt::format, + "#define samplerFunction {}\n#define samplerType {}\n{}", + sampler_function, + sampler_type, + FRAGMENT_SHADER_SOURCE); + let program = ShaderProgram::new(VERTEX_SHADER_SOURCE, fragment_shader_source.as_slice()); + TextureProgram { + program: program, + vertex_position_attr: program.get_attribute_location("aVertexPosition"), + modelview_uniform: program.get_uniform_location("uMVMatrix"), + projection_uniform: program.get_uniform_location("uPMatrix"), + sampler_uniform: program.get_uniform_location("uSampler"), + texture_space_transform_uniform: program.get_uniform_location("uTextureSpaceTransform"), } } - #[cfg(not(target_os="android"))] - fn create_if_necessary() -> Option { - use opengles::gl2::TEXTURE_RECTANGLE_ARB; - enable(TEXTURE_RECTANGLE_ARB); - Some(TextureRectangleProgram::new()) - } - - #[cfg(target_os="android")] - fn create_if_necessary() -> Option { - None - } - fn bind_uniforms_and_attributes(&self, - texture: &Texture, transform: &Matrix4, projection_matrix: &Matrix4, + texture_space_transform: &Matrix4, buffers: &Buffers) { uniform_1i(self.sampler_uniform, 0); - uniform_2f(self.size_uniform, - texture.size.width as GLfloat, - texture.size.height as GLfloat); uniform_matrix_4fv(self.modelview_uniform, false, transform.to_array()); uniform_matrix_4fv(self.projection_uniform, false, projection_matrix.to_array()); bind_buffer(ARRAY_BUFFER, buffers.textured_quad_vertex_buffer); - vertex_attrib_pointer_f32(self.vertex_position_attr as GLuint, 3, false, 0, 0); + vertex_attrib_pointer_f32(self.vertex_position_attr as GLuint, 2, false, 0, 0); - bind_texture_coordinate_buffer(buffers, texture.flip); - vertex_attrib_pointer_f32(self.texture_coord_attr as GLuint, 2, false, 0, 0); + uniform_matrix_4fv(self.texture_space_transform_uniform, + false, + texture_space_transform.to_array()); } fn enable_attribute_arrays(&self) { enable_vertex_attrib_array(self.vertex_position_attr as GLuint); - enable_vertex_attrib_array(self.texture_coord_attr as GLuint); } fn disable_attribute_arrays(&self) { disable_vertex_attrib_array(self.vertex_position_attr as GLuint); - disable_vertex_attrib_array(self.texture_coord_attr as GLuint); + } + + fn create_2d_program() -> TextureProgram { + TextureProgram::new("texture2D", "sampler2D") + } + + #[cfg(not(target_os="android"))] + fn create_rectangle_program_if_necessary() -> Option { + use opengles::gl2::TEXTURE_RECTANGLE_ARB; + enable(TEXTURE_RECTANGLE_ARB); + Some(TextureProgram::new("texture2DRect", "sampler2DRect")) + } + + #[cfg(target_os="android")] + fn create_rectangle_program_if_necessary() -> Option { + None } } struct SolidLineProgram { - id: GLuint, + program: ShaderProgram, vertex_position_attr: c_int, modelview_uniform: c_int, projection_uniform: c_int, color_uniform: c_int, + texture_space_transform_uniform: c_int, } impl SolidLineProgram { fn new() -> SolidLineProgram { - let vertex_shader = load_shader(VERTEX_SHADER_SOURCE, VERTEX_SHADER); - let fragment_shader = load_shader(SOLID_COLOR_FRAGMENT_SHADER_SOURCE, FRAGMENT_SHADER); - let program_id = init_program(vertex_shader, fragment_shader); - + let program = ShaderProgram::new(VERTEX_SHADER_SOURCE, SOLID_COLOR_FRAGMENT_SHADER_SOURCE); SolidLineProgram { - id: program_id, - vertex_position_attr: get_attrib_location(program_id, "aVertexPosition"), - modelview_uniform: get_uniform_location(program_id, "uMVMatrix"), - projection_uniform: get_uniform_location(program_id, "uPMatrix"), - color_uniform: get_uniform_location(program_id, "uColor"), + program: program, + vertex_position_attr: program.get_attribute_location("aVertexPosition"), + modelview_uniform: program.get_uniform_location("uMVMatrix"), + projection_uniform: program.get_uniform_location("uPMatrix"), + color_uniform: program.get_uniform_location("uColor"), + texture_space_transform_uniform: program.get_uniform_location("uTextureSpaceTransform"), } } @@ -302,7 +243,12 @@ impl SolidLineProgram { color.a as GLfloat); bind_buffer(ARRAY_BUFFER, buffers.line_quad_vertex_buffer); - vertex_attrib_pointer_f32(self.vertex_position_attr as GLuint, 3, false, 0, 0); + vertex_attrib_pointer_f32(self.vertex_position_attr as GLuint, 2, false, 0, 0); + + let texture_transform: Matrix4 = identity(); + uniform_matrix_4fv(self.texture_space_transform_uniform, + false, + texture_transform.to_array()); } fn enable_attribute_arrays(&self) { @@ -315,8 +261,8 @@ impl SolidLineProgram { } pub struct RenderContext { - texture_2d_program: Texture2DProgram, - texture_rectangle_program: Option, + texture_2d_program: TextureProgram, + texture_rectangle_program: Option, solid_line_program: SolidLineProgram, buffers: Buffers, @@ -334,9 +280,9 @@ impl RenderContext { enable(BLEND); blend_func(SRC_ALPHA, ONE_MINUS_SRC_ALPHA); - let texture_2d_program = Texture2DProgram::new(); + let texture_2d_program = TextureProgram::create_2d_program(); let solid_line_program = SolidLineProgram::new(); - let texture_rectangle_program = TextureRectangleProgram::create_if_necessary(); + let texture_rectangle_program = TextureProgram::create_rectangle_program_if_necessary(); RenderContext { texture_2d_program: texture_2d_program, @@ -357,63 +303,31 @@ impl RenderContext { bind_buffer(ARRAY_BUFFER, line_quad_vertex_buffer); buffer_data(ARRAY_BUFFER, LINE_QUAD_VERTICES, STATIC_DRAW); - let texture_coordinate_buffer = gen_buffers(1)[0]; - bind_buffer(ARRAY_BUFFER, texture_coordinate_buffer); - buffer_data(ARRAY_BUFFER, TEXTURE_COORDINATES, STATIC_DRAW); - - let flipped_texture_coordinate_buffer = gen_buffers(1)[0]; - bind_buffer(ARRAY_BUFFER, flipped_texture_coordinate_buffer); - buffer_data(ARRAY_BUFFER, FLIPPED_TEXTURE_COORDINATES, STATIC_DRAW); - Buffers { textured_quad_vertex_buffer: textured_quad_vertex_buffer, - texture_coordinate_buffer: texture_coordinate_buffer, - flipped_texture_coordinate_buffer: flipped_texture_coordinate_buffer, line_quad_vertex_buffer: line_quad_vertex_buffer, } } } -pub fn init_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint { - let program = create_program(); - attach_shader(program, vertex_shader); - attach_shader(program, fragment_shader); - link_program(program); - if get_program_iv(program, LINK_STATUS) == (0 as GLint) { - fail!("failed to initialize program"); - } - - program -} - -fn bind_texture_coordinate_buffer(buffers: &Buffers, flip: Flip) { - match flip { - NoFlip => bind_buffer(ARRAY_BUFFER, buffers.texture_coordinate_buffer), - VerticalFlip => { - bind_buffer(ARRAY_BUFFER, buffers.flipped_texture_coordinate_buffer) - } - } -} - pub fn bind_and_render_quad(render_context: RenderContext, texture: &Texture, transform: &Matrix4, scene_size: Size2D) { - let program_id = match texture.target { - TextureTarget2D => { - render_context.texture_2d_program.enable_attribute_arrays(); - render_context.texture_2d_program.id - } + let mut texture_coordinates_need_to_be_scaled_by_size = false; + let program = match texture.target { + TextureTarget2D => render_context.texture_2d_program, TextureTargetRectangle(..) => match render_context.texture_rectangle_program { Some(program) => { - program.enable_attribute_arrays(); - program.id + texture_coordinates_need_to_be_scaled_by_size = true; + program } None => fail!("There is no shader program for texture rectangle"), }, }; + program.enable_attribute_arrays(); - use_program(program_id); + use_program(program.program.id); active_texture(TEXTURE0); // FIXME: This should technically check that the transform @@ -432,35 +346,33 @@ pub fn bind_and_render_quad(render_context: RenderContext, // Set the projection matrix. let projection_matrix = ortho(0.0, scene_size.width, scene_size.height, 0.0, -10.0, 10.0); - // Set uniforms and vertex attribute pointers. - match texture.target { - TextureTarget2D => { - render_context.texture_2d_program. - bind_uniforms_and_attributes(texture, - transform, - &projection_matrix, - &render_context.buffers); - } - TextureTargetRectangle => { - render_context.texture_rectangle_program.unwrap(). - bind_uniforms_and_attributes(texture, - transform, - &projection_matrix, - &render_context.buffers); - } + // We calculate a transformation matrix for the texture coordinates + // which is useful for flipping the texture vertically or scaling the + // coordinates when dealing with GL_ARB_texture_rectangle. + let mut texture_transform: Matrix4 = identity(); + if texture.flip == VerticalFlip { + texture_transform = texture_transform.scale(1.0, -1.0, 1.0); + } + if texture_coordinates_need_to_be_scaled_by_size { + texture_transform = texture_transform.scale(texture.size.width as f32, + texture.size.height as f32, + 1.0); + } + if texture.flip == VerticalFlip { + texture_transform = texture_transform.translate(0.0, -1.0, 0.0); } + program.bind_uniforms_and_attributes(transform, + &projection_matrix, + &texture_transform, + &render_context.buffers); + + // Draw! draw_arrays(TRIANGLE_STRIP, 0, 4); bind_texture(TEXTURE_2D, 0); - match texture.target { - TextureTarget2D => render_context.texture_2d_program.disable_attribute_arrays(), - TextureTargetRectangle(..) => match render_context.texture_rectangle_program { - Some(program) => program.disable_attribute_arrays(), - None => {}, - }, - }; + program.disable_attribute_arrays() } pub fn bind_and_render_quad_lines(render_context: RenderContext, @@ -470,7 +382,7 @@ pub fn bind_and_render_quad_lines(render_context: RenderContext, line_thickness: uint) { let solid_line_program = render_context.solid_line_program; solid_line_program.enable_attribute_arrays(); - use_program(solid_line_program.id); + use_program(solid_line_program.program.id); let projection_matrix = ortho(0.0, scene_size.width, scene_size.height, 0.0, -10.0, 10.0); solid_line_program.bind_uniforms_and_attributes(transform, &projection_matrix,