← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/glsl into lp:widelands

 

SirVer has proposed merging lp:~widelands-dev/widelands/glsl into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #984372 in widelands: "opengl-es support"
  https://bugs.launchpad.net/widelands/+bug/984372

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/glsl/+merge/240363

Ports the GL terrain rendering to shaders, staying compatible with OpenGL 2 (so that targeting OpenGL ES 2 for mobile is easy). 

This does not yet remove the use of OpenGL 1 (fixed pipeline) from Widelands as there is still code for blitting GLSurfaces that needs to be ported, but this is the major work.
-- 
https://code.launchpad.net/~widelands-dev/widelands/glsl/+merge/240363
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/glsl into lp:widelands.
=== modified file 'src/game_io/game_preload_packet.cc'
--- src/game_io/game_preload_packet.cc	2014-10-12 08:39:48 +0000
+++ src/game_io/game_preload_packet.cc	2014-11-01 20:50:47 +0000
@@ -27,7 +27,7 @@
 #include "base/time_string.h"
 #include "graphic/graphic.h"
 #include "graphic/in_memory_image.h"
-#include "graphic/render/minimaprenderer.h"
+#include "graphic/minimap_renderer.h"
 #include "graphic/surface.h"
 #include "logic/game.h"
 #include "logic/game_data_error.h"

=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2014-07-15 05:12:37 +0000
+++ src/graphic/CMakeLists.txt	2014-11-01 20:50:47 +0000
@@ -39,18 +39,18 @@
 wl_library(graphic_surface
   SRCS
     compositemode.h
-    render/gl_surface.cc
-    render/gl_surface.h
-    render/gl_surface_screen.cc
-    render/gl_surface_screen.h
-    render/gl_surface_texture.cc
-    render/gl_surface_texture.h
-    render/gl_utils.cc
-    render/gl_utils.h
-    render/sdl_helper.cc
-    render/sdl_helper.h
-    render/sdl_surface.cc
-    render/sdl_surface.h
+    gl/surface.cc
+    gl/surface.h
+    gl/surface_screen.cc
+    gl/surface_screen.h
+    gl/surface_texture.cc
+    gl/surface_texture.h
+    gl/utils.cc
+    gl/utils.h
+    sdl/surface.cc
+    sdl/surface.h
+    sdl/utils.cc
+    sdl/utils.h
     surface.cc
     surface.h
     surface_cache.cc
@@ -82,27 +82,34 @@
     font_handler.h
     font_handler1.cc
     font_handler1.h
+    game_renderer.cc
+    game_renderer.h
+    gl/dither_program.cc
+    gl/dither_program.h
+    gl/fields_to_draw.h
+    gl/game_renderer.cc
+    gl/game_renderer.h
+    gl/road_program.cc
+    gl/road_program.h
+    gl/terrain_program.cc
+    gl/terrain_program.h
     graphic.cc
     graphic.h
     image_transformations.cc
     image_transformations.h
     in_memory_image.cc
     in_memory_image.h
-    render/gamerenderer.cc
-    render/gamerenderer.h
-    render/gamerenderer_gl.cc
-    render/gamerenderer_gl.h
-    render/gamerenderer_sdl.cc
-    render/gamerenderer_sdl.h
-    render/minimaprenderer.cc
-    render/minimaprenderer.h
-    render/terrain_sdl.cc
-    render/terrain_sdl.h
-    render/vertex.h
+    minimap_renderer.cc
+    minimap_renderer.h
     rendertarget.cc
     rendertarget.h
     richtext.cc
     richtext.h
+    sdl/game_renderer.cc
+    sdl/game_renderer.h
+    sdl/terrain.cc
+    sdl/terrain.h
+    sdl/vertex.h
     text_parser.cc
     text_parser.h
     texture.cc

=== renamed file 'src/graphic/render/gamerenderer.cc' => 'src/graphic/game_renderer.cc'
--- src/graphic/render/gamerenderer.cc	2014-09-10 17:52:49 +0000
+++ src/graphic/game_renderer.cc	2014-11-01 20:50:47 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "graphic/render/gamerenderer.h"
+#include "graphic/game_renderer.h"
 
 #include "base/macros.h"
 #include "graphic/graphic.h"

=== renamed file 'src/graphic/render/gamerenderer.h' => 'src/graphic/game_renderer.h'
--- src/graphic/render/gamerenderer.h	2014-09-10 08:55:04 +0000
+++ src/graphic/game_renderer.h	2014-11-01 20:50:47 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_GAMERENDERER_H
-#define WL_GRAPHIC_RENDER_GAMERENDERER_H
+#ifndef WL_GRAPHIC_GAME_RENDERER_H
+#define WL_GRAPHIC_GAME_RENDERER_H
 
 #include <boost/utility.hpp>
 
@@ -99,4 +99,4 @@
 	DISALLOW_COPY_AND_ASSIGN(GameRenderer);
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GAMERENDERER_H
+#endif  // end of include guard: WL_GRAPHIC_GAME_RENDERER_H

=== added directory 'src/graphic/gl'
=== added file 'src/graphic/gl/dither_program.cc'
--- src/graphic/gl/dither_program.cc	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/dither_program.cc	2014-11-01 20:50:47 +0000
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "graphic/gl/dither_program.h"
+
+#include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/surface_texture.h"
+#include "graphic/graphic.h"
+#include "graphic/image_io.h"
+#include "graphic/surface_cache.h"
+#include "graphic/texture.h"
+#include "io/fileread.h"
+#include "io/filesystem/layered_filesystem.h"
+
+namespace  {
+
+using namespace Widelands;
+
+const char kDitherVertexShader[] = R"(
+#version 120
+
+// Attributes.
+attribute float attr_brightness;
+attribute vec2 attr_position;
+attribute vec2 attr_texture_position;
+attribute vec2 attr_dither_texture_position;
+
+// Output of vertex shader.
+varying vec2 var_texture_position;
+varying vec2 var_dither_texture_position;
+varying float var_brightness;
+
+void main() {
+	var_texture_position = attr_texture_position;
+	var_dither_texture_position = attr_dither_texture_position;
+	var_brightness = attr_brightness;
+	vec4 p = vec4(attr_position, 0., 1.);
+	gl_Position = gl_ProjectionMatrix * p;
+}
+)";
+
+const char kDitherFragmentShader[] = R"(
+#version 120
+
+uniform sampler2D u_dither_texture;
+uniform sampler2D u_terrain_texture;
+
+varying float var_brightness;
+varying vec2 var_texture_position;
+varying vec2 var_dither_texture_position;
+
+void main() {
+	vec4 clr = texture2D(u_terrain_texture, var_texture_position);
+	clr.rgb *= var_brightness;
+	clr.a = 1. - texture2D(u_dither_texture, var_dither_texture_position).a;
+	gl_FragColor = clr;
+}
+)";
+
+// Returns the texture mask for the dithering step.
+const GLSurfaceTexture* get_dither_edge_texture() {
+	constexpr char kFilename[] = "world/pics/edge.png";
+	constexpr char kCacheName[] =  "gltex#world/pics/edge.png";
+
+	if (Surface* surface = g_gr->surfaces().get(kCacheName))
+		return dynamic_cast<GLSurfaceTexture*>(surface);
+
+	SDL_Surface* sdlsurf = load_image_as_sdl_surface(kFilename, g_fs);
+	GLSurfaceTexture* edgetexture = new GLSurfaceTexture(sdlsurf, true);
+	g_gr->surfaces().insert(kCacheName, edgetexture, false);
+	return edgetexture;
+}
+
+}  // namespace
+
+DitherProgram::DitherProgram() {
+	gl_program_.build(kDitherVertexShader, kDitherFragmentShader);
+
+	attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
+	attr_dither_texture_position_ =
+	   glGetAttribLocation(gl_program_.object(), "attr_dither_texture_position");
+	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
+	attr_texture_position_ =
+	   glGetAttribLocation(gl_program_.object(), "attr_texture_position");
+
+	u_dither_texture_ = glGetUniformLocation(gl_program_.object(), "u_dither_texture");
+	u_terrain_texture_ = glGetUniformLocation(gl_program_.object(), "u_terrain_texture");
+}
+
+void DitherProgram::add_vertex(const FieldsToDraw::Field& field,
+                               const int order_index,
+                               const int terrain) {
+	vertices_[terrain].emplace_back();
+	PerVertexData& back = vertices_[terrain].back();
+
+	back.x = field.x;
+	back.y = field.y;
+	back.texture_x = field.texture_x;
+	back.texture_y = field.texture_y;
+	back.brightness = field.brightness;
+
+	switch (order_index) {
+	case 0:
+		back.dither_texture_x = 0.;
+		back.dither_texture_y = 0.;
+		break;
+	case 1:
+		back.dither_texture_x = 1.;
+		back.dither_texture_y = 0.;
+		break;
+	case 2:
+		back.dither_texture_x = 0.5;
+		back.dither_texture_y = 1.;
+		break;
+	}
+}
+
+void DitherProgram::maybe_add_dithering_triangle(
+   const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+   const FieldsToDraw& fields_to_draw,
+   const int idx1,
+   const int idx2,
+   const int idx3,
+   const int my_terrain,
+   const int other_terrain) {
+	if (my_terrain == other_terrain) {
+		return;
+	}
+	if (terrains.get_unmutable(my_terrain).dither_layer() <
+	    terrains.get_unmutable(other_terrain).dither_layer()) {
+		add_vertex(fields_to_draw.at(idx1), 0, other_terrain);
+		add_vertex(fields_to_draw.at(idx2), 1, other_terrain);
+		add_vertex(fields_to_draw.at(idx3), 2, other_terrain);
+	}
+}
+
+void DitherProgram::draw(const DescriptionMaintainer<TerrainDescription>& terrains,
+                         const FieldsToDraw& fields_to_draw) {
+	glUseProgram(gl_program_.object());
+
+	glEnableVertexAttribArray(attr_brightness_);
+	glEnableVertexAttribArray(attr_dither_texture_position_);
+	glEnableVertexAttribArray(attr_position_);
+	glEnableVertexAttribArray(attr_texture_position_);
+
+	if (vertices_.size() != terrains.size()) {
+		vertices_.resize(terrains.size());
+	}
+	for (auto& container : vertices_) {
+		container.clear();
+	}
+
+	for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
+		const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
+
+		// The bottom right neighbor fields_to_draw is needed for both triangles
+		// associated with this field. If it is not in fields_to_draw, there is no need to
+		// draw any triangles.
+		const int brn_index = fields_to_draw.calculate_index(field.fx + (field.fy & 1), field.fy + 1);
+		if (brn_index == -1) {
+			continue;
+		}
+
+		// Dithering triangles for Down triangle.
+		const int bln_index =
+		   fields_to_draw.calculate_index(field.fx + (field.fy & 1) - 1, field.fy + 1);
+		if (bln_index != -1) {
+			maybe_add_dithering_triangle(terrains, fields_to_draw,
+			   brn_index, current_index, bln_index, field.ter_d, field.ter_r);
+
+			const int terrain_dd = fields_to_draw.at(bln_index).ter_r;
+			maybe_add_dithering_triangle(terrains, fields_to_draw,
+			   bln_index, brn_index, current_index, field.ter_d, terrain_dd);
+
+			const int ln_index = fields_to_draw.calculate_index(field.fx - 1, field.fy);
+			if (ln_index != -1) {
+				const int terrain_l = fields_to_draw.at(ln_index).ter_r;
+				maybe_add_dithering_triangle(terrains, fields_to_draw,
+				   current_index, bln_index, brn_index, field.ter_d, terrain_l);
+			}
+		}
+
+		// Dithering for right triangle.
+		const int rn_index = fields_to_draw.calculate_index(field.fx + 1, field.fy);
+		if (rn_index != -1) {
+			maybe_add_dithering_triangle(terrains, fields_to_draw,
+			   current_index, brn_index, rn_index, field.ter_r, field.ter_d);
+			int terrain_rr = fields_to_draw.at(rn_index).ter_d;
+			maybe_add_dithering_triangle(terrains, fields_to_draw,
+			   brn_index, rn_index, current_index, field.ter_r, terrain_rr);
+
+			const int trn_index =
+			   fields_to_draw.calculate_index(field.fx + (field.fy & 1), field.fy - 1);
+			if (trn_index != -1) {
+				const int terrain_u = fields_to_draw.at(trn_index).ter_d;
+				maybe_add_dithering_triangle(terrains, fields_to_draw,
+				   rn_index, current_index, brn_index, field.ter_r, terrain_u);
+			}
+		}
+	}
+
+	// Set the sampler texture unit to 0
+	glActiveTexture(GL_TEXTURE0);
+	glUniform1i(u_dither_texture_, 0);
+	glBindTexture(GL_TEXTURE_2D, get_dither_edge_texture()->get_gl_texture());
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+	glActiveTexture(GL_TEXTURE1);
+	glUniform1i(u_terrain_texture_, 1);
+
+	// Which triangles to draw?
+	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
+	for (size_t i = 0; i < vertices_.size(); ++i) {
+		const auto& current_data = vertices_[i];
+		if (current_data.empty()) {
+			continue;
+		}
+		glBindTexture(
+		   GL_TEXTURE_2D,
+		   g_gr->get_maptexture_data(terrains.get_unmutable(i).get_texture())->get_texture());
+
+		glBufferData(GL_ARRAY_BUFFER,
+		             sizeof(PerVertexData) * current_data.size(),
+		             current_data.data(),
+		             GL_STREAM_DRAW);
+
+		const auto set_attrib_pointer = [](const int vertex_index, int num_items, int offset) {
+			glVertexAttribPointer(vertex_index,
+			                      num_items,
+			                      GL_FLOAT,
+			                      GL_FALSE,
+			                      sizeof(PerVertexData),
+			                      reinterpret_cast<void*>(offset));
+		};
+		set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
+		set_attrib_pointer(attr_dither_texture_position_, 2, offsetof(PerVertexData, dither_texture_x));
+		set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, x));
+		set_attrib_pointer(attr_texture_position_, 2, offsetof(PerVertexData, texture_x));
+
+		glDrawArrays(GL_TRIANGLES, 0, current_data.size());
+	}
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	glDisableVertexAttribArray(attr_brightness_);
+	glDisableVertexAttribArray(attr_dither_texture_position_);
+	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_texture_position_);
+
+	glActiveTexture(GL_TEXTURE0);
+
+	// Release Program object.
+	glUseProgram(0);
+}

