make world renderer optional + fmt
authoralfadur
Fri, 22 Mar 2019 20:26:29 +0300
changeset 14726 19122a329774
parent 14725 12db7e435ea6
child 14727 88b0efba77b9
make world renderer optional + fmt
rust/hwrunner/src/main.rs
rust/lib-hedgewars-engine/src/instance.rs
rust/lib-hedgewars-engine/src/lib.rs
rust/lib-hedgewars-engine/src/render.rs
rust/lib-hedgewars-engine/src/render/camera.rs
rust/lib-hedgewars-engine/src/render/gl.rs
rust/lib-hedgewars-engine/src/render/map.rs
rust/lib-hedgewars-engine/src/world.rs
--- a/rust/hwrunner/src/main.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/hwrunner/src/main.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -1,29 +1,14 @@
 use glutin::{
-    dpi,
-    Event,
-    WindowEvent,
-    DeviceEvent,
-    ElementState,
-    MouseButton,
-    MouseScrollDelta,
-    EventsLoop,
-    WindowedContext,
-    GlRequest,
-    GlProfile,
-    ContextTrait,
+    dpi, ContextTrait, DeviceEvent, ElementState, Event, EventsLoop, GlProfile, GlRequest,
+    MouseButton, MouseScrollDelta, WindowEvent, WindowedContext,
 };
 
