rust/lib-hedgewars-engine/src/render/map.rs
changeset 14702 29dbe9ce8b7d
child 14705 19122a329774
equal deleted inserted replaced
14701:5e2c892b0222 14702:29dbe9ce8b7d
       
     1 use integral_geometry::{Point, Rect, Size};
       
     2 use land2d::{Land2D};
       
     3 use vec2d::{Vec2D};
       
     4 
       
     5 use super::gl::{
       
     6     Texture2D,
       
     7     Buffer,
       
     8     Shader,
       
     9     InputLayout,
       
    10     VariableBinding,
       
    11     InputElement,
       
    12     InputFormat,
       
    13 };
       
    14 
       
    15 // TODO: temp
       
    16 const VERTEX_SHADER: &'static str = r#"
       
    17 #version 150
       
    18 
       
    19 in vec2 Position;
       
    20 in vec3 Uv;
       
    21 
       
    22 out vec3 a_Uv;
       
    23 
       
    24 //uniform Common {
       
    25 uniform mat4 Projection;
       
    26 //};
       
    27 
       
    28 void main()
       
    29 {
       
    30 	a_Uv = Uv;
       
    31 	gl_Position = Projection * vec4(Position, 0.0, 1.0);
       
    32 }
       
    33 "#;
       
    34 
       
    35 const PIXEL_SHADER: &'static str = r#"
       
    36 #version 150
       
    37 
       
    38 in vec3 a_Uv;
       
    39 
       
    40 uniform sampler2D Texture;
       
    41 
       
    42 out vec4 Target;
       
    43 
       
    44 void main()
       
    45 {
       
    46 	 Target = texture2D(Texture, a_Uv.xy);
       
    47 }
       
    48 "#;
       
    49 
       
    50 pub struct MapTile {
       
    51     // either index into GL texture array or emulated [Texture; N]
       
    52     texture_index: u32,
       
    53 
       
    54     width: u32,
       
    55     height: u32,
       
    56 }
       
    57 
       
    58 #[repr(C)]
       
    59 #[derive(Copy, Clone)]
       
    60 pub struct TileVertex {
       
    61     pos: [f32; 2],
       
    62     // doesn't hurt to include another float, just in case..
       
    63     uv: [f32; 3],
       
    64 }
       
    65 
       
    66 pub struct DrawTile {
       
    67     texture_index: u32,
       
    68     index_len: u32,
       
    69 }
       
    70 
       
    71 pub struct MapRenderer {
       
    72     tiles: Vec<MapTile>,
       
    73     textures: Vec<Texture2D>,
       
    74 
       
    75     tile_vertex_buffer: Buffer,
       
    76     tile_index_buffer: Buffer,
       
    77     tile_vertices: Vec<TileVertex>,
       
    78     tile_indices: Vec<u16>,
       
    79     tile_draw_calls: Vec<DrawTile>,
       
    80     index_offset: u16,
       
    81     tile_shader: Shader,
       
    82     tile_layout: InputLayout,
       
    83     
       
    84     tile_width: u32,
       
    85     tile_height: u32,
       
    86     num_tile_x: i32,
       
    87 }
       
    88 
       
    89 impl MapRenderer {
       
    90     pub fn new(tile_width: u32, tile_height: u32) -> Self {
       
    91         let tile_shader = Shader::new(
       
    92             VERTEX_SHADER,
       
    93             Some(PIXEL_SHADER),
       
    94             &[
       
    95                 VariableBinding::Attribute("Position", 0),
       
    96                 VariableBinding::Attribute("Uv", 1),
       
    97                 VariableBinding::Sampler("Texture", 0),
       
    98             ]
       
    99         ).unwrap();
       
   100 
       
   101         let tile_layout = InputLayout::new(vec![
       
   102             // position
       
   103             InputElement {
       
   104                 shader_slot: 0,
       
   105                 buffer_slot: 0,
       
   106                 format: InputFormat::Float(gl::FLOAT, false),
       
   107                 components: 2,
       
   108                 stride: 20,
       
   109                 offset: 0
       
   110             },
       
   111             // uv
       
   112             InputElement {
       
   113                 shader_slot: 1,
       
   114                 buffer_slot: 0,
       
   115                 format: InputFormat::Float(gl::FLOAT, false),
       
   116                 components: 3,
       
   117                 stride: 20,
       
   118                 offset: 8
       
   119             },
       
   120         ]);
       
   121         
       
   122         MapRenderer {
       
   123             tiles: Vec::new(),
       
   124             textures: Vec::new(),
       
   125             
       
   126             tile_vertex_buffer: Buffer::empty(gl::ARRAY_BUFFER, gl::DYNAMIC_DRAW),
       
   127             tile_index_buffer: Buffer::empty(gl::ELEMENT_ARRAY_BUFFER, gl::DYNAMIC_DRAW),
       
   128             tile_vertices: Vec::new(),
       
   129             tile_indices: Vec::new(),
       
   130             index_offset: 0,
       
   131 
       
   132             tile_draw_calls: Vec::new(),
       
   133             tile_shader,
       
   134             tile_layout,
       
   135 
       
   136             tile_width,
       
   137             tile_height,
       
   138             num_tile_x: 0,
       
   139         }
       
   140     }
       
   141 
       
   142     pub fn init(&mut self, land: &Vec2D<u32>) {
       
   143         // clear tiles, but keep our textures for potential re-use
       
   144         self.tiles.clear();
       
   145 
       
   146         let tw = self.tile_width as usize;
       
   147         let th = self.tile_height as usize;
       
   148         let lw = land.width();
       
   149         let lh = land.height();
       
   150         let num_tile_x = lw / tw + if lw % tw != 0 { 1 } else { 0 };
       
   151         let num_tile_y = lh / th + if lh % th != 0 { 1 } else { 0 };
       
   152 
       
   153         self.num_tile_x = num_tile_x as i32;
       
   154 
       
   155         for y in 0..num_tile_y {
       
   156             for x in 0..num_tile_x {
       
   157                 let idx = x + y * num_tile_x;
       
   158 
       
   159                 let (data, stride) = {
       
   160                     let bpp = 4;
       
   161 
       
   162                     let offset = x * tw * bpp + y * th * lw * bpp;
       
   163 
       
   164                     let data = unsafe { &land.as_bytes()[offset..] };
       
   165                     let stride = land.width();
       
   166 
       
   167                     (data, stride as u32)
       
   168                 };
       
   169                 
       
   170                 let texture_index = if idx >= self.textures.len() {
       
   171                     let texture = Texture2D::with_data(
       
   172                         data,
       
   173                         stride,
       
   174                         self.tile_width,
       
   175                         self.tile_height,
       
   176                         gl::RGBA8,
       
   177                         gl::RGBA,
       
   178                         gl::UNSIGNED_BYTE,
       
   179                         gl::NEAREST
       
   180                     );
       
   181 
       
   182                     let texture_index = self.textures.len();
       
   183                     self.textures.push(texture);
       
   184 
       
   185                     texture_index
       
   186                 } else {
       
   187                     let texture_region = Rect::new(
       
   188                         Point::new(0, 0),
       
   189                         Point::new(self.tile_width as i32, self.tile_height as i32)
       
   190                     );
       
   191 
       
   192                     self.textures[idx].update(texture_region, data, stride, gl::RGBA, gl::UNSIGNED_BYTE);
       
   193                     idx
       
   194                 };
       
   195 
       
   196                 let tile = MapTile {
       
   197                     texture_index: texture_index as u32,
       
   198                     
       
   199                     // TODO: are there ever non-power of two textures?
       
   200                     width: self.tile_width,
       
   201                     height: self.tile_height,
       
   202                 };
       
   203                 self.tiles.push(tile);
       
   204             }
       
   205         }
       
   206     }
       
   207 
       
   208     pub fn update(&mut self, land: &Land2D<u32>, region: Rect) {
       
   209 
       
   210     }
       
   211 
       
   212     pub fn render(&mut self, viewport: Rect) {
       
   213         self.tile_vertices.clear();
       
   214         self.tile_indices.clear();
       
   215         self.tile_draw_calls.clear();
       
   216         self.index_offset = 0;
       
   217         
       
   218         for (idx, tile) in self.tiles.iter().enumerate() {
       
   219             let tile_x = idx as i32 % self.num_tile_x;
       
   220             let tile_y = idx as i32 / self.num_tile_x;
       
   221             let tile_w = self.tile_width as i32;
       
   222             let tile_h = self.tile_height as i32;
       
   223 
       
   224             let origin = Point::new(tile_x * tile_w, tile_y * tile_h);
       
   225             let tile_rect = Rect::new(origin, origin + Point::new(tile_w, tile_h));
       
   226 
       
   227             if viewport.intersects(&tile_rect) {
       
   228                 // lazy
       
   229                 //dbg!(origin);
       
   230                 let tile_x = origin.x as f32;
       
   231                 let tile_y = origin.y as f32;
       
   232                 let tile_w = tile_x + tile_w as f32;
       
   233                 let tile_h = tile_y + tile_h as f32;
       
   234                 let uv_depth = tile.texture_index as f32;
       
   235 
       
   236                 //dbg!(tile_x);
       
   237                 let tl = TileVertex { pos: [tile_x, tile_y], uv: [0f32, 0f32, uv_depth] };
       
   238                 let bl = TileVertex { pos: [tile_x, tile_h], uv: [0f32, 1f32, uv_depth] };
       
   239                 let br = TileVertex { pos: [tile_w, tile_h], uv: [1f32, 1f32, uv_depth] };
       
   240                 let tr = TileVertex { pos: [tile_w, tile_y], uv: [1f32, 0f32, uv_depth] };
       
   241 
       
   242                 self.tile_vertices.extend(&[tl, bl, br, tr]);
       
   243 
       
   244                 let i = self.index_offset;
       
   245                 self.tile_indices.extend(&[
       
   246                     i + 0, i + 1, i + 2,
       
   247                     i + 2, i + 3, i + 0,
       
   248                 ]);
       
   249                 self.index_offset += 4;
       
   250 
       
   251                 self.tile_draw_calls.push(DrawTile {
       
   252                     texture_index: tile.texture_index,
       
   253                     index_len: 6
       
   254                 });
       
   255             }
       
   256         }
       
   257 
       
   258         self.tile_vertex_buffer.write_typed(&self.tile_vertices);
       
   259         self.tile_index_buffer.write_typed(&self.tile_indices);
       
   260 
       
   261         let _g = self.tile_layout.bind(&[
       
   262             (0, &self.tile_vertex_buffer)
       
   263         ], Some(&self.tile_index_buffer));
       
   264 
       
   265         let ortho = {
       
   266             let l = viewport.left() as f32;
       
   267             let r = viewport.right() as f32;
       
   268             let b = viewport.bottom() as f32;
       
   269             let t = viewport.top() as f32;
       
   270 
       
   271             [
       
   272                 2f32 / (r - l),    0f32,              0f32,   0f32,
       
   273                 0f32,              2f32 / (t - b),    0f32,   0f32,
       
   274                 0f32,              0f32,              0.5f32, 0f32,
       
   275                 (r + l) / (l - r), (t + b) / (b - t), 0.5f32, 1f32,
       
   276             ]
       
   277         };
       
   278 
       
   279         self.tile_shader.bind();
       
   280         self.tile_shader.set_matrix("Projection", ortho.as_ptr());
       
   281         
       
   282         let mut draw_offset = 0;
       
   283         for draw_call in &self.tile_draw_calls {
       
   284             unsafe {
       
   285                 self.tile_shader.bind_texture_2d(0, &self.textures[draw_call.texture_index as usize]);
       
   286                 
       
   287                 gl::DrawElements(
       
   288                     gl::TRIANGLES,
       
   289                     draw_call.index_len as i32,
       
   290                     gl::UNSIGNED_SHORT,
       
   291                     draw_offset as *const _
       
   292                 );
       
   293             }
       
   294 
       
   295             draw_offset += draw_call.index_len * 2;
       
   296         }
       
   297     }
       
   298 }
       
   299 
       
   300