=== added file 'src/graphic/gl/dither_program.h'
--- src/graphic/gl/dither_program.h	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/dither_program.h	2014-11-01 20:50:47 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_GL_DITHER_PROGRAM_H
+#define WL_GRAPHIC_GL_DITHER_PROGRAM_H
+
+#include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/utils.h"
+#include "logic/description_maintainer.h"
+#include "logic/world/terrain_description.h"
+
+class DitherProgram {
+public:
+	DitherProgram();
+
+	// Draws the terrain.
+	void draw(const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+	          const FieldsToDraw& fields_to_draw);
+
+private:
+	// Adds the triangle between the indexes (which index 'fields_to_draw' to
+	// vertices_ if the my_terrain != other_terrain and the dither_layer()
+	// agree.
+	void maybe_add_dithering_triangle(
+	   const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+	   const FieldsToDraw& fields_to_draw,
+	   int idx1,
+	   int idx2,
+	   int idx3,
+	   int my_terrain,
+	   int other_terrain);
+
+	// Adds the 'field' as an vertex to the 'vertices_' entry for 'terrain'. The
+	// 'order_index' defines which texture position will be used for this
+	// vertcx.
+	void add_vertex(const FieldsToDraw::Field& field, int order_index, int terrain);
+
+	struct PerVertexData {
+		float x;
+		float y;
+		float texture_x;
+		float texture_y;
+		float brightness;
+		float dither_texture_x;
+		float dither_texture_y;
+	};
+
+	// The program used for drawing the terrain.
+	Gl::Program gl_program_;
+
+	// The buffer that contains the data to be rendered.
+	Gl::Buffer gl_array_buffer_;
+
+	// Attributes.
+	GLint attr_position_;
+	GLint attr_texture_position_;
+	GLint attr_dither_texture_position_;
+	GLint attr_brightness_;
+
+	// Uniforms.
+	GLint u_terrain_texture_;
+	GLint u_dither_texture_;
+
+	// Objects below are here to avoid memory allocations on each frame, they
+	// could theoretically also always be recreated. Index as follows:
+	// vertices_[terrain_index][vertex_index]
+	std::vector<std::vector<PerVertexData>> vertices_;
+};
+
+#endif  // end of include guard: WL_GRAPHIC_GL_DITHER_PROGRAM_H

=== added file 'src/graphic/gl/fields_to_draw.h'
--- src/graphic/gl/fields_to_draw.h	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/fields_to_draw.h	2014-11-01 20:50:47 +0000
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_GL_FIELDS_TO_DRAW_H
+#define WL_GRAPHIC_GL_FIELDS_TO_DRAW_H
+
+#include <vector>
+
+#include <stdint.h>
+
+// Helper struct that contains the data needed for drawing all fields. All
+// methods are inlined for performance reasons.
+class FieldsToDraw {
+public:
+	struct Field {
+		int fx, fy;  // geometric coordinates (i.e. map coordinates that can be out of bounds).
+		float x, y;  // Pixel position relative to top left.
+		float texture_x, texture_y;  // Texture coordinates.
+		float brightness;            // brightness of the pixel
+		uint8_t ter_r, ter_d;        // Texture index of the right and down triangle.
+		uint8_t roads;  // Bitmask of roads to render, see logic/roadtype.h.
+	};
+
+	FieldsToDraw(int minfx, int maxfx, int minfy, int maxfy)
+	   : min_fx_(minfx),
+	     max_fx_(maxfx),
+	     min_fy_(minfy),
+	     max_fy_(maxfy),
+	     w_(max_fx_ - min_fx_ + 1),
+	     h_(max_fy_ - min_fy_ + 1) {
+		fields_.resize(w_ * h_);
+	}
+
+	// Calculates the index of the given field with ('fx', 'fy') being geometric
+	// coordinates in the map. Returns -1 if this field is not in the fields_to_draw.
+	inline int calculate_index(int fx, int fy) const {
+		uint16_t xidx = fx - min_fx_;
+		if (xidx >= w_) {
+			return -1;
+		}
+		uint16_t yidx = fy - min_fy_;
+		if (yidx >= h_) {
+			return -1;
+		}
+		return yidx * w_ + xidx;
+	}
+
+	// The number of fields to draw.
+	inline size_t size() const {
+		return fields_.size();
+	}
+
+	// Get the field at 'index' which must be in bound.
+	inline const Field& at(const int index) const {
+		return fields_.at(index);
+	}
+
+	// Returns a mutable field at 'index' which must be in bound.
+	inline Field* mutable_field(const int index) {
+		return &fields_[index];
+	}
+
+private:
+	// Minimum and maximum field coordinates (geometric) to render. Can be negative.
+	const int min_fx_;
+	const int max_fx_;
+	const int min_fy_;
+	const int max_fy_;
+
+	// Width and height in number of fields.
+	const int w_;
+	const int h_;
+
+	std::vector<Field> fields_;
+};
+
+
+#endif  // end of include guard: WL_GRAPHIC_GL_FIELDS_TO_DRAW_H

=== renamed file 'src/graphic/render/gamerenderer_gl.cc' => 'src/graphic/gl/game_renderer.cc'
--- src/graphic/render/gamerenderer_gl.cc	2014-09-20 09:37:47 +0000
+++ src/graphic/gl/game_renderer.cc	2014-11-01 20:50:47 +0000
@@ -17,484 +17,63 @@
  *
  */
 
-#include "graphic/render/gamerenderer_gl.h"
-
-#include <SDL_image.h>
-
+#include "graphic/gl/game_renderer.h"
+
+#include <memory>
+
+#include "graphic/gl/dither_program.h"
+#include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/road_program.h"
+#include "graphic/gl/surface.h"
+#include "graphic/gl/terrain_program.h"
 #include "graphic/graphic.h"
-#include "graphic/image_io.h"
-#include "graphic/render/gl_surface.h"
 #include "graphic/rendertarget.h"
-#include "graphic/surface_cache.h"
 #include "graphic/texture.h"
-#include "io/fileread.h"
-#include "io/filesystem/layered_filesystem.h"
 #include "logic/editor_game_base.h"
 #include "logic/player.h"
-#include "logic/world/terrain_description.h"
 #include "logic/world/world.h"
 #include "wui/mapviewpixelconstants.h"
 #include "wui/mapviewpixelfunctions.h"
 #include "wui/overlay_manager.h"
 