-use hedgewars_engine::{
-    instance::EngineInstance,
-};
+use hedgewars_engine::instance::EngineInstance;
 
 use integral_geometry::Point;
 
 fn init(event_loop: &EventsLoop, size: dpi::LogicalSize) -> WindowedContext {
-    use glutin::{
-        ContextBuilder,
-        WindowBuilder
-    };
+    use glutin::{ContextBuilder, WindowBuilder};
 
     let window = WindowBuilder::new()
         .with_title("hwengine")
@@ -32,15 +17,17 @@
     let cxt = ContextBuilder::new()
         .with_gl(GlRequest::Latest)
         .with_gl_profile(GlProfile::Core)
-        .build_windowed(window, &event_loop).ok().unwrap();
+        .build_windowed(window, &event_loop)
+        .ok()
+        .unwrap();
 
     unsafe {
         cxt.make_current().unwrap();
         gl::load_with(|ptr| cxt.get_proc_address(ptr) as *const _);
-        
+
         if let Some(sz) = cxt.get_inner_size() {
             let phys = sz.to_physical(cxt.get_hidpi_factor());
-            
+
             gl::Viewport(0, 0, phys.width as i32, phys.height as i32);
         }
     }
@@ -54,13 +41,14 @@
     let window = init(&event_loop, dpi::LogicalSize::new(w, h));
 
     let mut engine = EngineInstance::new();
+    engine.world.create_renderer(w as u16, h as u16);
 
     let mut dragging = false;
 
     use std::time::Instant;
 
     let mut now = Instant::now();
-    
+
     let mut is_running = true;
     while is_running {
         let curr = Instant::now();
@@ -68,48 +56,44 @@
         now = curr;
         let ms = delta.as_secs() as f64 * 1000.0 + delta.subsec_millis() as f64;
         window.set_title(&format!("hwengine {:.3}ms", ms));
-        
-        event_loop.poll_events(|event| {
-            match event {
-                Event::WindowEvent { event, ..} => match event {
-                    WindowEvent::CloseRequested => {
-                        is_running = false;
-                    },
-                    WindowEvent::MouseInput { button, state, .. } => {
-                        if let MouseButton::Right = button {
-                            if let ElementState::Pressed = state {
-                                dragging = true;
-                            } else {
-                                dragging = false;
-                            }
+
+        event_loop.poll_events(|event| match event {
+            Event::WindowEvent { event, .. } => match event {
+                WindowEvent::CloseRequested => {
+                    is_running = false;
+                }
+                WindowEvent::MouseInput { button, state, .. } => {
+                    if let MouseButton::Right = button {
+                        if let ElementState::Pressed = state {
+                            dragging = true;
+                        } else {
+                            dragging = false;
                         }
                     }
-                    WindowEvent::MouseWheel { delta, .. } => {
-                        let zoom_change = match delta {
-                            MouseScrollDelta::LineDelta(x, y) => {
-                                y as f32 * 0.1f32
-                            }
-                            MouseScrollDelta::PixelDelta(delta) => {
-                                let physical = delta.to_physical(window.get_hidpi_factor());
-                                physical.y as f32 * 0.1f32
-                            }
-                        };
-                        engine.world.move_camera(Point::ZERO, zoom_change);
+                }
+                WindowEvent::MouseWheel { delta, .. } => {
+                    let zoom_change = match delta {
+                        MouseScrollDelta::LineDelta(x, y) => y as f32 * 0.1f32,
+                        MouseScrollDelta::PixelDelta(delta) => {
+                            let physical = delta.to_physical(window.get_hidpi_factor());
+                            physical.y as f32 * 0.1f32
+                        }
+                    };
+                    engine.world.move_camera(Point::ZERO, zoom_change);
+                }
+                _ => (),
+            },
+            Event::DeviceEvent { event, .. } => match event {
+                DeviceEvent::MouseMotion { delta } => {
+                    if dragging {
+                        engine
+                            .world
+                            .move_camera(Point::new(delta.0 as i32, delta.1 as i32), 0.0)
                     }
-                    _ => ()
-                },
-                Event::DeviceEvent { event, .. } => match event {
-                    DeviceEvent::MouseMotion { delta } => {
-                        if dragging {
-                            engine.world.move_camera(
-                                Point::new(delta.0 as i32, delta.1 as i32), 0.0
-                            )
-                        }
-                    }
-                    _ => {}
                 }
-                _ => ()
-            }
+                _ => {}
+            },
+            _ => (),
         });
 
         unsafe { window.make_current().unwrap() };
--- a/rust/lib-hedgewars-engine/src/instance.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/instance.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -3,8 +3,8 @@
     UnorderedEngineMessage::*, UnsyncedEngineMessage::*, *,
 };
 
+use integral_geometry::{Point, Rect, Size};
 use landgen::outline_template::OutlineTemplate;
-use integral_geometry::{Point, Rect, Size};
 
 use super::{ipc::IPC, world::World};
 
@@ -18,7 +18,7 @@
         let mut world = World::new();
 
         fn template() -> OutlineTemplate {
-            let mut template = OutlineTemplate::new(Size::new(4096*1, 2048*1));
+            let mut template = OutlineTemplate::new(Size::new(4096 * 1, 2048 * 1));
             template.islands = vec![vec![
                 Rect::from_size_coords(100, 2050, 1, 1),
                 Rect::from_size_coords(100, 500, 400, 1200),
@@ -29,9 +29,9 @@
 
             template
         }
-        
+
         world.init(template());
-        
+
         Self {
             world,
             ipc: IPC::new(),
--- a/rust/lib-hedgewars-engine/src/lib.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/lib.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -10,7 +10,7 @@
     os::raw::{c_char, c_void},
 };
 
-use self::instance::{EngineInstance};
+use self::instance::EngineInstance;
 
 #[repr(C)]
 #[derive(Copy, Clone)]
@@ -81,6 +81,7 @@
     height: u16,
     gl_loader: extern "C" fn(*const c_char) -> *const c_void,
 ) {
+    engine_state.world.create_renderer(width, height);
 }
 
 #[no_mangle]
--- a/rust/lib-hedgewars-engine/src/render.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -1,7 +1,6 @@
-mod map;
+pub mod camera;
 mod gl;
-pub mod camera;
+mod map;
 
+use self::gl::*;
 pub use self::map::*;
-use self::gl::*;
-
--- a/rust/lib-hedgewars-engine/src/render/camera.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render/camera.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -4,15 +4,23 @@
 pub struct Camera {
     pub position: Point,
     pub zoom: f32,
-    size: Size
+    size: Size,
 }
 
 impl Camera {
     pub fn new() -> Self {
-        Self {position: Point::ZERO, zoom: 0.0, size: Size::new(1024, 768) }
+        Self::with_size(Size::new(1024, 768))
+    }
+
+    pub fn with_size(size: Size) -> Self {
+        Self {
+            position: Point::ZERO,
+            zoom: 0.0,
+            size,
+        }
     }
 
     pub fn viewport(&self) -> Rect {
         Rect::from_size(self.position, self.size)
     }
-}
\ No newline at end of file
+}
--- a/rust/lib-hedgewars-engine/src/render/gl.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render/gl.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -1,12 +1,7 @@
- use integral_geometry::Rect;
 
-use std::{
-    mem,
-    slice,
-    ptr,
-    ffi,
-    ffi::CString,
-};
+use integral_geometry::Rect;
+
+use std::{ffi, ffi::CString, mem, ptr, slice};
 
 #[derive(Debug)]
 pub struct Texture2D {
@@ -32,13 +27,13 @@
         internal_format: u32,
         format: u32,
         ty: u32,
-        filter: u32
+        filter: u32,
     ) -> Self {
         let mut handle = 0;
-        
+
         unsafe {
             gl::GenTextures(1, &mut handle);
-            
+
             gl::BindTexture(gl::TEXTURE_2D, handle);
             gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32);
             gl::TexImage2D(
@@ -50,7 +45,7 @@
                 0,
                 format as u32,
                 ty,
-                data.as_ptr() as *const _
+                data.as_ptr() as *const _,
             );
 
             gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
@@ -59,9 +54,7 @@
             gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, filter as i32);
         }
 
-        Texture2D {
-            handle
-        }
+        Texture2D { handle }
     }
 
     pub fn update(&self, region: Rect, data: &[u8], data_stride: u32, format: u32, ty: u32) {
@@ -70,13 +63,13 @@
             gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32);
             gl::TexSubImage2D(
                 gl::TEXTURE_2D,
-                0, // texture level
+                0,             // texture level
                 region.left(), // texture region
                 region.top(),
                 region.width() as i32 - 1,
                 region.height() as i32 - 1,
-                format, // data format
-                ty, // data type
+                format,                    // data format
+                ty,                        // data type
                 data.as_ptr() as *const _, // data ptr
             );
         }
@@ -110,12 +103,8 @@
             usage,
         }
     }
-    
-    fn with_data(
-        ty: u32,
-        usage: u32,
-        data: &[u8]
-    ) -> Buffer {
+
+    fn with_data(ty: u32, usage: u32, data: &[u8]) -> Buffer {
         let mut buffer = 0;
 
         unsafe {
@@ -127,7 +116,7 @@
         Buffer {
             handle: buffer,
             ty,
-            usage
+            usage,
         }
     }
 
@@ -141,20 +130,28 @@
 
     pub fn write_typed<T>(&self, data: &[T]) {
         unsafe {
-            let data = slice::from_raw_parts(
-                data.as_ptr() as *const u8,
-                data.len() * mem::size_of::<T>(),
+            let data =
+                slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::<T>());
+
+            gl::BindBuffer(self.ty, self.handle);
+            gl::BufferData(
+                self.ty,
+                data.len() as isize,
+                data.as_ptr() as *const _ as *const _,
+                self.usage,
             );
-            
-            gl::BindBuffer(self.ty, self.handle);
-            gl::BufferData(self.ty, data.len() as isize, data.as_ptr() as *const _ as *const _, self.usage);
         }
     }
-    
-    pub fn write(&self, data: &[u8]) {        
+
+    pub fn write(&self, data: &[u8]) {
         unsafe {
             gl::BindBuffer(self.ty, self.handle);
-            gl::BufferData(self.ty, data.len() as isize, data.as_ptr() as *const _ as *const _, self.usage);
+            gl::BufferData(
+                self.ty,
+                data.len() as isize,
+                data.as_ptr() as *const _ as *const _,
+                self.usage,
+            );
         }
     }
 }
@@ -192,7 +189,7 @@
     pub fn new<'a>(
         vs: &str,
         ps: Option<&str>,
-        bindings: &[VariableBinding<'a>]
+        bindings: &[VariableBinding<'a>],
     ) -> Result<Self, String> {
         unsafe fn compile_shader(ty: u32, shdr: &str) -> Result<u32, String> {
             let shader = gl::CreateShader(ty);
@@ -217,7 +214,7 @@
                 Ok(shader)
             }
         }
-        
+
         let vs = unsafe { compile_shader(gl::VERTEX_SHADER, vs)? };
         let ps = if let Some(ps) = ps {
             Some(unsafe { compile_shader(gl::FRAGMENT_SHADER, ps)? })
@@ -227,7 +224,7 @@
 
         unsafe {
             let program = gl::CreateProgram();
-            
+
             gl::AttachShader(program, vs);
             if let Some(ps) = ps {
                 gl::AttachShader(program, ps);
@@ -237,8 +234,12 @@
                 match bind {
                     &VariableBinding::Attribute(ref name, id) => {
                         let c_str = CString::new(name.as_bytes()).unwrap();
-                        gl::BindAttribLocation(program, id, c_str.to_bytes_with_nul().as_ptr() as *const _);     
-                    },
+                        gl::BindAttribLocation(
+                            program,
+                            id,
+                            c_str.to_bytes_with_nul().as_ptr() as *const _,
+                        );
+                    }
                     _ => {}
                 }
             }
@@ -270,29 +271,36 @@
                 match bind {
                     VariableBinding::Uniform(name, id) => {
                         let c_str = CString::new(name.as_bytes()).unwrap();
-                        let index = gl::GetUniformLocation(program, c_str.to_bytes_with_nul().as_ptr() as *const _);
+                        let index = gl::GetUniformLocation(
+                            program,
+                            c_str.to_bytes_with_nul().as_ptr() as *const _,
+                        );
 
                         // TODO: impl for block?
-                    },
+                    }
                     VariableBinding::UniformBlock(name, id) => {
                         let c_str = CString::new(name.as_bytes()).unwrap();
-                        let index = gl::GetUniformBlockIndex(program, c_str.to_bytes_with_nul().as_ptr() as *const _);
+                        let index = gl::GetUniformBlockIndex(
+                            program,
+                            c_str.to_bytes_with_nul().as_ptr() as *const _,
+                        );
 
                         gl::UniformBlockBinding(program, index, *id);
                     }
                     VariableBinding::Sampler(name, id) => {
                         let c_str = CString::new(name.as_bytes()).unwrap();
-                        let index = gl::GetUniformLocation(program, c_str.to_bytes_with_nul().as_ptr() as *const _);
-                        
+                        let index = gl::GetUniformLocation(
+                            program,
+                            c_str.to_bytes_with_nul().as_ptr() as *const _,
+                        );
+
                         gl::Uniform1i(index, *id as i32);
-                    },
+                    }
                     _ => {}
                 }
             }
 
-            Ok(Shader {
-                program
-            })
+            Ok(Shader { program })
         }
     }
 
@@ -305,15 +313,18 @@
     pub fn set_matrix(&self, name: &str, matrix: *const f32) {
         unsafe {
             let c_str = CString::new(name).unwrap();
-            let index = gl::GetUniformLocation(self.program, c_str.to_bytes_with_nul().as_ptr() as *const _);
-            
+            let index = gl::GetUniformLocation(
+                self.program,
+                c_str.to_bytes_with_nul().as_ptr() as *const _,
+            );
+
             gl::UniformMatrix4fv(index, 1, gl::FALSE, matrix);
         }
     }
 
     pub fn bind_texture_2d(&self, index: u32, texture: &Texture2D) {
         self.bind();
-        
+
         unsafe {
             gl::ActiveTexture(gl::TEXTURE0 + index);
             gl::BindTexture(gl::TEXTURE_2D, texture.handle);
@@ -335,13 +346,13 @@
     pub offset: u32,
 }
 
-// TODO: 
+// TODO:
 pub struct InputLayout {
     pub elements: Vec<InputElement>,
 }
 
 pub struct LayoutGuard {
-    vao: u32
+    vao: u32,
 }
 
 impl Drop for LayoutGuard {
@@ -355,24 +366,26 @@
 
 impl InputLayout {
     pub fn new(elements: Vec<InputElement>) -> Self {
-        InputLayout {
-            elements,
-        }
+        InputLayout { elements }
     }
 
-    pub fn bind(&mut self, buffers: &[(u32, &Buffer)], index_buffer: Option<&Buffer>) -> LayoutGuard {
+    pub fn bind(
+        &mut self,
+        buffers: &[(u32, &Buffer)],
+        index_buffer: Option<&Buffer>,
+    ) -> LayoutGuard {
         let mut vao = 0;
-        
+
         unsafe {
             gl::GenVertexArrays(1, &mut vao);
             gl::BindVertexArray(vao);
         }
-        
+
         for &(slot, ref buffer) in buffers {
             unsafe {
                 gl::BindBuffer(buffer.ty(), buffer.handle());
             }
-            
+
             for attr in self.elements.iter().filter(|a| a.buffer_slot == slot) {
                 unsafe {
                     gl::EnableVertexAttribArray(attr.shader_slot);
@@ -382,13 +395,9 @@
                                 attr.shader_slot,
                                 attr.components as i32,
                                 fmt,
-                                if normalized {
-                                    gl::TRUE
-                                } else {
-                                    gl::FALSE
-                                },
+                                if normalized { gl::TRUE } else { gl::FALSE },
                                 attr.stride as i32,
-                                attr.offset as *const _
+                                attr.offset as *const _,
                             );
                         }
                         InputFormat::Integer(fmt) => {
@@ -397,11 +406,10 @@
                                 attr.components as i32,
                                 fmt,
                                 attr.stride as i32,
-                                attr.offset as *const _
+                                attr.offset as *const _,
                             );
                         }
                     }
-
                 }
             }
         }
@@ -412,8 +420,6 @@
             }
         }
 
