rust/lib-hedgewars-engine/src/render/map.rs
changeset 14702 29dbe9ce8b7d
child 14705 19122a329774
--- /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<MapTile>,
+    textures: Vec<Texture2D>,
+
+    tile_vertex_buffer: Buffer,
+    tile_index_buffer: Buffer,
+    tile_vertices: Vec<TileVertex>,
+    tile_indices: Vec<u16>,
+    tile_draw_calls: Vec<DrawTile>,
+    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<u32>) {
+        // 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<u32>, 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;
+        }
+    }
+}
+
+