+namespace {
+
 using namespace Widelands;
 
-static const uint32_t PatchSize = 4;
-
-GameRendererGL::GameRendererGL() :
-	m_patch_vertices_size(0),
-	m_patch_indices_size(0),
-	m_edge_vertices_size(0),
-	m_road_vertices_size(0)
-{
-}
-
-GameRendererGL::~GameRendererGL()
-{
-}
-
-const GLSurfaceTexture * GameRendererGL::get_dither_edge_texture()
-{
-	const std::string fname = "world/pics/edge.png";
-	const std::string cachename = std::string("gltex#") + fname;
-
-	if (Surface* surface = g_gr->surfaces().get(cachename))
-		return dynamic_cast<GLSurfaceTexture *>(surface);
-
-	SDL_Surface* sdlsurf = load_image_as_sdl_surface(fname, g_fs);
-	GLSurfaceTexture* edgetexture = new GLSurfaceTexture(sdlsurf, true);
-	g_gr->surfaces().insert(cachename, edgetexture, false);
-	return edgetexture;
-}
-
-uint32_t GameRendererGL::patch_index(const Coords & f) const
-{
-	uint32_t x = f.x - m_patch_size.x;
-	uint32_t y = f.y - m_patch_size.y;
-
-	assert(x < m_patch_size.w);
-	assert(y < m_patch_size.h);
-
-	uint32_t outerx = x / PatchSize;
-	uint32_t outery = y / PatchSize;
-	uint32_t innerx = x % PatchSize;
-	uint32_t innery = y % PatchSize;
-
-	return
-		(outery * (m_patch_size.w / PatchSize) + outerx) * (PatchSize * PatchSize) +
-		innery * PatchSize + innerx;
-}
-
-uint8_t GameRendererGL::field_brightness(const FCoords & coords) const
-{
-	uint32_t brightness;
-	brightness = 144 + coords.field->get_brightness();
-	brightness = (brightness * 255) / 160;
-	if (brightness > 255)
-		brightness = 255;
-
-	if (m_player && !m_player->see_all()) {
-		const Map & map = m_egbase->map();
-		const Player::Field & pf = m_player->fields()[Map::get_index(coords, map.get_width())];
+// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for
+// 'player' (which can be nullptr).
+float field_brightness(const FCoords& fcoords,
+                       const uint32_t gametime,
+                       const Map& map,
+                       const Player* const player) {
+	uint32_t brightness = 144 + fcoords.field->get_brightness();
+	brightness = std::min<uint32_t>(255, (brightness * 255) / 160);
+
+	if (player && !player->see_all()) {
+		const Player::Field& pf = player->fields()[Map::get_index(fcoords, map.get_width())];
 		if (pf.vision == 0) {
-			return 0;
+			return 0.;
 		} else if (pf.vision == 1) {
-			static const uint32_t DecayTime = 20000;
-			Duration time_ago = m_egbase->get_gametime() - pf.time_node_last_unseen;
-			if (time_ago < DecayTime)
-				brightness = (brightness * (2 * DecayTime - time_ago)) / (2 * DecayTime);
-			else
+			static const uint32_t kDecayTimeInMs = 20000;
+			const Duration time_ago = gametime - pf.time_node_last_unseen;
+			if (time_ago < kDecayTimeInMs) {
+				brightness = (brightness * (2 * kDecayTimeInMs - time_ago)) / (2 * kDecayTimeInMs);
+			} else {
 				brightness = brightness / 2;
-		}
-	}
-
-	return brightness;
-}
-
-void GameRendererGL::draw()
-{
-	const World & world = m_egbase->world();
-	if (m_terrain_freq.size() < world.terrains().get_nitems()) {
-		m_terrain_freq.resize(world.terrains().get_nitems());
-		m_terrain_edge_freq.resize(world.terrains().get_nitems());
-	}
-
-	m_surface = dynamic_cast<GLSurface *>(m_dst->get_surface());
-	if (!m_surface)
-		return;
-	m_rect = m_dst->get_rect();
-	m_surface_offset = m_dst_offset + m_rect.top_left() + m_dst->get_offset();
-
-	m_patch_size.x = m_minfx - 1;
-	m_patch_size.y = m_minfy;
-	m_patch_size.w = ((m_maxfx - m_minfx + 2 + PatchSize) / PatchSize) * PatchSize;
-	m_patch_size.h = ((m_maxfy - m_minfy + 1 + PatchSize) / PatchSize) * PatchSize;
-
-	glScissor
-		(m_rect.x, m_surface->height() - m_rect.y - m_rect.h,
-		 m_rect.w, m_rect.h);
-	glEnable(GL_SCISSOR_TEST);
-
-	prepare_terrain_base();
-	draw_terrain_base();
-	if (g_gr->caps().gl.multitexture && g_gr->caps().gl.max_tex_combined >= 2) {
-		prepare_terrain_dither();
-		draw_terrain_dither();
-	}
-	prepare_roads();
-	draw_roads();
-	draw_objects();
-
-	glDisable(GL_SCISSOR_TEST);
-}
-
-template<typename vertex>
-void GameRendererGL::compute_basevertex(const Coords & coords, vertex & vtx) const
-{
-	const Map & map = m_egbase->map();
-	Coords ncoords(coords);
-	map.normalize_coords(ncoords);
-	FCoords fcoords = map.get_fcoords(ncoords);
-	Point pix;
-	MapviewPixelFunctions::get_basepix(coords, pix.x, pix.y);
-	pix.y -= fcoords.field->get_height() * HEIGHT_FACTOR;
-	pix += m_surface_offset;
-	vtx.x = pix.x;
-	vtx.y = pix.y;
-	Point tex;
-	MapviewPixelFunctions::get_basepix(coords, tex.x, tex.y);
-	vtx.tcx = float(tex.x) / TEXTURE_WIDTH;
-	vtx.tcy = float(tex.y) / TEXTURE_HEIGHT;
-	uint8_t brightness = field_brightness(fcoords);
-	vtx.color[0] = vtx.color[1] = vtx.color[2] = brightness;
-	vtx.color[3] = 255;
-}
-
-void GameRendererGL::count_terrain_base(TerrainIndex ter)
-{
-	if (ter >= m_terrain_freq.size())
-		m_terrain_freq.resize(ter + 1);
-	m_terrain_freq[ter] += 1;
-}
-
-void GameRendererGL::add_terrain_base_triangle
-	(TerrainIndex ter, const Coords & p1, const Coords & p2, const Coords & p3)
-{
-	uint32_t index = m_patch_indices_indexs[ter];
-	m_patch_indices[index++] = patch_index(p1);
-	m_patch_indices[index++] = patch_index(p2);
-	m_patch_indices[index++] = patch_index(p3);
-	m_patch_indices_indexs[ter] = index;
-}
-
-void GameRendererGL::collect_terrain_base(bool onlyscan)
-{
-	const Map & map = m_egbase->map();
-
-	uint32_t index = 0;
-	for (uint32_t outery = 0; outery < m_patch_size.h / PatchSize; ++outery) {
-		for (uint32_t outerx = 0; outerx < m_patch_size.w / PatchSize; ++outerx) {
-			for (uint32_t innery = 0; innery < PatchSize; ++innery) {
-				for (uint32_t innerx = 0; innerx < PatchSize; ++innerx) {
-					Coords coords
-						(m_patch_size.x + outerx * PatchSize + innerx,
-						 m_patch_size.y + outery * PatchSize + innery);
-
-					if (onlyscan) {
-						assert(index == patch_index(coords));
-						compute_basevertex(coords, m_patch_vertices[index]);
-						++index;
-					}
-
-					if (coords.x >= m_minfx && coords.y >= m_minfy && coords.x <= m_maxfx && coords.y <= m_maxfy) {
-						Coords ncoords(coords);
-						map.normalize_coords(ncoords);
-						FCoords fcoords = map.get_fcoords(ncoords);
-						TerrainIndex ter_d = fcoords.field->get_terrains().d;
-						TerrainIndex ter_r = fcoords.field->get_terrains().r;
-
-						if (onlyscan) {
-							count_terrain_base(ter_d);
-							count_terrain_base(ter_r);
-						} else {
-							Coords brn(coords.x + (coords.y & 1), coords.y + 1);
-							Coords bln(brn.x - 1, brn.y);
-							Coords rn(coords.x + 1, coords.y);
-
-							add_terrain_base_triangle(ter_d, coords, bln, brn);
-							add_terrain_base_triangle(ter_r, coords, brn, rn);
-						}
-					}
-				}
-			}
-		}
-	}
-}
-
-void GameRendererGL::prepare_terrain_base()
-{
-	static_assert(sizeof(BaseVertex) == 32, "assert(sizeof(basevertex) == 32) failed.");
-
-	uint32_t reqsize = m_patch_size.w * m_patch_size.h;
-	if (reqsize > 0x10000)
-		throw wexception("Too many vertices; decrease screen resolution");
-
-	if (reqsize > m_patch_vertices_size) {
-		m_patch_vertices.reset(new BaseVertex[reqsize]);
-		m_patch_vertices_size = reqsize;
-	}
-
-	m_terrain_freq.assign(m_terrain_freq.size(), 0);
-
-	collect_terrain_base(true);
-
-	m_terrain_freq_cum.resize(m_terrain_freq.size());
-	uint32_t nrtriangles = 0;
-	for (uint32_t idx = 0; idx < m_terrain_freq.size(); ++idx) {
-		m_terrain_freq_cum[idx] = nrtriangles;
-		nrtriangles += m_terrain_freq[idx];
-	}
-
-	if (3 * nrtriangles > m_patch_indices_size) {
-		m_patch_indices.reset(new uint16_t[3 * nrtriangles]);
-		m_patch_indices_size = 3 * nrtriangles;
-	}
-
-	m_patch_indices_indexs.resize(m_terrain_freq.size());
-	for (TerrainIndex ter = 0; ter < m_terrain_freq.size(); ++ter)
-		m_patch_indices_indexs[ter] = 3 * m_terrain_freq_cum[ter];
-
-	collect_terrain_base(false);
-
-	for (TerrainIndex ter = 0; ter < m_terrain_freq.size(); ++ter) {
-		assert(m_patch_indices_indexs[ter] == 3 * (m_terrain_freq_cum[ter] + m_terrain_freq[ter]));
-	}
-}
-
-void GameRendererGL::draw_terrain_base()
-{
-	const World & world = m_egbase->world();
-
-	glMatrixMode(GL_TEXTURE);
-	glLoadIdentity();
-
-	glVertexPointer(2, GL_FLOAT, sizeof(BaseVertex), &m_patch_vertices[0].x);
-	glTexCoordPointer(2, GL_FLOAT, sizeof(BaseVertex), &m_patch_vertices[0].tcx);
-	glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(BaseVertex), &m_patch_vertices[0].color);
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glEnableClientState(GL_COLOR_ARRAY);
-
-	glColor3f(1.0, 1.0, 1.0);
-	glDisable(GL_BLEND);
-
-	for (TerrainIndex ter = 0; ter < m_terrain_freq.size(); ++ter) {
-		if (!m_terrain_freq[ter])
-			continue;
-
-		const Texture & texture =
-				*g_gr->get_maptexture_data
-					(world.terrain_descr(ter).get_texture());
-		glBindTexture(GL_TEXTURE_2D, texture.get_texture());
-		glDrawRangeElements
-			(GL_TRIANGLES,
-			 0, m_patch_size.w * m_patch_size.h - 1,
-			 3 * m_terrain_freq[ter], GL_UNSIGNED_SHORT,
-			 &m_patch_indices[3 * m_terrain_freq_cum[ter]]);
-	}
-
-	glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_COLOR_ARRAY);
-}
-
-void GameRendererGL::add_terrain_dither_triangle
-	(bool onlyscan, TerrainIndex ter, const Coords & edge1, const Coords & edge2, const Coords & opposite)
-{
-	if (onlyscan) {
-		assert(ter < m_terrain_edge_freq.size());
-		m_terrain_edge_freq[ter] += 1;
-	} else {
-		static const float TyZero = 1.0 / TEXTURE_HEIGHT;
-		static const float TyOne = 1.0 - TyZero;
-
-		uint32_t index = m_terrain_edge_indexs[ter];
-		compute_basevertex(edge1, m_edge_vertices[index]);
-		m_edge_vertices[index].edgex = 0.0;
-		m_edge_vertices[index].edgey = TyZero;
-		++index;
-		compute_basevertex(edge2, m_edge_vertices[index]);
-		m_edge_vertices[index].edgex = 1.0;
-		m_edge_vertices[index].edgey = TyZero;
-		++index;
-		compute_basevertex(opposite, m_edge_vertices[index]);
-		m_edge_vertices[index].edgex = 0.5;
-		m_edge_vertices[index].edgey = TyOne;
-		++index;
-		m_terrain_edge_indexs[ter] = index;
-	}
-}
-
-void GameRendererGL::collect_terrain_dither(bool onlyscan)
-{
-	const Map & map = m_egbase->map();
-	const World & world = m_egbase->world();
-
-	for (int32_t fy = m_minfy; fy <= m_maxfy; ++fy) {
-		for (int32_t fx = m_minfx; fx <= m_maxfx; ++fx) {
-			Coords ncoords(fx, fy);
-			map.normalize_coords(ncoords);
-			FCoords fcoords = map.get_fcoords(ncoords);
-
-			TerrainIndex ter_d = fcoords.field->get_terrains().d;
-			TerrainIndex ter_r = fcoords.field->get_terrains().r;
-			TerrainIndex ter_u = map.tr_n(fcoords).field->get_terrains().d;
-			TerrainIndex ter_rr = map.r_n(fcoords).field->get_terrains().d;
-			TerrainIndex ter_l = map.l_n(fcoords).field->get_terrains().r;
-			TerrainIndex ter_dd = map.bl_n(fcoords).field->get_terrains().r;
-			int32_t lyr_d = world.terrain_descr(ter_d).dither_layer();
-			int32_t lyr_r = world.terrain_descr(ter_r).dither_layer();
-			int32_t lyr_u = world.terrain_descr(ter_u).dither_layer();
-			int32_t lyr_rr = world.terrain_descr(ter_rr).dither_layer();
-			int32_t lyr_l = world.terrain_descr(ter_l).dither_layer();
-			int32_t lyr_dd = world.terrain_descr(ter_dd).dither_layer();
-
-			Coords f(fx, fy);
-			Coords rn(fx + 1, fy);
-			Coords brn(fx + (fy & 1), fy + 1);
-			Coords bln(brn.x - 1, brn.y);
-
-			if (lyr_r > lyr_d) {
-				add_terrain_dither_triangle(onlyscan, ter_r, brn, f, bln);
-			} else if (ter_d != ter_r) {
-				add_terrain_dither_triangle(onlyscan, ter_d, f, brn, rn);
-			}
-			if ((lyr_u > lyr_r) || (lyr_u == lyr_r && ter_u != ter_r)) {
-				add_terrain_dither_triangle(onlyscan, ter_u, rn, f, brn);
-			}
-			if (lyr_rr > lyr_r) {
-				add_terrain_dither_triangle(onlyscan, ter_rr, brn, rn, f);
-			}
-			if ((lyr_l > lyr_d) || (lyr_l == lyr_d && ter_l != ter_d)) {
-				add_terrain_dither_triangle(onlyscan, ter_l, f, bln, brn);
-			}
-			if (lyr_dd > lyr_d) {
-				add_terrain_dither_triangle(onlyscan, ter_dd, bln, brn, f);
-			}
-		}
-	}
-}
-
-/*
- * Schematic of triangle neighborhood:
- *
- *               *
- *              / \
- *             / u \
- *         (f)/     \
- *    *------*------* (r)
- *     \  l / \  r / \
- *      \  /   \  /   \
- *       \/  d  \/ rr  \
- *       *------*------* (br)
- *        \ dd /
- *         \  /
- *          \/
- *          *
- */
-void GameRendererGL::prepare_terrain_dither()
-{
-	static_assert(sizeof(DitherVertex) == 32, "assert(sizeof(dithervertex) == 32) failed.");
-
-	m_terrain_edge_freq.assign(m_terrain_edge_freq.size(), 0);
-
-	collect_terrain_dither(true);
-
-	uint32_t nrtriangles = 0;
-	m_terrain_edge_freq_cum.resize(m_terrain_edge_freq.size());
-	for (TerrainIndex ter = 0; ter < m_terrain_edge_freq.size(); ++ter) {
-		m_terrain_edge_freq_cum[ter] = nrtriangles;
-		nrtriangles += m_terrain_edge_freq[ter];
-	}
-
-	if (3 * nrtriangles > m_edge_vertices_size) {
-		m_edge_vertices.reset(new DitherVertex[3 * nrtriangles]);
-		m_edge_vertices_size = 3 * nrtriangles;
-	}
-
-	m_terrain_edge_indexs.resize(m_terrain_edge_freq_cum.size());
-	for (TerrainIndex ter = 0; ter < m_terrain_edge_freq.size(); ++ter)
-		m_terrain_edge_indexs[ter] = 3 * m_terrain_edge_freq_cum[ter];
-
-	collect_terrain_dither(false);
-
-	for (TerrainIndex ter = 0; ter < m_terrain_edge_freq.size(); ++ter) {
-		assert(m_terrain_edge_indexs[ter] == 3 * (m_terrain_edge_freq_cum[ter] + m_terrain_edge_freq[ter]));
-	}
-}
-
-void GameRendererGL::draw_terrain_dither()
-{
-	if (m_edge_vertices_size == 0)
-		return;
-
-	glVertexPointer(2, GL_FLOAT, sizeof(DitherVertex), &m_edge_vertices[0].x);
-	glTexCoordPointer(2, GL_FLOAT, sizeof(DitherVertex), &m_edge_vertices[0].tcx);
-	glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(DitherVertex), &m_edge_vertices[0].color);
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glEnableClientState(GL_COLOR_ARRAY);
-
-	glActiveTextureARB(GL_TEXTURE1_ARB);
-	glClientActiveTextureARB(GL_TEXTURE1_ARB);
-	glTexCoordPointer(2, GL_FLOAT, sizeof(DitherVertex), &m_edge_vertices[0].edgex);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	GLuint edge = get_dither_edge_texture()->get_gl_texture();
-	glBindTexture(GL_TEXTURE_2D, edge);
-	glEnable(GL_TEXTURE_2D);
-	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
-	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
-	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
-	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
-	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
-	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
-	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
-	glActiveTextureARB(GL_TEXTURE0_ARB);
-	glClientActiveTextureARB(GL_TEXTURE0_ARB);
-
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
-
-	for (TerrainIndex ter = 0; ter < m_terrain_freq.size(); ++ter) {
-		if (!m_terrain_edge_freq[ter])
-			continue;
-
-		const Texture & texture =
-				*g_gr->get_maptexture_data
-					(m_egbase->world().terrain_descr(ter).get_texture());
-		glBindTexture(GL_TEXTURE_2D, texture.get_texture());
-		glDrawArrays
-			(GL_TRIANGLES,
-			 3 * m_terrain_edge_freq_cum[ter], 3 * m_terrain_edge_freq[ter]);
-	}
-
-	glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_COLOR_ARRAY);
-	glActiveTextureARB(GL_TEXTURE1_ARB);
-	glClientActiveTextureARB(GL_TEXTURE1_ARB);
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisable(GL_TEXTURE_2D);
-	glActiveTextureARB(GL_TEXTURE0_ARB);
-	glClientActiveTextureARB(GL_TEXTURE0_ARB);
-}
-
-uint8_t GameRendererGL::field_roads(const FCoords & coords) const
-{
+			}
+		}
+	}
+	return brightness / 255.;
+}
+
+// Returns the road that should be rendered here. The format is like in field,
+// but this is not the physically present road, but the one that should be
+// drawn (i.e. taking into account if there is fog of war involved or road
+// building overlays enabled).
+uint8_t field_roads(const FCoords& coords, const Map& map, const Player* const player) {
 	uint8_t roads;
-	const Map & map = m_egbase->map();
-	if (m_player && !m_player->see_all()) {
-		const Player::Field & pf = m_player->fields()[Map::get_index(coords, map.get_width())];
+	if (player && !player->see_all()) {
+		const Player::Field& pf = player->fields()[Map::get_index(coords, map.get_width())];
 		roads = pf.roads | map.overlay_manager().get_road_overlay(coords);
 	} else {
 		roads = coords.field->get_roads();
@@ -503,159 +82,111 @@
 	return roads;
 }
 
-void GameRendererGL::prepare_roads()
-{
-	const Map & map = m_egbase->map();
-
-	m_road_freq[0] = 0;
-	m_road_freq[1] = 0;
-
-	for (int32_t fy = m_minfy; fy <= m_maxfy; ++fy) {
-		for (int32_t fx = m_minfx; fx <= m_maxfx; ++fx) {
-			Coords ncoords(fx, fy);
-			map.normalize_coords(ncoords);
-			FCoords fcoords = map.get_fcoords(ncoords);
-			uint8_t roads = field_roads(fcoords);
-
-			for (int dir = 0; dir < 3; ++dir) {
-				uint8_t road = (roads >> (2 * dir)) & Road_Mask;
-				if (road >= Road_Normal && road <= Road_Busy) {
-					++m_road_freq[road - Road_Normal];
-				}
-			}
-		}
-	}
-
-	uint32_t nrquads = m_road_freq[0] + m_road_freq[1];
-	if (4 * nrquads > m_road_vertices_size) {
-		m_road_vertices.reset(new BaseVertex[4 * nrquads]);
-		m_road_vertices_size = 4 * nrquads;
-	}
-
-	uint32_t indexs[2];
-	indexs[0] = 0;
-	indexs[1] = 4 * m_road_freq[0];
-
-	for (int32_t fy = m_minfy; fy <= m_maxfy; ++fy) {
-		for (int32_t fx = m_minfx; fx <= m_maxfx; ++fx) {
-			Coords ncoords(fx, fy);
-			map.normalize_coords(ncoords);
-			FCoords fcoords = map.get_fcoords(ncoords);
-			uint8_t roads = field_roads(fcoords);
-
-			uint8_t road = (roads >> Road_East) & Road_Mask;
-			if (road >= Road_Normal && road <= Road_Busy) {
-				uint32_t index = indexs[road - Road_Normal];
-				BaseVertex start, end;
-				compute_basevertex(Coords(fx, fy), start);
-				compute_basevertex(Coords(fx + 1, fy), end);
-				m_road_vertices[index] = start;
-				m_road_vertices[index].y -= 2;
-				m_road_vertices[index].tcy -= 2.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = start;
-				m_road_vertices[index].y += 2;
-				m_road_vertices[index].tcy += 2.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = end;
-				m_road_vertices[index].y += 2;
-				m_road_vertices[index].tcy += 2.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = end;
-				m_road_vertices[index].y -= 2;
-				m_road_vertices[index].tcy -= 2.0 / TEXTURE_HEIGHT;
-				++index;
-				indexs[road - Road_Normal] = index;
-			}
-
-			road = (roads >> Road_SouthEast) & Road_Mask;
-			if (road >= Road_Normal && road <= Road_Busy) {
-				uint32_t index = indexs[road - Road_Normal];
-				BaseVertex start, end;
-				compute_basevertex(Coords(fx, fy), start);
-				compute_basevertex(Coords(fx + (fy & 1), fy + 1), end);
-				m_road_vertices[index] = start;
-				m_road_vertices[index].x += 3;
-				m_road_vertices[index].tcx += 3.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = start;
-				m_road_vertices[index].x -= 3;
-				m_road_vertices[index].tcx -= 3.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = end;
-				m_road_vertices[index].x -= 3;
-				m_road_vertices[index].tcx -= 3.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = end;
-				m_road_vertices[index].x += 3;
-				m_road_vertices[index].tcx += 3.0 / TEXTURE_HEIGHT;
-				++index;
-				indexs[road - Road_Normal] = index;
-			}
-
-			road = (roads >> Road_SouthWest) & Road_Mask;
-			if (road >= Road_Normal && road <= Road_Busy) {
-				uint32_t index = indexs[road - Road_Normal];
-				BaseVertex start, end;
-				compute_basevertex(Coords(fx, fy), start);
-				compute_basevertex(Coords(fx + (fy & 1) - 1, fy + 1), end);
-				m_road_vertices[index] = start;
-				m_road_vertices[index].x += 3;
-				m_road_vertices[index].tcx += 3.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = start;
-				m_road_vertices[index].x -= 3;
-				m_road_vertices[index].tcx -= 3.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = end;
-				m_road_vertices[index].x -= 3;
-				m_road_vertices[index].tcx -= 3.0 / TEXTURE_HEIGHT;
-				++index;
-				m_road_vertices[index] = end;
-				m_road_vertices[index].x += 3;
-				m_road_vertices[index].tcx += 3.0 / TEXTURE_HEIGHT;
-				++index;
-				indexs[road - Road_Normal] = index;
-			}
-		}
-	}
-
-	assert(indexs[0] == 4 * m_road_freq[0]);
-	assert(indexs[1] == 4 * nrquads);
-}
-
-void GameRendererGL::draw_roads()
-{
-	if (!m_road_freq[0] && !m_road_freq[1])
+}  // namespace
+
+// Explanation of how drawing works:
+// Schematic of triangle neighborhood:
+//
+//               *
+//              / \
+//             / u \
+//         (f)/     \
+//    *------*------* (r)
+//     \  l / \  r / \
+//      \  /   \  /   \
+//       \/  d  \/ rr  \
+//       *------*------* (br)
+//        \ dd /
+//         \  /
+//          \/
+//          *
+//
+// Each field (f) owns two triangles: (r)ight & (d)own. When we look at the
+// field, we have to make sure to schedule drawing the triangles. This is done
+// by of these triangles is done by TerrainProgram.
+//
+// To draw dithered edges, we have to look at the neighboring triangles for the
+// two triangles too: If a neighboring triangle has another texture and our
+// dither layer is smaller, we have to draw a dithering triangle too - this lets the neighboring
+// texture
+// bleed into our triangle.
+//
+// The dither triangle is the triangle that should be partially (either r or
+// d). Example: if r and d have different textures and r.dither_layer >
+// d.dither_layer, then we will repaint d with the dither texture as mask.
+
+
+std::unique_ptr<TerrainProgram> GlGameRenderer::terrain_program_;
+std::unique_ptr<DitherProgram> GlGameRenderer::dither_program_;
+std::unique_ptr<RoadProgram> GlGameRenderer::road_program_;
+
+GlGameRenderer::GlGameRenderer()  {
+}
+
+GlGameRenderer::~GlGameRenderer() {
+}
+
+void GlGameRenderer::draw() {
+	if (terrain_program_ == nullptr) {
+		terrain_program_.reset(new TerrainProgram());
+		dither_program_.reset(new DitherProgram());
+		road_program_.reset(new RoadProgram());
+	}
+
+	GLSurface* surface = dynamic_cast<GLSurface*>(m_dst->get_surface());
+	if (!surface)
 		return;
 
-	GLuint rt_normal =
-		dynamic_cast<const GLSurfaceTexture &>
-		(g_gr->get_road_texture(Widelands::Road_Normal)).get_gl_texture();
-	GLuint rt_busy =
-		dynamic_cast<const GLSurfaceTexture &>
-		(g_gr->get_road_texture(Widelands::Road_Busy)).get_gl_texture();
-
-	glVertexPointer(2, GL_FLOAT, sizeof(BaseVertex), &m_road_vertices[0].x);
-	glTexCoordPointer(2, GL_FLOAT, sizeof(BaseVertex), &m_road_vertices[0].tcx);
-	glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(BaseVertex), &m_road_vertices[0].color);
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glEnableClientState(GL_COLOR_ARRAY);
-
-	glDisable(GL_BLEND);
-
-	if (m_road_freq[0]) {
-		glBindTexture(GL_TEXTURE_2D, rt_normal);
-		glDrawArrays(GL_QUADS, 0, 4 * m_road_freq[0]);
-	}
-
-	if (m_road_freq[1]) {
-		glBindTexture(GL_TEXTURE_2D, rt_busy);
-		glDrawArrays(GL_QUADS, 4 * m_road_freq[0], 4 * m_road_freq[1]);
-	}
-
-	glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_COLOR_ARRAY);
+	const Rect& bounding_rect = m_dst->get_rect();
+	const Point surface_offset = m_dst_offset + bounding_rect.top_left() + m_dst->get_offset();
+
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	glScissor(bounding_rect.x,
+	          surface->height() - bounding_rect.y - bounding_rect.h,
+	          bounding_rect.w,
+	          bounding_rect.h);
+	glEnable(GL_SCISSOR_TEST);
+
+	Map& map = m_egbase->map();
+	const uint32_t gametime = m_egbase->get_gametime();
+
+	FieldsToDraw fields_to_draw(m_minfx, m_maxfx, m_minfy, m_maxfy);
+	for (int32_t fy = m_minfy; fy <= m_maxfy; ++fy) {
+		for (int32_t fx = m_minfx; fx <= m_maxfx; ++fx) {
+			FieldsToDraw::Field& f =
+			   *fields_to_draw.mutable_field(fields_to_draw.calculate_index(fx, fy));
+
+			f.fx = fx;
+			f.fy = fy;
+
+			Coords coords(fx, fy);
+			int x, y;
+			MapviewPixelFunctions::get_basepix(coords, x, y);
+
+			map.normalize_coords(coords);
+			const FCoords& fcoords = map.get_fcoords(coords);
+
+			f.texture_x = float(x) / TEXTURE_WIDTH;
+			f.texture_y = float(y) / TEXTURE_HEIGHT;
+			f.x = x + surface_offset.x;
+			f.y = y + surface_offset.y - fcoords.field->get_height() * HEIGHT_FACTOR;
+
+			f.ter_d = fcoords.field->terrain_d();
+			f.ter_r = fcoords.field->terrain_r();
+
+			f.brightness = field_brightness(fcoords, gametime, map, m_player);
+
+			f.roads = field_roads(fcoords, map, m_player);
+		}
+	}
+
+	const World& world = m_egbase->world();
+	terrain_program_->draw(world.terrains(), fields_to_draw);
+	dither_program_->draw(world.terrains(), fields_to_draw);
+	road_program_->draw(fields_to_draw);
+
+	draw_objects();
+
+	glDisable(GL_SCISSOR_TEST);
 }