-        LayoutGuard {
-            vao
-        }
+        LayoutGuard { vao }
     }
 }
--- a/rust/lib-hedgewars-engine/src/render/map.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render/map.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -1,15 +1,9 @@
 use integral_geometry::{Point, Rect, Size};
-use land2d::{Land2D};
-use vec2d::{Vec2D};
+use land2d::Land2D;
+use vec2d::Vec2D;
 
 use super::gl::{
-    Texture2D,
-    Buffer,
-    Shader,
-    InputLayout,
-    VariableBinding,
-    InputElement,
-    InputFormat,
+    Buffer, InputElement, InputFormat, InputLayout, Shader, Texture2D, VariableBinding,
 };
 
 // TODO: temp
@@ -80,7 +74,7 @@
     index_offset: u16,
     tile_shader: Shader,
     tile_layout: InputLayout,
-    
+
     tile_width: u32,
     tile_height: u32,
     num_tile_x: i32,
@@ -95,8 +89,9 @@
                 VariableBinding::Attribute("Position", 0),
                 VariableBinding::Attribute("Uv", 1),
                 VariableBinding::Sampler("Texture", 0),
-            ]
-        ).unwrap();
+            ],
+        )
+        .unwrap();
 
         let tile_layout = InputLayout::new(vec![
             // position
@@ -106,7 +101,7 @@
                 format: InputFormat::Float(gl::FLOAT, false),
                 components: 2,
                 stride: 20,
-                offset: 0
+                offset: 0,
             },
             // uv
             InputElement {
@@ -115,14 +110,14 @@
                 format: InputFormat::Float(gl::FLOAT, false),
                 components: 3,
                 stride: 20,
-                offset: 8
+                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(),
@@ -166,7 +161,7 @@
 
                     (data, stride as u32)
                 };
-                
+
                 let texture_index = if idx >= self.textures.len() {
                     let texture = Texture2D::with_data(
                         data,
@@ -176,7 +171,7 @@
                         gl::RGBA8,
                         gl::RGBA,
                         gl::UNSIGNED_BYTE,
-                        gl::NEAREST
+                        gl::NEAREST,
                     );
 
                     let texture_index = self.textures.len();
@@ -186,16 +181,22 @@
                 } else {
                     let texture_region = Rect::new(
                         Point::new(0, 0),
-                        Point::new(self.tile_width as i32, self.tile_height as i32)
+                        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);
+                    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,
@@ -205,16 +206,14 @@
         }
     }
 
