diff -r 5e2c892b0222 -r 29dbe9ce8b7d rust/lib-hedgewars-engine/src/render/map.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hedgewars-engine/src/render/map.rs Fri Mar 22 18:01:08 2019 +0200 @@ -0,0 +1,300 @@ +use integral_geometry::{Point, Rect, Size}; +use land2d::{Land2D}; +use vec2d::{Vec2D}; + +use super::gl::{ + Texture2D, + Buffer, + Shader, + InputLayout, + VariableBinding, + InputElement, + InputFormat, +}; + +// TODO: temp +const VERTEX_SHADER: &'static str = r#" +#version 150 + +in vec2 Position; +in vec3 Uv; + +out vec3 a_Uv; + +//uniform Common { +uniform mat4 Projection; +//}; + +void main() +{ + a_Uv = Uv; + gl_Position = Projection * vec4(Position, 0.0, 1.0); +} +"#; + +const PIXEL_SHADER: &'static str = r#" +#version 150 + +in vec3 a_Uv; + +uniform sampler2D Texture; + +out vec4 Target; + +void main() +{ + Target = texture2D(Texture, a_Uv.xy); +} +"#; + +pub struct MapTile { + // either index into GL texture array or emulated [Texture; N] + texture_index: u32, + + width: u32, + height: u32, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct TileVertex { + pos: [f32; 2], + // doesn't hurt to include another float, just in case.. + uv: [f32; 3], +} + +pub struct DrawTile { + texture_index: u32, + index_len: u32, +} + +pub struct MapRenderer { + tiles: Vec, + textures: Vec, + + tile_vertex_buffer: Buffer, + tile_index_buffer: Buffer, + tile_vertices: Vec, + tile_indices: Vec, + tile_draw_calls: Vec, + index_offset: u16, + tile_shader: Shader, + tile_layout: InputLayout, + + tile_width: u32, + tile_height: u32, + num_tile_x: i32, +} + +impl MapRenderer { + pub fn new(tile_width: u32, tile_height: u32) -> Self { + let tile_shader = Shader::new( + VERTEX_SHADER, + Some(PIXEL_SHADER), + &[ + VariableBinding::Attribute("Position", 0), + VariableBinding::Attribute("Uv", 1), + VariableBinding::Sampler("Texture", 0), + ] + ).unwrap(); + + let tile_layout = InputLayout::new(vec![ + // position + InputElement { + shader_slot: 0, + buffer_slot: 0, + format: InputFormat::Float(gl::FLOAT, false), + components: 2, + stride: 20, + offset: 0 + }, + // uv + InputElement { + shader_slot: 1, + buffer_slot: 0, + format: InputFormat::Float(gl::FLOAT, false), + components: 3, + stride: 20, + offset: 8 + }, + ]); + + MapRenderer { + tiles: Vec::new(), + textures: Vec::new(), + + tile_vertex_buffer: Buffer::empty(gl::ARRAY_BUFFER, gl::DYNAMIC_DRAW), + tile_index_buffer: Buffer::empty(gl::ELEMENT_ARRAY_BUFFER, gl::DYNAMIC_DRAW), + tile_vertices: Vec::new(), + tile_indices: Vec::new(), + index_offset: 0, + + tile_draw_calls: Vec::new(), + tile_shader, + tile_layout, + + tile_width, + tile_height, + num_tile_x: 0, + } + } + + pub fn init(&mut self, land: &Vec2D) { + // clear tiles, but keep our textures for potential re-use + self.tiles.clear(); + + let tw = self.tile_width as usize; + let th = self.tile_height as usize; + let lw = land.width(); + let lh = land.height(); + let num_tile_x = lw / tw + if lw % tw != 0 { 1 } else { 0 }; + let num_tile_y = lh / th + if lh % th != 0 { 1 } else { 0 }; + + self.num_tile_x = num_tile_x as i32; + + for y in 0..num_tile_y { + for x in 0..num_tile_x { + let idx = x + y * num_tile_x; + + let (data, stride) = { + let bpp = 4; + + let offset = x * tw * bpp + y * th * lw * bpp; + + let data = unsafe { &land.as_bytes()[offset..] }; + let stride = land.width(); + + (data, stride as u32) + }; + + let texture_index = if idx >= self.textures.len() { + let texture = Texture2D::with_data( + data, + stride, + self.tile_width, + self.tile_height, + gl::RGBA8, + gl::RGBA, + gl::UNSIGNED_BYTE, + gl::NEAREST + ); + + let texture_index = self.textures.len(); + self.textures.push(texture); + + texture_index + } else { + let texture_region = Rect::new( + Point::new(0, 0), + Point::new(self.tile_width as i32, self.tile_height as i32) + ); + + self.textures[idx].update(texture_region, data, stride, gl::RGBA, gl::UNSIGNED_BYTE); + idx + }; + + let tile = MapTile { + texture_index: texture_index as u32, + + // TODO: are there ever non-power of two textures? + width: self.tile_width, + height: self.tile_height, + }; + self.tiles.push(tile); + } + } + } + + pub fn update(&mut self, land: &Land2D, region: Rect) { + + } + + pub fn render(&mut self, viewport: Rect) { + self.tile_vertices.clear(); + self.tile_indices.clear(); + self.tile_draw_calls.clear(); + self.index_offset = 0; + + for (idx, tile) in self.tiles.iter().enumerate() { + let tile_x = idx as i32 % self.num_tile_x; + let tile_y = idx as i32 / self.num_tile_x; + let tile_w = self.tile_width as i32; + let tile_h = self.tile_height as i32; + + let origin = Point::new(tile_x * tile_w, tile_y * tile_h); + let tile_rect = Rect::new(origin, origin + Point::new(tile_w, tile_h)); + + if viewport.intersects(&tile_rect) { + // lazy + //dbg!(origin); + let tile_x = origin.x as f32; + let tile_y = origin.y as f32; + let tile_w = tile_x + tile_w as f32; + let tile_h = tile_y + tile_h as f32; + let uv_depth = tile.texture_index as f32; + + //dbg!(tile_x); + let tl = TileVertex { pos: [tile_x, tile_y], uv: [0f32, 0f32, uv_depth] }; + let bl = TileVertex { pos: [tile_x, tile_h], uv: [0f32, 1f32, uv_depth] }; + let br = TileVertex { pos: [tile_w, tile_h], uv: [1f32, 1f32, uv_depth] }; + let tr = TileVertex { pos: [tile_w, tile_y], uv: [1f32, 0f32, uv_depth] }; + + self.tile_vertices.extend(&[tl, bl, br, tr]); + + let i = self.index_offset; + self.tile_indices.extend(&[ + i + 0, i + 1, i + 2, + i + 2, i + 3, i + 0, + ]); + self.index_offset += 4; + + self.tile_draw_calls.push(DrawTile { + texture_index: tile.texture_index, + index_len: 6 + }); + } + } + + self.tile_vertex_buffer.write_typed(&self.tile_vertices); + self.tile_index_buffer.write_typed(&self.tile_indices); + + let _g = self.tile_layout.bind(&[ + (0, &self.tile_vertex_buffer) + ], Some(&self.tile_index_buffer)); + + let ortho = { + let l = viewport.left() as f32; + let r = viewport.right() as f32; + let b = viewport.bottom() as f32; + let t = viewport.top() as f32; + + [ + 2f32 / (r - l), 0f32, 0f32, 0f32, + 0f32, 2f32 / (t - b), 0f32, 0f32, + 0f32, 0f32, 0.5f32, 0f32, + (r + l) / (l - r), (t + b) / (b - t), 0.5f32, 1f32, + ] + }; + + self.tile_shader.bind(); + self.tile_shader.set_matrix("Projection", ortho.as_ptr()); + + let mut draw_offset = 0; + for draw_call in &self.tile_draw_calls { + unsafe { + self.tile_shader.bind_texture_2d(0, &self.textures[draw_call.texture_index as usize]); + + gl::DrawElements( + gl::TRIANGLES, + draw_call.index_len as i32, + gl::UNSIGNED_SHORT, + draw_offset as *const _ + ); + } + + draw_offset += draw_call.index_len * 2; + } + } +} + +