=== renamed file 'src/graphic/render/gamerenderer_gl.h' => 'src/graphic/gl/game_renderer.h'
--- src/graphic/render/gamerenderer_gl.h	2014-09-14 16:08:13 +0000
+++ src/graphic/gl/game_renderer.h	2014-11-01 20:50:47 +0000
@@ -17,112 +17,32 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_GAMERENDERER_GL_H
-#define WL_GRAPHIC_RENDER_GAMERENDERER_GL_H
+#ifndef WL_GRAPHIC_GL_GAME_RENDERER_H
+#define WL_GRAPHIC_GL_GAME_RENDERER_H
 
 #include <memory>
-#include <vector>
-
-#include "base/rect.h"
-#include "graphic/render/gamerenderer.h"
-#include "logic/widelands.h"
-
-namespace Widelands {
-struct Coords;
-struct FCoords;
-class World;
-}
-
-class GLSurface;
-class GLSurfaceTexture;
+
+#include "graphic/game_renderer.h"
+#include "graphic/gl/utils.h"
+
+class TerrainProgram;
+class DitherProgram;
+class RoadProgram;
 
 /**
  * OpenGL implementation of @ref GameRenderer.
  */
-class GameRendererGL : public GameRenderer {
+class GlGameRenderer : public GameRenderer {
 public:
-	GameRendererGL();
-	virtual ~GameRendererGL();
+	GlGameRenderer();
+	virtual ~GlGameRenderer();
 
 private:
-	struct BaseVertex {
-		float x;
-		float y;
-		float tcx;
-		float tcy;
-		uint8_t color[4];
-		uint32_t pad[3];
-	};
-
-	struct DitherVertex {
-		float x;
-		float y;
-		float tcx;
-		float tcy;
-		float edgex;
-		float edgey;
-		uint8_t color[4];
-		uint32_t pad[1];
-	};
-
-	const GLSurfaceTexture * get_dither_edge_texture();
+	static std::unique_ptr<TerrainProgram> terrain_program_;
+	static std::unique_ptr<DitherProgram> dither_program_;
+	static std::unique_ptr<RoadProgram> road_program_;
 
 	void draw() override;
-	void prepare_terrain_base();
-	void collect_terrain_base(bool onlyscan);
-	void count_terrain_base(Widelands::TerrainIndex ter);
-	void add_terrain_base_triangle
-		(Widelands::TerrainIndex ter,
-		 const Widelands::Coords & p1, const Widelands::Coords & p2, const Widelands::Coords & p3);
-	void draw_terrain_base();
-	void prepare_terrain_dither();
-	void collect_terrain_dither(bool onlyscan);
-	void add_terrain_dither_triangle
-		(bool onlyscan, Widelands::TerrainIndex ter,
-		 const Widelands::Coords & edge1, const Widelands::Coords & edge2,
-		 const Widelands::Coords & opposite);
-	void draw_terrain_dither();
-	void prepare_roads();
-	void draw_roads();
-
-	uint32_t patch_index(const Widelands::Coords & f) const;
-	uint8_t field_brightness(const Widelands::FCoords & coords) const;
-	uint8_t field_roads(const Widelands::FCoords & coords) const;
-	template<typename vertex>
-	void compute_basevertex(const Widelands::Coords & coords, vertex & vtx) const;
-
-	/**
-	 * The following variables are only valid during rendering.
-	 */
-	/*@{*/
-	GLSurface * m_surface;
-
-	/// Bounding rectangle inside the destination surface
-	Rect m_rect;
-
-	/// Translation from map pixel coordinates to surface pixel coordinates
-	/// (relative to the top-left corner of the surface, @b not relative
-	/// to the bounding rectangle)
-	Point m_surface_offset;
-
-	Rect m_patch_size;
-	std::unique_ptr<BaseVertex[]> m_patch_vertices;
-	uint32_t m_patch_vertices_size;
-	std::unique_ptr<uint16_t[]> m_patch_indices;
-	uint32_t m_patch_indices_size;
-	std::vector<uint32_t> m_patch_indices_indexs;
-	std::vector<uint32_t> m_terrain_freq;
-	std::vector<uint32_t> m_terrain_freq_cum;
-	std::unique_ptr<DitherVertex[]> m_edge_vertices;
-	uint32_t m_edge_vertices_size;
-	std::vector<uint32_t> m_terrain_edge_freq;
-	std::vector<uint32_t> m_terrain_edge_freq_cum;
-	std::vector<uint32_t> m_terrain_edge_indexs;
-
-	uint32_t m_road_freq[2];
-	std::unique_ptr<BaseVertex[]> m_road_vertices;
-	uint32_t m_road_vertices_size;
-	/*@}*/
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GAMERENDERER_GL_H
+#endif  // end of include guard: WL_GRAPHIC_GL_GAME_RENDERER_H

=== added file 'src/graphic/gl/road_program.cc'
--- src/graphic/gl/road_program.cc	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/road_program.cc	2014-11-01 20:50:47 +0000
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "graphic/gl/road_program.h"
+
+#include <cmath>
+
+#include "base/log.h"
+#include "graphic/gl/fields_to_draw.h"
+#include "graphic/graphic.h"
+#include "graphic/texture.h"
+#include "logic/roadtype.h"
+
+namespace  {
+
+// We target OpenGL 2.1 for the desktop here.
+// TODO(sirver): In the end we need to replace gl_ProjectionMatrix. It is not
+// supported in ES and more modern Open GL version.
+const char kRoadVertexShader[] = R"(
+#version 120
+
+// Attributes.
+attribute vec2 attr_position;
+attribute vec2 attr_texture_position;
+attribute float attr_texture_mix;
+attribute float attr_brightness;
+
+// Outputs.
+varying vec2 out_texture_position;
+varying float out_texture_mix;
+varying float out_brightness;
+
+void main() {
+	out_texture_position = attr_texture_position;
+	out_texture_mix = attr_texture_mix;
+	out_brightness = attr_brightness;
+	gl_Position = gl_ProjectionMatrix * vec4(attr_position, 0., 1.);
+}
+)";
+
+const char kRoadFragmentShader[] = R"(
+#version 120
+
+// Inputs.
+varying vec2 out_texture_position;
+varying float out_texture_mix;
+varying float out_brightness;
+
+uniform sampler2D u_normal_road_texture;
+uniform sampler2D u_busy_road_texture;
+
+void main() {
+	vec4 normal_road_color = texture2D(u_normal_road_texture, out_texture_position);
+	vec4 busy_road_color = texture2D(u_busy_road_texture, out_texture_position);
+	vec4 color = mix(normal_road_color, busy_road_color, out_texture_mix);
+	color.rgb *= out_brightness;
+	gl_FragColor = color;
+}
+)";
+
+}  // namespace
+
+RoadProgram::RoadProgram() {
+	gl_program_.build(kRoadVertexShader, kRoadFragmentShader);
+
+	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
+	attr_texture_position_ =
+		glGetAttribLocation(gl_program_.object(), "attr_texture_position");
+	attr_texture_mix_ = glGetAttribLocation(gl_program_.object(), "attr_texture_mix");
+	attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
+
+	u_normal_road_texture_ = glGetUniformLocation(gl_program_.object(), "u_normal_road_texture");
+	u_busy_road_texture_ = glGetUniformLocation(gl_program_.object(), "u_busy_road_texture");
+}
+
+void RoadProgram::add_road(const FieldsToDraw::Field& start,
+                           const FieldsToDraw::Field& end,
+                           const Widelands::RoadType road_type) {
+	static constexpr float kRoadThicknessInPixels = 3.;
+	const float delta_x = end.x - start.x;
+	const float delta_y = end.y - start.y;
+	const float vector_length = std::hypot(delta_x, delta_y);
+
+	// Find the reciprocal unit vector, so that we can calculate start and end
+	// points for the quad that will make the road.
+	const float dx = -delta_y / vector_length;
+	const float dy = delta_x / vector_length;
+
+	const float texture_mix = road_type == Widelands::Road_Normal ? 0. : 1.;
+	const PerVertexData p1 = {
+	   start.x + kRoadThicknessInPixels * dx, start.y + kRoadThicknessInPixels * dy,
+		0., 0.,
+		start.brightness,
+		texture_mix,
+	};
+	const PerVertexData p2 = {
+	   start.x - kRoadThicknessInPixels * dx, start.y - kRoadThicknessInPixels * dy,
+		0., 1.,
+		start.brightness,
+		texture_mix,
+	};
+	const PerVertexData p3 = {
+	   end.x + kRoadThicknessInPixels * dx, end.y + kRoadThicknessInPixels * dy,
+		1., 0.,
+		end.brightness,
+		texture_mix,
+	};
+	const PerVertexData p4 = {
+	   end.x - kRoadThicknessInPixels * dx, end.y - kRoadThicknessInPixels * dy,
+		1., 1.,
+		end.brightness,
+		texture_mix,
+	};
+
+	// As OpenGl does not support drawing quads in modern days and we have a
+	// bunch of roads that might not be neighbored, we need to add two triangles
+	// for each road. :(. Another alternative would be to use primitive restart,
+	// but that is a fairly recent OpenGL feature.
+	vertices_.emplace_back(p1);
+	vertices_.emplace_back(p2);
+	vertices_.emplace_back(p3);
+	vertices_.emplace_back(p2);
+	vertices_.emplace_back(p3);
+	vertices_.emplace_back(p4);
+}
+
+void RoadProgram::draw(const FieldsToDraw& fields_to_draw) {
+	vertices_.clear();
+
+	for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
+		const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
+
+		// Road to right neighbor.
+		const int rn_index = fields_to_draw.calculate_index(field.fx + 1, field.fy);
+		if (rn_index != -1) {
+			const Widelands::RoadType road =
+			   static_cast<Widelands::RoadType>(field.roads & Widelands::Road_Mask);
+			if (road != Widelands::Road_None) {
+				add_road(field, fields_to_draw.at(rn_index), road);
+			}
+		}
+
+		// Road to bottom right neighbor.
+		const int brn_index = fields_to_draw.calculate_index(field.fx + (field.fy & 1), field.fy + 1);
+		if (brn_index != -1) {
+			const Widelands::RoadType road =
+			   static_cast<Widelands::RoadType>((field.roads >> 2) & Widelands::Road_Mask);
+			if (road != Widelands::Road_None) {
+				add_road(field, fields_to_draw.at(brn_index), road);
+			}
+		}
+
+		// Road to bottom right neighbor.
+		const int bln_index =
+		   fields_to_draw.calculate_index(field.fx + (field.fy & 1) - 1, field.fy + 1);
+		if (brn_index != -1) {
+			const Widelands::RoadType road =
+			   static_cast<Widelands::RoadType>((field.roads >> 4) & Widelands::Road_Mask);
+			if (road != Widelands::Road_None) {
+				add_road(field, fields_to_draw.at(bln_index), road);
+			}
+		}
+	}
+
+	glUseProgram(gl_program_.object());
+
+	glEnableVertexAttribArray(attr_position_);
+	glEnableVertexAttribArray(attr_texture_position_);
+	glEnableVertexAttribArray(attr_brightness_);
+	glEnableVertexAttribArray(attr_texture_mix_);
+
+	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
+	glBufferData(
+	   GL_ARRAY_BUFFER, sizeof(PerVertexData) * vertices_.size(), vertices_.data(), GL_STREAM_DRAW);
+
+	const auto set_attrib_pointer = [](const int vertex_index, int num_items, int offset) {
+		glVertexAttribPointer(vertex_index,
+		                      num_items,
+		                      GL_FLOAT,
+		                      GL_FALSE,
+		                      sizeof(PerVertexData),
+		                      reinterpret_cast<void*>(offset));
+	};
+	set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, x));
+	set_attrib_pointer(attr_texture_position_, 2, offsetof(PerVertexData, texture_x));
+	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
+	set_attrib_pointer(attr_texture_mix_, 1, offsetof(PerVertexData, texture_mix));
+
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	// Bind the textures.
+	const GLuint rt_normal = dynamic_cast<const GLSurfaceTexture&>(
+	                      g_gr->get_road_texture(Widelands::Road_Normal)).get_gl_texture();
+	const GLuint rt_busy = dynamic_cast<const GLSurfaceTexture&>(
+	                    g_gr->get_road_texture(Widelands::Road_Busy)).get_gl_texture();
+
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, rt_normal);
+	glUniform1i(u_normal_road_texture_, 0);
+
+	glActiveTexture(GL_TEXTURE1);
+	glBindTexture(GL_TEXTURE_2D, rt_busy);
+	glUniform1i(u_busy_road_texture_, 1);
+
+	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
+
+	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_texture_position_);
+	glDisableVertexAttribArray(attr_brightness_);
+	glDisableVertexAttribArray(attr_texture_mix_);
+
+	glActiveTexture(GL_TEXTURE0);
+
+	glUseProgram(0);
+
+}