-    pub fn update(&mut self, land: &Land2D<u32>, region: Rect) {
-
-    }
+    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;
@@ -234,23 +233,33 @@
                 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] };
+                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.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
+                    index_len: 6,
                 });
             }
         }
@@ -258,9 +267,10 @@
         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 _g = self.tile_layout.bind(
+            &[(0, &self.tile_vertex_buffer)],
+            Some(&self.tile_index_buffer),
+        );
 
         let ortho = {
             let l = viewport.left() as f32;
@@ -269,26 +279,39 @@
             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,
+                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]);
-                
+                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 as *const _,
                 );
             }
 
@@ -296,5 +319,3 @@
         }
     }
 }
-
-
--- a/rust/lib-hedgewars-engine/src/world.rs	Fri Mar 22 20:01:47 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/world.rs	Fri Mar 22 20:26:29 2019 +0300
@@ -1,19 +1,14 @@
-use fpnum::{FPNum, fp};
+use fpnum::{fp, FPNum};
+use hwphysics as hwp;
 use integral_geometry::{Point, Rect, Size};
 use land2d::Land2D;
 use landgen::{
-    outline_template::OutlineTemplate,
-    template_based::TemplatedLandGenerator,
-    LandGenerationParameters,
-    LandGenerator,
+    outline_template::OutlineTemplate, template_based::TemplatedLandGenerator,
+    LandGenerationParameters, LandGenerator,
 };
 use lfprng::LaggedFibonacciPRNG;