=== added file 'src/graphic/gl/road_program.h'
--- src/graphic/gl/road_program.h	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/road_program.h	2014-11-01 20:50:47 +0000
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_GL_ROAD_PROGRAM_H
+#define WL_GRAPHIC_GL_ROAD_PROGRAM_H
+
+#include <vector>
+
+#include "base/macros.h"
+#include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/utils.h"
+#include "logic/roadtype.h"
+
+class RoadProgram {
+public:
+	// Compiles the program. Throws on error.
+	RoadProgram();
+
+	// Draws the roads.
+	void draw(const FieldsToDraw& fields_to_draw);
+
+private:
+	struct PerVertexData {
+		float x;
+		float y;
+		float texture_x;
+		float texture_y;
+		float brightness;
+
+		// This is a hack: we want to draw busy and normal roads in the same
+		// run, but since samplers (apparently?) cannot be passed through
+		// attribute arrays, we instead sample twice (busy and normal) and mix
+		// them together with 'texture_mix' which is either 1 or 0.
+		float texture_mix;
+	};
+	static_assert(sizeof(PerVertexData) == 24, "Wrong padding.");
+
+	// Adds a road from 'start' to 'end' to be rendered in this frame using the
+	// correct texture for 'road_type'.
+	void add_road(const FieldsToDraw::Field& start,
+	              const FieldsToDraw::Field& end,
+	              const Widelands::RoadType road_type);
+
+	// The buffer that will contain 'vertices_' for rendering.
+	Gl::Buffer gl_array_buffer_;
+
+	// The program used for drawing the roads.
+	Gl::Program gl_program_;
+
+	// Attributes.
+	GLint attr_position_;
+	GLint attr_texture_position_;
+	GLint attr_brightness_;
+	GLint attr_texture_mix_;
+
+	// Uniforms.
+	GLint u_normal_road_texture_;
+	GLint u_busy_road_texture_;
+
+	// All vertices that get rendered this frame.
+	std::vector<PerVertexData> vertices_;
+
+	DISALLOW_COPY_AND_ASSIGN(RoadProgram);
+};
+
+#endif  // end of include guard: WL_GRAPHIC_GL_ROAD_PROGRAM_H

=== renamed file 'src/graphic/render/gl_surface.cc' => 'src/graphic/gl/surface.cc'
--- src/graphic/render/gl_surface.cc	2014-07-14 10:45:44 +0000
+++ src/graphic/gl/surface.cc	2014-11-01 20:50:47 +0000
@@ -17,15 +17,15 @@
  *
  */
 
-#include "graphic/render/gl_surface.h"
+#include "graphic/gl/surface.h"
 
 #include <cassert>
 #include <cmath>
 #include <cstdlib>
 
 #include "base/macros.h"
+#include "graphic/gl/surface_texture.h"
 #include "graphic/graphic.h"
-#include "graphic/render/gl_surface_texture.h"
 
 uint16_t GLSurface::width() const {
 	return m_w;

=== renamed file 'src/graphic/render/gl_surface.h' => 'src/graphic/gl/surface.h'
--- src/graphic/render/gl_surface.h	2014-07-26 10:43:23 +0000
+++ src/graphic/gl/surface.h	2014-11-01 20:50:47 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_GL_SURFACE_H
-#define WL_GRAPHIC_RENDER_GL_SURFACE_H
+#ifndef WL_GRAPHIC_GL_SURFACE_H
+#define WL_GRAPHIC_GL_SURFACE_H
 
 #include <memory>
 
@@ -56,4 +56,4 @@
 	std::unique_ptr<uint8_t[]> m_pixels;
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GL_SURFACE_H
+#endif  // end of include guard: WL_GRAPHIC_GL_SURFACE_H

=== renamed file 'src/graphic/render/gl_surface_screen.cc' => 'src/graphic/gl/surface_screen.cc'
--- src/graphic/render/gl_surface_screen.cc	2014-07-25 11:14:03 +0000
+++ src/graphic/gl/surface_screen.cc	2014-11-01 20:50:47 +0000
@@ -16,12 +16,12 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#include "graphic/render/gl_surface_screen.h"
+#include "graphic/gl/surface_screen.h"
 
 #include <algorithm>
 #include <cassert>
 
-#include "graphic/render/gl_utils.h"
+#include "graphic/gl/utils.h"
 
 GLSurfaceScreen::GLSurfaceScreen(uint16_t w, uint16_t h)
 {
@@ -49,7 +49,7 @@
 }
 
 const SDL_PixelFormat & GLSurfaceScreen::format() const {
-	return gl_rgba_format();
+	return Gl::gl_rgba_format();
 }
 
 void GLSurfaceScreen::lock(Surface::LockMode mode)

=== renamed file 'src/graphic/render/gl_surface_screen.h' => 'src/graphic/gl/surface_screen.h'
--- src/graphic/render/gl_surface_screen.h	2014-07-26 10:43:23 +0000
+++ src/graphic/gl/surface_screen.h	2014-11-01 20:50:47 +0000
@@ -16,10 +16,10 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#ifndef WL_GRAPHIC_RENDER_GL_SURFACE_SCREEN_H
-#define WL_GRAPHIC_RENDER_GL_SURFACE_SCREEN_H
+#ifndef WL_GRAPHIC_GL_SURFACE_SCREEN_H
+#define WL_GRAPHIC_GL_SURFACE_SCREEN_H
 
-#include "graphic/render/gl_surface.h"
+#include "graphic/gl/surface.h"
 
 /**
  * This surface represents the screen in OpenGL mode.
@@ -39,4 +39,4 @@
 	void swap_rows();
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GL_SURFACE_SCREEN_H
+#endif  // end of include guard:

=== renamed file 'src/graphic/render/gl_surface_texture.cc' => 'src/graphic/gl/surface_texture.cc'
--- src/graphic/render/gl_surface_texture.cc	2014-09-20 09:37:47 +0000
+++ src/graphic/gl/surface_texture.cc	2014-11-01 20:50:47 +0000
@@ -16,19 +16,34 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#include "graphic/render/gl_surface_texture.h"
+#include "graphic/gl/surface_texture.h"
 
 #include <cassert>
 
 #include "base/wexception.h"
-#include "gl_surface.h"  // for glew.h
+#include "graphic/gl/surface.h"
+#include "graphic/gl/utils.h"
 #include "graphic/graphic.h"
-#include "graphic/render/gl_utils.h"
 
 GLuint GLSurfaceTexture::gl_framebuffer_id_;
 
 static bool use_arb_;
 
+namespace  {
+
+// Return the smallest power of two greater than or equal to \p x.
+uint32_t next_power_of_two(uint32_t x)
+{
+	uint32_t pot = 1;
+
+	while (pot < x)
+		pot *= 2;
+
+	return pot;
+}
+
+}  // namespace
+
 /**
  * Initial global resources needed for fast offscreen rendering.
  */
@@ -160,7 +175,6 @@
 	SDL_FreeSurface(surface);
 
 	glPopAttrib();
-	handle_glerror();
 }
 
 GLSurfaceTexture::~GLSurfaceTexture()
@@ -170,7 +184,6 @@
 
 void GLSurfaceTexture::init(uint16_t w, uint16_t h)
 {
-	handle_glerror();
 	m_w = w;
 	m_h = h;
 	if (m_w <= 0 || m_h <= 0) {
@@ -193,12 +206,10 @@
 	// makes no difference
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-	handle_glerror();
 }
 
 const SDL_PixelFormat & GLSurfaceTexture::format() const {
-	return gl_rgba_format();
+	return Gl::gl_rgba_format();
 }
 
 void GLSurfaceTexture::lock(LockMode mode) {

=== renamed file 'src/graphic/render/gl_surface_texture.h' => 'src/graphic/gl/surface_texture.h'
--- src/graphic/render/gl_surface_texture.h	2014-09-20 09:37:47 +0000
+++ src/graphic/gl/surface_texture.h	2014-11-01 20:50:47 +0000
@@ -16,10 +16,10 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#ifndef WL_GRAPHIC_RENDER_GL_SURFACE_TEXTURE_H
-#define WL_GRAPHIC_RENDER_GL_SURFACE_TEXTURE_H
+#ifndef WL_GRAPHIC_GL_SURFACE_TEXTURE_H
+#define WL_GRAPHIC_GL_SURFACE_TEXTURE_H
 
-#include "graphic/render/gl_surface.h"
+#include "graphic/gl/surface.h"
 
 struct SDL_Surface;
 
@@ -73,4 +73,4 @@
 	uint16_t m_tex_w, m_tex_h;
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GL_SURFACE_TEXTURE_H
+#endif  // end of include guard: WL_GRAPHIC_GL_SURFACE_TEXTURE_H

=== added file 'src/graphic/gl/terrain_program.cc'
--- src/graphic/gl/terrain_program.cc	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/terrain_program.cc	2014-11-01 20:50:47 +0000
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "graphic/gl/terrain_program.h"
+
+#include "graphic/gl/fields_to_draw.h"
+#include "graphic/graphic.h"
+#include "graphic/texture.h"
+
+namespace  {
+
+using namespace Widelands;
+
+// QuickRef:
+// http://www.cs.unh.edu/~cs770/docs/glsl-1.20-quickref.pdf
+// Full specification:
+// http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf
+// We target OpenGL 2.1 for the desktop here.
+// TODO(sirver): In the end we need to replace gl_ProjectionMatrix. It is not
+// supported in ES and more modern Open GL version.
+const char kTerrainVertexShader[] = R"(
+#version 120
+
+// Attributes.
+attribute vec2 attr_position;
+attribute float attr_brightness;
+attribute vec2 attr_texture_position;
+
+// Output of vertex shader.
+varying float var_brightness;
+varying vec2 var_texture_position;
+
+void main() {
+	var_texture_position = attr_texture_position;
+	var_brightness = attr_brightness;
+	vec4 p = vec4(attr_position, 0., 1.);
+	gl_Position = gl_ProjectionMatrix * p;
+}
+)";
+
+const char kTerrainFragmentShader[] = R"(
+#version 120
+
+uniform sampler2D u_terrain_texture;
+
+varying float var_brightness;
+varying vec2 var_texture_position;
+
+void main() {
+	vec4 clr = texture2D(u_terrain_texture, var_texture_position);
+	clr.rgb *= var_brightness;
+	gl_FragColor = clr;
+}
+)";
+
+}  // namespace
+
+TerrainProgram::TerrainProgram() {
+	gl_program_.build(kTerrainVertexShader, kTerrainFragmentShader);
+
+	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
+	attr_texture_position_ =
+	   glGetAttribLocation(gl_program_.object(), "attr_texture_position");
+	attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
+
+	u_terrain_texture_ = glGetUniformLocation(gl_program_.object(), "u_terrain_texture");
+}
+
+void TerrainProgram::gl_draw(int num_vertices,
+                             const DescriptionMaintainer<TerrainDescription>& terrains) {
+	glUseProgram(gl_program_.object());
+
+	glEnableVertexAttribArray(attr_position_);
+	glEnableVertexAttribArray(attr_texture_position_);
+	glEnableVertexAttribArray(attr_brightness_);
+
+	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
+	glBufferData(GL_ARRAY_BUFFER,
+	             sizeof(TerrainProgram::PerVertexData) * num_vertices,
+	             vertices_.data(),
+	             GL_STREAM_DRAW);
+
+	const auto set_attrib_pointer = [](const int vertex_index, int num_items, int offset) {
+		glVertexAttribPointer(vertex_index,
+		                      num_items,
+		                      GL_FLOAT,
+		                      GL_FALSE,
+		                      sizeof(TerrainProgram::PerVertexData),
+		                      reinterpret_cast<void*>(offset));
+	};
+	set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, x));
+	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
+	set_attrib_pointer(attr_texture_position_, 2, offsetof(PerVertexData, texture_x));
+
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	// Set the sampler texture unit to 0
+	glActiveTexture(GL_TEXTURE0);
+	glUniform1i(u_terrain_texture_, 0);
+
+	// Which triangles to draw?
+	for (size_t i = 0; i < terrains_to_indices_.size(); ++i) {
+		const auto& indices = terrains_to_indices_[i];
+		if (indices.empty()) {
+			continue;
+		}
+		glBindTexture(
+		   GL_TEXTURE_2D,
+		   g_gr->get_maptexture_data(terrains.get_unmutable(i).get_texture())->get_texture());
+		glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, indices.data());
+	}
+
+	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_texture_position_);
+	glDisableVertexAttribArray(attr_brightness_);
+
+	glUseProgram(0);
+}
+
+void TerrainProgram::draw(const DescriptionMaintainer<TerrainDescription>& terrains,
+                          const FieldsToDraw& fields_to_draw) {
+	if (vertices_.size() < fields_to_draw.size()) {
+		vertices_.resize(fields_to_draw.size());
+		terrains_to_indices_.resize(terrains.size());
+	}
+	for (auto& container : terrains_to_indices_) {
+		container.clear();
+		container.reserve(fields_to_draw.size());
+	}
+
+	for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
+		const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
+
+		PerVertexData& vertex = vertices_[current_index];
+		vertex.texture_x = field.texture_x;
+		vertex.texture_y = field.texture_y;
+		vertex.x = field.x;
+		vertex.y = field.y;
+		vertex.brightness = field.brightness;
+
+		// The bottom right neighbor fields_to_draw is needed for both triangles
+		// associated with this field. If it is not in fields_to_draw, there is no need to
+		// draw any triangles.
+		const int brn_index = fields_to_draw.calculate_index(field.fx + (field.fy & 1), field.fy + 1);
+		if (brn_index == -1) {
+			continue;
+		}
+
+		// Down triangle.
+		const int bln_index =
+		   fields_to_draw.calculate_index(field.fx + (field.fy & 1) - 1, field.fy + 1);
+		if (bln_index != -1) {
+			terrains_to_indices_[field.ter_d].push_back(current_index);
+			terrains_to_indices_[field.ter_d].push_back(bln_index);
+			terrains_to_indices_[field.ter_d].push_back(brn_index);
+		}
+
+		// Right triangle.
+		const int rn_index = fields_to_draw.calculate_index(field.fx + 1, field.fy);
+		if (rn_index != -1) {
+			terrains_to_indices_[field.ter_r].push_back(current_index);
+			terrains_to_indices_[field.ter_r].push_back(brn_index);
+			terrains_to_indices_[field.ter_r].push_back(rn_index);
+		}
+	}
+
+	gl_draw(fields_to_draw.size(), terrains);
+}

=== added file 'src/graphic/gl/terrain_program.h'
--- src/graphic/gl/terrain_program.h	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/terrain_program.h	2014-11-01 20:50:47 +0000
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_GL_TERRAIN_PROGRAM_H
+#define WL_GRAPHIC_GL_TERRAIN_PROGRAM_H
+
+#include <vector>
+
+#include "graphic/gl/utils.h"
+#include "logic/description_maintainer.h"
+#include "logic/world/terrain_description.h"
+
+class FieldsToDraw;
+
+class TerrainProgram {
+public:
+	// Compiles the program. Throws on errors.
+	TerrainProgram();
+
+	// Draws the terrain.
+	void draw(const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+	          const FieldsToDraw& fields_to_draw);
+
+private:
+	struct PerVertexData {
+		float x;
+		float y;
+		float brightness;
+		float texture_x;
+		float texture_y;
+	};
+	static_assert(sizeof(PerVertexData) == 20, "Wrong padding.");
+
+	void gl_draw(int num_vertices,
+	             const DescriptionMaintainer<Widelands::TerrainDescription>& terrains);
+
+	// The buffer that will contain 'vertices_' for rendering.
+	Gl::Buffer gl_array_buffer_;
+
+	// The program used for drawing the terrain.
+	Gl::Program gl_program_;
+
+	// Attributes.
+	GLint attr_position_;
+	GLint attr_texture_position_;
+	GLint attr_brightness_;
+
+	// Uniforms.
+	GLint u_terrain_texture_;
+
+	// Objects below are kept around to avoid memory allocations on each frame.
+	// They could theoretically also be recreated.
+
+	// All vertices that are going to get rendered this frame.
+	std::vector<PerVertexData> vertices_;
+
+	// A map from terrain index in world.terrains() to indices in 'vertices_'
+	// that have this terrain type.
+	std::vector<std::vector<uint16_t>> terrains_to_indices_;
+
+	DISALLOW_COPY_AND_ASSIGN(TerrainProgram);
+};
+
+#endif  // end of include guard: WL_GRAPHIC_GL_TERRAIN_PROGRAM_H

=== renamed file 'src/graphic/render/gl_utils.cc' => 'src/graphic/gl/utils.cc'
--- src/graphic/render/gl_utils.cc	2014-06-01 18:00:48 +0000
+++ src/graphic/gl/utils.cc	2014-11-01 20:50:47 +0000
@@ -16,24 +16,39 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#include "graphic/render/gl_utils.h"
+#include "graphic/gl/utils.h"
+
+#include <memory>
+#include <string>
 
 #include <SDL_video.h>
 
 #include "base/log.h"