-use hwphysics as hwp;
 
-use crate::render::{
-    MapRenderer,
-    camera::Camera
-};
+use crate::render::{camera::Camera, MapRenderer};
 
 struct GameState {
     land: Land2D<u32>,
@@ -22,10 +17,7 @@
 
 impl GameState {
     fn new(land: Land2D<u32>, physics: hwp::World) -> Self {
-        Self {
-            land,
-            physics,
-        }
+        Self { land, physics }
     }
 }
 
@@ -33,8 +25,8 @@
     random_numbers_gen: LaggedFibonacciPRNG,
     preview: Option<Land2D<u8>>,
     game_state: Option<GameState>,
-    renderer: MapRenderer,
-    camera: Camera
+    renderer: Option<MapRenderer>,
+    camera: Camera,
 }
 
 impl World {
@@ -43,11 +35,16 @@
             random_numbers_gen: LaggedFibonacciPRNG::new(&[]),
             preview: None,
             game_state: None,
-            renderer: MapRenderer::new(512, 512),
-            camera: Camera::new()
+            renderer: None,
+            camera: Camera::new(),
         }
     }
 
+    pub fn create_renderer(&mut self, width: u16, height: u16) {
+        self.renderer = Some(MapRenderer::new(512, 512));
+        self.camera = Camera::with_size(Size::new(width as usize, height as usize));
+    }
+
     pub fn set_seed(&mut self, seed: &[u8]) {
         self.random_numbers_gen = LaggedFibonacciPRNG::new(seed);
     }
@@ -87,16 +84,17 @@
         let land = landgen.generate_land(&params, &mut self.random_numbers_gen);
 
         use mapgen::{
+            theme::{slice_u32_to_u8, Theme},
             MapGenerator,
-            theme::{Theme, slice_u32_to_u8}
         };
 
         use std::path::Path;
-        
+
         let theme = Theme::load(Path::new("../../share/hedgewars/Data/Themes/Cheese/")).unwrap();
         let texture = MapGenerator::new().make_texture32(&land, &theme);
-        self.renderer.init(&texture);
-        
+        if let Some(ref mut renderer) = self.renderer {
+            renderer.init(&texture);
+        }
         self.game_state = Some(GameState::new(land, physics));
     }
 
@@ -106,13 +104,14 @@
     }
 
     pub fn render(&mut self) {
+        if let Some(ref mut renderer) = self.renderer {
+            unsafe {
+                gl::ClearColor(0.4f32, 0f32, 0.2f32, 1f32);
+                gl::Clear(gl::COLOR_BUFFER_BIT);
+            }
 
-        unsafe {
-            gl::ClearColor(0.4f32, 0f32, 0.2f32, 1f32);
-            gl::Clear(gl::COLOR_BUFFER_BIT);
+            renderer.render(self.camera.viewport());
         }
-
-        self.renderer.render(self.camera.viewport());
     }
 
     pub fn step(&mut self) {
@@ -121,6 +120,3 @@
         }
     }
 }
-
-
-