-
-/**
- * Return the smallest power of two greater than or equal to \p x.
- */
-uint32_t next_power_of_two(uint32_t x)
-{
-	uint32_t pot = 1;
-
-	while (pot < x)
-		pot *= 2;
-
-	return pot;
-}
+#include "base/wexception.h"
+
+namespace Gl {
+
+namespace {
+
+// Returns a readable string for a GL_*_SHADER 'type' for debug output.
+std::string shader_to_string(GLenum type) {
+	if (type == GL_VERTEX_SHADER) {
+		return "vertex";
+	}
+	if (type == GL_FRAGMENT_SHADER) {
+		return "fragment";
+	}
+	return "unknown";
+}
+
+// Creates one OpenGL buffer.
+GLuint create_buffer() {
+	GLuint buffer = 0;
+	glGenBuffers(1, &buffer);
+	return buffer;
+}
+
+}  // namespace
 
 /**
  * \return the standard 32-bit RGBA format that we use in OpenGL
@@ -63,7 +78,6 @@
 GLenum _handle_glerror(const char * file, unsigned int line)
 {
 	GLenum err = glGetError();
-#ifndef NDEBUG
 	if (err == GL_NO_ERROR)
 		return err;
 
@@ -95,6 +109,107 @@
 	default:
 		log("unknown\n");
 	}
-#endif
 	return err;
 }
+
+// Thin wrapper around a Shader object to ensure proper cleanup.
+class Shader {
+public:
+	Shader(GLenum type);
+	~Shader();
+
+	GLuint object() const {
+		return shader_object_;
+	}
+
+	// Compiles 'source'. Throws an exception on error.
+	void compile(const char* source);
+
+private:
+	const GLenum type_;
+	const GLuint shader_object_;
+
+	DISALLOW_COPY_AND_ASSIGN(Shader);
+};
+
+Shader::Shader(GLenum type) : type_(type), shader_object_(glCreateShader(type)) {
+	if (!shader_object_) {
+		throw wexception("Could not create %s shader.", shader_to_string(type).c_str());
+	}
+}
+
+Shader::~Shader() {
+	if (shader_object_) {
+		glDeleteShader(shader_object_);
+	}
+}
+
+void Shader::compile(const char* source) {
+	glShaderSource(shader_object_, 1, &source, nullptr);
+
+	glCompileShader(shader_object_);
+	GLint compiled;
+	glGetShaderiv(shader_object_, GL_COMPILE_STATUS, &compiled);
+	if (!compiled) {
+		GLint infoLen = 0;
+		glGetShaderiv(shader_object_, GL_INFO_LOG_LENGTH, &infoLen);
+		if (infoLen > 1) {
+			std::unique_ptr<char[]> infoLog(new char[infoLen]);
+			glGetShaderInfoLog(shader_object_, infoLen, NULL, infoLog.get());
+			throw wexception(
+			   "Error compiling %s shader:\n%s", shader_to_string(type_).c_str(), infoLog.get());
+		}
+	}
+}
+
+Buffer::Buffer() : buffer_object_(create_buffer()) {
+	if (!buffer_object_) {
+		throw wexception("Could not create GL program.");
+	}
+}
+
+Buffer::~Buffer() {
+	if (buffer_object_) {
+		glDeleteBuffers(1, &buffer_object_);
+	}
+}
+
+Program::Program() : program_object_(glCreateProgram()) {
+	if (!program_object_) {
+		throw wexception("Could not create GL program.");
+	}
+}
+
+Program::~Program() {
+	if (program_object_) {
+		glDeleteProgram(program_object_);
+	}
+}
+
+void Program::build(const char* vertex_shader_source, const char* fragment_shader_source) {
+	vertex_shader_.reset(new Shader(GL_VERTEX_SHADER));
+	vertex_shader_->compile(vertex_shader_source);
+	glAttachShader(program_object_, vertex_shader_->object());
+
+	fragment_shader_.reset(new Shader(GL_FRAGMENT_SHADER));
+	fragment_shader_->compile(fragment_shader_source);
+	glAttachShader(program_object_, fragment_shader_->object());
+
+	glLinkProgram(program_object_);
+
+	// Check the link status
+	GLint linked;
+	glGetProgramiv(program_object_, GL_LINK_STATUS, &linked);
+	if (!linked) {
+		GLint infoLen = 0;
+		glGetProgramiv(program_object_, GL_INFO_LOG_LENGTH, &infoLen);
+
+		if (infoLen > 1) {
+			std::unique_ptr<char[]> infoLog(new char[infoLen]);
+			glGetProgramInfoLog(program_object_, infoLen, NULL, infoLog.get());
+			throw wexception("Error linking:\n%s", infoLog.get());
+		}
+	}
+}
+
+}  // namespace Gl

=== renamed file 'src/graphic/render/gl_utils.h' => 'src/graphic/gl/utils.h'
--- src/graphic/render/gl_utils.h	2014-07-05 16:41:51 +0000
+++ src/graphic/gl/utils.h	2014-11-01 20:50:47 +0000
@@ -16,25 +16,74 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#ifndef WL_GRAPHIC_RENDER_GL_UTILS_H
-#define WL_GRAPHIC_RENDER_GL_UTILS_H
+#ifndef WL_GRAPHIC_GL_UTILS_H
+#define WL_GRAPHIC_GL_UTILS_H
 
 #define NO_SDL_GLEXT
 
+#include <memory>
+
 #include <GL/glew.h>
 #include <SDL_opengl.h>
 #include <stdint.h>
 
+#include "base/macros.h"
+
 struct SDL_PixelFormat;
 
-uint32_t next_power_of_two(uint32_t x);
+namespace Gl {
+
+class Shader;
+
 const SDL_PixelFormat & gl_rgba_format();
 GLenum _handle_glerror(const char * file, unsigned int line);
 
+// Thin wrapper around a OpenGL program object to ensure proper cleanup. Throws
+// on all errors.
+class Program {
+public:
+	Program();
+	~Program();
+
+	GLuint object() const {
+		return program_object_;
+	}
+
+	// Creates and compiles 'vertex_shader_source' and 'fragment_shader_source'
+	// into shader objects. Then links them into the program.
+	void build(const char* vertex_shader_source, const char* fragment_shader_source);
+
+private:
+	const GLuint program_object_;
+	std::unique_ptr<Shader> vertex_shader_;
+	std::unique_ptr<Shader> fragment_shader_;
+
+	DISALLOW_COPY_AND_ASSIGN(Program);
+};
+
+// Thin wrapper around a OpenGL buffer object to ensure proper cleanup. Throws
+// on all errors.
+class Buffer {
+public:
+	Buffer();
+	~Buffer();
+
+	GLuint object() const {
+		return buffer_object_;
+	}
+
+private:
+	const GLuint buffer_object_;
+
+	DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+}  // namespace Gl
+
 /**
  * handle_glerror() is intended to make debugging of OpenGL easier. It logs the
  * error code returned by glGetError and returns the error code.
  */
-#define handle_glerror() _handle_glerror(__FILE__, __LINE__)
+#define handle_glerror() Gl::_handle_glerror(__FILE__, __LINE__)
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GL_UTILS_H
+#endif  // end of include guard: WL_GRAPHIC_GL_UTILS_H

=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc	2014-09-20 09:37:47 +0000
+++ src/graphic/graphic.cc	2014-11-01 20:50:47 +0000
@@ -35,12 +35,12 @@
 #include "graphic/animation.h"
 #include "graphic/diranimations.h"
 #include "graphic/font_handler.h"
+#include "graphic/gl/surface_screen.h"
 #include "graphic/image.h"
 #include "graphic/image_io.h"
 #include "graphic/image_transformations.h"
-#include "graphic/render/gl_surface_screen.h"
-#include "graphic/render/sdl_surface.h"
 #include "graphic/rendertarget.h"
+#include "graphic/sdl/surface.h"
 #include "graphic/surface_cache.h"
 #include "graphic/texture.h"
 #include "io/fileread.h"
@@ -100,6 +100,10 @@
 
 	if (opengl) {
 		log("Graphics: Trying opengl\n");
+
+		// TODO(sirver): We should explicitly request an OpenGL 2.? core context
+		// here instead of relying on SDL to give us whatever.
+
 		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 		flags |= SDL_OPENGL;
 	}
@@ -151,6 +155,9 @@
 		//  about opengl and set the rendering capabilities.
 		log ("Graphics: OpenGL: OpenGL enabled\n");
 
+		// See http://stackoverflow.com/questions/13558073/program-crash-on-glgenvertexarrays-call for
+		// the next line.
+		glewExperimental = GL_TRUE;
 		GLenum err = glewInit();
 		if (err != GLEW_OK) {
 			log("glewInit returns %i\nYour OpenGL installation must be __very__ broken.\n", err);
@@ -207,10 +214,6 @@
 		log("Graphics: OpenGL: Number of stencil buffer bits: %u\n", glInt);
 		m_caps.gl.stencil_buffer_bits = glInt;
 
-		glGetIntegerv(GL_MAX_TEXTURE_UNITS, &glInt);
-		log("Graphics: OpenGL: Maximum number of textures for multitextures: %u\n", glInt);
-		m_caps.gl.max_tex_combined = glInt;
-
 		const char * str = reinterpret_cast<const char *>(glGetString(GL_VERSION));
 		m_caps.gl.major_version = atoi(str);
 		m_caps.gl.minor_version = strstr(str, ".")?atoi(strstr(str, ".") + 1):0;
@@ -227,12 +230,6 @@
 			(m_caps.gl.tex_power_of_two?"must have a size power of two\n":
 			 "may have any size\n");
 
-		m_caps.gl.multitexture =
-			 ((strstr(extensions, "GL_ARB_multitexture") != nullptr) &&
-			  (strstr(extensions, "GL_ARB_texture_env_combine") != nullptr));
-		log("Graphics: OpenGL: Multitexture capabilities ");
-		log(m_caps.gl.multitexture ? "sufficient\n" : "insufficient, only basic terrain rendering possible\n");
-
 DIAG_OFF("-Wold-style-cast")
 		m_caps.gl.blendequation = GLEW_VERSION_1_4 || GLEW_ARB_imaging;
 DIAG_ON ("-Wold-style-cast")

=== modified file 'src/graphic/graphic.h'
--- src/graphic/graphic.h	2014-07-14 10:45:44 +0000
+++ src/graphic/graphic.h	2014-11-01 20:50:47 +0000
@@ -56,10 +56,6 @@
 	int aux_buffers;
 	/// Whether the BlendEquation support is available
 	bool blendequation;
-	/// Maximum number of textures that can be combined
-	int max_tex_combined;
-	/// Whether multitexturing is supported
-	bool multitexture;
 };
 
 /**

=== modified file 'src/graphic/image_transformations.cc'
--- src/graphic/image_transformations.cc	2014-07-26 10:43:23 +0000
+++ src/graphic/image_transformations.cc	2014-11-01 20:50:47 +0000
@@ -28,7 +28,7 @@
 #include "base/macros.h"
 #include "graphic/color.h"
 #include "graphic/graphic.h"
-#include "graphic/render/sdl_surface.h"
+#include "graphic/sdl/surface.h"
 #include "graphic/surface.h"
 #include "graphic/surface_cache.h"
 

=== renamed file 'src/graphic/render/minimaprenderer.cc' => 'src/graphic/minimap_renderer.cc'
--- src/graphic/render/minimaprenderer.cc	2014-09-10 08:55:04 +0000
+++ src/graphic/minimap_renderer.cc	2014-11-01 20:50:47 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "graphic/render/minimaprenderer.h"
+#include "graphic/minimap_renderer.h"
 
 #include <memory>
 

=== renamed file 'src/graphic/render/minimaprenderer.h' => 'src/graphic/minimap_renderer.h'
--- src/graphic/render/minimaprenderer.h	2014-09-10 08:55:04 +0000
+++ src/graphic/minimap_renderer.h	2014-11-01 20:50:47 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_MINIMAPRENDERER_H
-#define WL_GRAPHIC_RENDER_MINIMAPRENDERER_H
+#ifndef WL_GRAPHIC_MINIMAP_RENDERER_H
+#define WL_GRAPHIC_MINIMAP_RENDERER_H
 
 #include <memory>
 
@@ -66,4 +66,4 @@
 	(const Widelands::EditorGameBase& egbase, Widelands::Player const* player,
 	 const Point& viewpoint, MiniMapLayer layers, StreamWrite* const streamwrite);
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_MINIMAPRENDERER_H
+#endif  // end of include guard: WL_GRAPHIC_MINIMAP_RENDERER_H

=== removed directory 'src/graphic/render'
=== added directory 'src/graphic/sdl'
=== renamed file 'src/graphic/render/gamerenderer_sdl.cc' => 'src/graphic/sdl/game_renderer.cc'
--- src/graphic/render/gamerenderer_sdl.cc	2014-09-10 08:55:04 +0000
+++ src/graphic/sdl/game_renderer.cc	2014-11-01 20:50:47 +0000
@@ -17,10 +17,10 @@
  *
  */
 
-#include "graphic/render/gamerenderer_sdl.h"
+#include "graphic/sdl/game_renderer.h"
 
-#include "graphic/render/terrain_sdl.h"
 #include "graphic/rendertarget.h"
+#include "graphic/sdl/terrain.h"
 #include "logic/field.h"
 #include "logic/map.h"
 #include "logic/player.h"
@@ -56,13 +56,13 @@
 }
 
 
-void GameRendererSDL::draw()
+void SdlGameRenderer::draw()
 {
 	draw_terrain();
 	draw_objects();
 }
 
-void GameRendererSDL::draw_terrain()
+void SdlGameRenderer::draw_terrain()
 {
 	if (m_player && !m_player->see_all())
 		m_dst->get_surface()->fill_rect(m_dst->get_rect(), RGBAColor(0, 0, 0, 255));
@@ -239,7 +239,7 @@
  *        /  d  \/
  *  (bl) *------* (br)
  */
-void GameRendererSDL::draw_field
+void SdlGameRenderer::draw_field
 	(RenderTarget & dst,
 	 const Vertex  &  f_vert,
 	 const Vertex  &  r_vert,

=== renamed file 'src/graphic/render/gamerenderer_sdl.h' => 'src/graphic/sdl/game_renderer.h'
--- src/graphic/render/gamerenderer_sdl.h	2014-07-05 16:41:51 +0000
+++ src/graphic/sdl/game_renderer.h	2014-11-01 20:50:47 +0000
@@ -17,10 +17,10 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_GAMERENDERER_SDL_H
-#define WL_GRAPHIC_RENDER_GAMERENDERER_SDL_H
+#ifndef WL_GRAPHIC_SDL_GAME_RENDERER_H
+#define WL_GRAPHIC_SDL_GAME_RENDERER_H
 
-#include "graphic/render/gamerenderer.h"
+#include "graphic/game_renderer.h"
 
 struct Texture;
 struct Vertex;
@@ -28,7 +28,7 @@
 /**
  * Software-rendering implementation of @ref GameRenderer.
  */
-class GameRendererSDL : public GameRenderer {
+class SdlGameRenderer : public GameRenderer {
 protected:
 	void draw() override;
 
@@ -52,4 +52,4 @@
 		 const Texture &  f_r_texture);
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_GAMERENDERER_SDL_H
+#endif  // end of include guard: WL_GRAPHIC_SDL_GAME_RENDERER_H

=== renamed file 'src/graphic/render/sdl_surface.cc' => 'src/graphic/sdl/surface.cc'
--- src/graphic/render/sdl_surface.cc	2014-07-14 10:45:44 +0000
+++ src/graphic/sdl/surface.cc	2014-11-01 20:50:47 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "graphic/render/sdl_surface.h"
+#include "graphic/sdl/surface.h"
 
 #include <cassert>
 

=== renamed file 'src/graphic/render/sdl_surface.h' => 'src/graphic/sdl/surface.h'
--- src/graphic/render/sdl_surface.h	2014-07-26 10:43:23 +0000
+++ src/graphic/sdl/surface.h	2014-11-01 20:50:47 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_SDL_SURFACE_H
-#define WL_GRAPHIC_RENDER_SDL_SURFACE_H
+#ifndef WL_GRAPHIC_SDL_SURFACE_H
+#define WL_GRAPHIC_SDL_SURFACE_H
 
 #include "base/rect.h"
 #include "graphic/color.h"
@@ -72,5 +72,4 @@
 	bool m_free_surface_on_delete;
 };
 
-
-#endif  // end of include guard: WL_GRAPHIC_RENDER_SDL_SURFACE_H
+#endif  // end of include guard: WL_GRAPHIC_SDL_SURFACE_H

=== renamed file 'src/graphic/render/terrain_sdl.cc' => 'src/graphic/sdl/terrain.cc'
--- src/graphic/render/terrain_sdl.cc	2013-07-26 20:19:36 +0000
+++ src/graphic/sdl/terrain.cc	2014-11-01 20:50:47 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "graphic/render/terrain_sdl.h"
+#include "graphic/sdl/terrain.h"
 
 /// get lambda and mu so that
 /// lambda * u + mu * v = (1 0)^T with u = (u1 u2)^T and v = (v1 v2)^T

=== renamed file 'src/graphic/render/terrain_sdl.h' => 'src/graphic/sdl/terrain.h'
--- src/graphic/render/terrain_sdl.h	2014-07-25 22:17:48 +0000
+++ src/graphic/sdl/terrain.h	2014-11-01 20:50:47 +0000
@@ -17,16 +17,16 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_TERRAIN_SDL_H
-#define WL_GRAPHIC_RENDER_TERRAIN_SDL_H
+#ifndef WL_GRAPHIC_SDL_TERRAIN_H
+#define WL_GRAPHIC_SDL_TERRAIN_H
 
 #include <cassert>
 
 #include "base/log.h"
 #include "base/macros.h"
 #include "graphic/graphic.h"
-#include "graphic/render/sdl_surface.h"
-#include "graphic/render/vertex.h"
+#include "graphic/sdl/surface.h"
+#include "graphic/sdl/vertex.h"
 #include "graphic/texture.h"
 #include "logic/roadtype.h"
 #include "random/random.h"
@@ -665,4 +665,4 @@
 	// TODO(unknown): similar textures may not need dithering
 }
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_TERRAIN_SDL_H
+#endif  // end of include guard: WL_GRAPHIC_SDL_TERRAIN_H

=== renamed file 'src/graphic/render/sdl_helper.cc' => 'src/graphic/sdl/utils.cc'
--- src/graphic/render/sdl_helper.cc	2014-07-14 10:45:44 +0000
+++ src/graphic/sdl/utils.cc	2014-11-01 20:50:47 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "graphic/render/sdl_helper.h"
+#include "graphic/sdl/utils.h"
 
 #include <cassert>
 

=== renamed file 'src/graphic/render/sdl_helper.h' => 'src/graphic/sdl/utils.h'
--- src/graphic/render/sdl_helper.h	2014-07-05 16:41:51 +0000
+++ src/graphic/sdl/utils.h	2014-11-01 20:50:47 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_SDL_HELPER_H
-#define WL_GRAPHIC_RENDER_SDL_HELPER_H
+#ifndef WL_GRAPHIC_SDL_UTILS_H
+#define WL_GRAPHIC_SDL_UTILS_H
 
 #include <stdint.h>
 
@@ -26,4 +26,4 @@
 
 SDL_Surface * empty_sdl_surface(int16_t w, int16_t h);
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_SDL_HELPER_H
+#endif  // end of include guard: WL_GRAPHIC_SDL_UTILS_H

=== renamed file 'src/graphic/render/vertex.h' => 'src/graphic/sdl/vertex.h'
--- src/graphic/render/vertex.h	2014-07-05 16:41:51 +0000
+++ src/graphic/sdl/vertex.h	2014-11-01 20:50:47 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_GRAPHIC_RENDER_VERTEX_H
-#define WL_GRAPHIC_RENDER_VERTEX_H
+#ifndef WL_GRAPHIC_SDL_VERTEX_H
+#define WL_GRAPHIC_SDL_VERTEX_H
 
 #include "base/point.h"
 
@@ -36,4 +36,4 @@
 	int32_t b, tx, ty;
 };
 
-#endif  // end of include guard: WL_GRAPHIC_RENDER_VERTEX_H
+#endif  // end of include guard: WL_GRAPHIC_SDL_VERTEX_H

=== modified file 'src/graphic/surface.cc'
--- src/graphic/surface.cc	2014-07-17 13:26:23 +0000
+++ src/graphic/surface.cc	2014-11-01 20:50:47 +0000
@@ -21,9 +21,9 @@
 
 #include <SDL.h>
 
-#include "graphic/render/gl_surface_texture.h"
-#include "graphic/render/sdl_helper.h"
-#include "graphic/render/sdl_surface.h"
+#include "graphic/gl/surface_texture.h"
+#include "graphic/sdl/surface.h"
+#include "graphic/sdl/utils.h"
 
 extern bool g_opengl;
 

=== modified file 'src/graphic/text/sdl_ttf_font.cc'
--- src/graphic/text/sdl_ttf_font.cc	2014-09-10 16:57:31 +0000
+++ src/graphic/text/sdl_ttf_font.cc	2014-11-01 20:50:47 +0000
@@ -22,7 +22,7 @@
 #include <SDL_ttf.h>
 #include <boost/format.hpp>
 
-#include "graphic/render/sdl_helper.h"
+#include "graphic/sdl/utils.h"
 #include "graphic/surface.h"
 #include "graphic/surface_cache.h"
 #include "graphic/text/rt_errors.h"

=== modified file 'src/graphic/text/test/render_richtext.cc'
--- src/graphic/text/test/render_richtext.cc	2014-09-20 09:37:47 +0000
+++ src/graphic/text/test/render_richtext.cc	2014-11-01 20:50:47 +0000
@@ -31,7 +31,7 @@
 
 #include "base/log.h"
 #include "graphic/image_io.h"
-#include "graphic/render/sdl_surface.h"
+#include "graphic/sdl/surface.h"
 #include "graphic/text/rt_errors.h"
 #include "graphic/text/test/render.h"
 #include "io/filesystem/filesystem.h"

=== modified file 'src/graphic/texture.h'
--- src/graphic/texture.h	2014-09-20 09:37:47 +0000
+++ src/graphic/texture.h	2014-11-01 20:50:47 +0000
@@ -27,7 +27,7 @@
 #include <stdint.h>
 
 #include "graphic/colormap.h"
-#include "graphic/render/gl_surface_texture.h"
+#include "graphic/gl/surface_texture.h"
 
 /// Textures have a fixed size and are squares.
 /// TEXTURE_HEIGHT is just defined for easier understanding of the code.

=== modified file 'src/logic/description_maintainer.h'
--- src/logic/description_maintainer.h	2014-09-14 11:31:58 +0000
+++ src/logic/description_maintainer.h	2014-11-01 20:50:47 +0000
@@ -36,6 +36,9 @@
 
 	// Returns the number of entries in the container.
 	size_t get_nitems() const {return items_.size();}
+	// TODO(sirver): Remove get_nitems().
+
+	size_t size() const {return items_.size();}
 
 	// Returns the entry with the given 'name' if it exists or nullptr.
 	T* exists(const std::string& name) const;

=== modified file 'src/logic/map_info.cc'
--- src/logic/map_info.cc	2014-09-20 09:37:47 +0000
+++ src/logic/map_info.cc	2014-11-01 20:50:47 +0000
@@ -28,7 +28,7 @@
 #include "config.h"
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
-#include "graphic/render/minimaprenderer.h"
+#include "graphic/minimap_renderer.h"
 #include "graphic/surface.h"
 #include "io/filesystem/filesystem.h"
 #include "io/filesystem/layered_filesystem.h"

=== modified file 'src/wui/mapview.cc'
--- src/wui/mapview.cc	2014-09-10 13:03:40 +0000
+++ src/wui/mapview.cc	2014-11-01 20:50:47 +0000
@@ -20,10 +20,10 @@
 #include "wui/mapview.h"
 
 #include "base/macros.h"
+#include "graphic/gl/game_renderer.h"
 #include "graphic/graphic.h"
-#include "graphic/render/gamerenderer_gl.h"
-#include "graphic/render/gamerenderer_sdl.h"
 #include "graphic/rendertarget.h"
+#include "graphic/sdl/game_renderer.h"
 #include "logic/map.h"
 #include "logic/player.h"
 #include "wlapplication.h"
@@ -98,10 +98,10 @@
 
 	if (!m_renderer) {
 		if (g_opengl) {
-			m_renderer.reset(new GameRendererGL());
+			m_renderer.reset(new GlGameRenderer());
 		} else
 		{
-			m_renderer.reset(new GameRendererSDL());
+			m_renderer.reset(new SdlGameRenderer());
 		}
 	}
 	if (upcast(InteractivePlayer const, interactive_player, &intbase())) {

=== modified file 'src/wui/mapviewpixelconstants.h'
--- src/wui/mapviewpixelconstants.h	2014-07-05 16:41:51 +0000
+++ src/wui/mapviewpixelconstants.h	2014-11-01 20:50:47 +0000
@@ -21,9 +21,8 @@
 #define WL_WUI_MAPVIEWPIXELCONSTANTS_H
 
 //  These are constants with the unit pixel.
-#define TRIANGLE_WIDTH 64
-#define TRIANGLE_HEIGHT 32
-#define HEIGHT_FACTOR 5
-
+constexpr int TRIANGLE_WIDTH = 64;
+constexpr int TRIANGLE_HEIGHT = 32;
+constexpr int HEIGHT_FACTOR = 5;
 
 #endif  // end of include guard: WL_WUI_MAPVIEWPIXELCONSTANTS_H

=== modified file 'src/wui/mapviewpixelfunctions.h'
--- src/wui/mapviewpixelfunctions.h	2014-09-10 08:55:04 +0000
+++ src/wui/mapviewpixelfunctions.h	2014-11-01 20:50:47 +0000
@@ -30,19 +30,18 @@
 
 namespace MapviewPixelFunctions {
 
-float calc_brightness
-	(int32_t const l,
-	 int32_t const r,
-	 int32_t const tl,
-	 int32_t const tr,
-	 int32_t const bl,
-	 int32_t const br);
-
-Point calc_pix_difference(const Widelands::Map &, Point, Point);
-uint32_t calc_pix_distance(const Widelands::Map &, Point, Point);
-
-uint32_t get_map_end_screen_x(const Widelands::Map &);
-uint32_t get_map_end_screen_y(const Widelands::Map &);
+float calc_brightness(int32_t l, int32_t r, int32_t tl, int32_t tr, int32_t bl, int32_t br);
+
+Point calc_pix_difference(const Widelands::Map&, Point, Point);
+uint32_t calc_pix_distance(const Widelands::Map&, Point, Point);
+
+inline uint32_t get_map_end_screen_x(const Widelands::Map& map) {
+	return map.get_width() * TRIANGLE_WIDTH;
+}
+
+inline uint32_t get_map_end_screen_y(const Widelands::Map& map) {
+	return map.get_height() * TRIANGLE_HEIGHT;
+}
 
 /**
  * Calculate the coordinates of the triangle the given point in pixels is in.
@@ -53,80 +52,37 @@
  * the point is in. But this should be fully correct for all but the most
  * bizarre triangle shapes, and acceptable even for them.
  */
-Widelands::NodeAndTriangle<> calc_node_and_triangle
-	(const Widelands::Map &, uint32_t x, uint32_t y);
-
-void normalize_pix(const Widelands::Map &, Point & p);
-
-/**
- * Calculate the on-screen position of the node without taking height into
- * account.
- */
-void get_basepix(Widelands::Coords fc, int32_t & px, int32_t & py);
-
-/**
- * Calculate the on-screen position of the node.
- */
-void get_pix(Widelands::FCoords fc, int32_t & px, int32_t & py);
-
-void get_pix
-	(const Widelands::Map &, Widelands::Coords c, int32_t & px, int32_t & py);
-
-void get_save_pix
-	(const Widelands::Map &, Widelands::Coords c, int32_t & px, int32_t & py);
-}
-
-//  Implementation follows:
-//  The rest of the content of this file is only here to be inlined. It should
-//  have been in the cc file otherwise. Now objectcode modularity is not
-//  achieved, but only sourcecode modularity is required.
-
-inline uint32_t MapviewPixelFunctions::get_map_end_screen_x
-	(const Widelands::Map & map)
-{
-	return map.get_width() * TRIANGLE_WIDTH;
-}
-inline uint32_t MapviewPixelFunctions::get_map_end_screen_y
-	(const Widelands::Map & map)
-{
-	return map.get_height() * TRIANGLE_HEIGHT;
-}
-
-/*
-===============
-Calculate the on-screen position of the node without taking height
-into account.
-===============
-*/
-inline void MapviewPixelFunctions::get_basepix
-	(Widelands::Coords const  c, int32_t & px, int32_t & py)
-{
+Widelands::NodeAndTriangle<> calc_node_and_triangle(const Widelands::Map&, uint32_t x, uint32_t y);
+
+void normalize_pix(const Widelands::Map&, Point& p);
+
+// Calculate the on-screen position of the node without taking height into
+// account.
+inline void get_basepix(const Widelands::Coords& c, int32_t& px, int32_t& py) {
 	py = c.y * TRIANGLE_HEIGHT;
 	px = c.x * TRIANGLE_WIDTH + (c.y & 1) * (TRIANGLE_WIDTH / 2);
 }
 
-
-inline void MapviewPixelFunctions::get_pix
-	(Widelands::FCoords const fc, int32_t & px, int32_t & py)
-{
+/**
+ * Calculate the on-screen position of the node.
+ */
+inline void get_pix(const Widelands::FCoords& fc, int32_t& px, int32_t& py) {
 	get_basepix(fc, px, py);
 	py -= fc.field->get_height() * HEIGHT_FACTOR;
 }
 
-inline void MapviewPixelFunctions::get_pix
-	(const Widelands::Map & map, const Widelands::Coords c,
-	 int32_t & px, int32_t & py)
-{
+inline void
+get_pix(const Widelands::Map& map, const Widelands::Coords& c, int32_t& px, int32_t& py) {
 	get_pix(map.get_fcoords(c), px, py);
 }
 
 // fx and fy might be out of range, must be normalized for the field
 // theres no need for such a function for FCoords, since x, y out of range
 // but field valid doesn't make sense
-inline void MapviewPixelFunctions::get_save_pix
-	(const Widelands::Map & map, Widelands::Coords const c,
-	 int32_t & px, int32_t & py)
-{
+inline void get_save_pix(const Widelands::Map& map,
+                         const Widelands::Coords& c,
+                         int32_t& px,
+                         int32_t& py) {
 	Widelands::Coords c1 = c;
 	map.normalize_coords(c1);
 	Widelands::FCoords fc = map.get_fcoords(c1);
@@ -135,4 +91,6 @@
 	get_pix(fc, px, py);
 }
 
+}  // namespace MapviewPixelFunctions
+
 #endif  // end of include guard: WL_WUI_MAPVIEWPIXELFUNCTIONS_H

=== modified file 'src/wui/minimap.cc'
--- src/wui/minimap.cc	2014-09-10 08:55:04 +0000
+++ src/wui/minimap.cc	2014-11-01 20:50:47 +0000
@@ -24,7 +24,7 @@
 #include "base/i18n.h"
 #include "graphic/graphic.h"
 #include "graphic/in_memory_image.h"
-#include "graphic/render/minimaprenderer.h"
+#include "graphic/minimap_renderer.h"
 #include "graphic/rendertarget.h"
 #include "graphic/surface.h"
 #include "logic/map.h"

=== modified file 'src/wui/minimap.h'
--- src/wui/minimap.h	2014-09-10 08:55:04 +0000
+++ src/wui/minimap.h	2014-11-01 20:50:47 +0000
@@ -22,7 +22,7 @@
 
 #include <boost/signals2.hpp>
 
-#include "graphic/render/minimaprenderer.h"
+#include "graphic/minimap_renderer.h"
 #include "ui_basic/button.h"
 #include "ui_basic/unique_window.h"
 


References