← Back to team overview

widelands-dev team mailing list archive

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

 

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

Commit message:
Split 'GameRenderer' into individual functions and move drawing logic into individual classes.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/fields_to_draw/+merge/329723
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/fields_to_draw into lp:widelands.
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2017-08-28 07:39:59 +0000
+++ src/editor/editorinteractive.cc	2017-08-28 12:55:36 +0000
@@ -312,11 +312,38 @@
 }
 
 void EditorInteractive::draw(RenderTarget& dst) {
-	const GameRenderer::Overlays overlays{get_text_to_draw(), {}};
-	map_view()->draw_map_view(
-	   egbase(), overlays,
-	   draw_immovables_ ? GameRenderer::DrawImmovables::kYes : GameRenderer::DrawImmovables::kNo,
-	   draw_bobs_ ? GameRenderer::DrawBobs::kYes : GameRenderer::DrawBobs::kNo, nullptr, &dst);
+	const auto& ebase = egbase();
+	auto* fields_to_draw = map_view()->draw_terrain(ebase, &dst);
+
+	const float scale = 1.f / map_view()->view().zoom;
+	const uint32_t gametime = ebase.get_gametime();
+
+	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
+		const FieldsToDraw::Field& field = fields_to_draw->at(idx);
+		if (draw_immovables_) {
+			Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
+			if (imm != nullptr && imm->get_positions(ebase).front() == field.fcoords) {
+				imm->draw(gametime, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
+			}
+		}
+
+		if (draw_bobs_) {
+			for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
+			     bob = bob->get_next_bob()) {
+				bob->draw(ebase, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
+			}
+		}
+
+		// TODO(sirver): Do not use the field_overlay_manager, instead draw the
+		// overlays we are interested in here directly.
+		field_overlay_manager().foreach_overlay(
+		   field.fcoords, [&dst, &field, scale](const Image* pic, const Vector2i& hotspot) {
+			   dst.blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale,
+			                            pic->width() * scale, pic->height() * scale),
+			                      pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
+			                      BlendMode::UseAlpha);
+			});
+	}
 }
 
 /// Needed to get freehand painting tools (hold down mouse and move to edit).

=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2017-08-11 20:01:03 +0000
+++ src/graphic/CMakeLists.txt	2017-08-28 12:55:36 +0000
@@ -164,8 +164,9 @@
     base_geometry
     base_macros
     graphic_color
+    graphic_draw_programs
+    graphic_fields_to_draw
     graphic_terrain_programs
-    graphic_draw_programs
     logic
 )
 
@@ -177,10 +178,10 @@
     base_geometry
     base_macros
     graphic
+    graphic_fields_to_draw
     graphic_gl_utils
     graphic_render_queue
     graphic_surface
-    graphic_terrain_programs
     logic
     wui
     wui_field_overlay_manager
@@ -201,9 +202,22 @@
     wui_mapview_pixelfunctions
 )
 
+wl_library(graphic_fields_to_draw
+  SRCS
+    gl/fields_to_draw.cc
+    gl/fields_to_draw.h
+  DEPENDS
+    base_geometry
+    graphic
+    graphic_gl_utils
+    logic
+    logic_constants
+    logic_widelands_geometry
+    wui_mapview_pixelfunctions
+)
+
 wl_library(graphic_terrain_programs
   SRCS
-    gl/fields_to_draw.h
     gl/road_program.cc
     gl/road_program.h
     gl/terrain_program.cc
@@ -216,13 +230,12 @@
     base_log
     base_macros
     graphic
+    graphic_fields_to_draw
     graphic_gl_utils
     graphic_image_io
     graphic_surface
     io_filesystem
     logic
-    logic_constants
-    logic_widelands_geometry
 )
 
 

=== modified file 'src/graphic/game_renderer.cc'
--- src/graphic/game_renderer.cc	2017-08-28 11:52:39 +0000
+++ src/graphic/game_renderer.cc	2017-08-28 12:55:36 +0000
@@ -66,324 +66,38 @@
  * d.dither_layer, then we will repaint d with the dither texture as mask.
  */
 
-namespace {
-
-using namespace Widelands;
-
-// 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.;
-		} else if (pf.vision == 1) {
-			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 / 255.;
-}
-
-void draw_immovables_for_visible_field(const EditorGameBase& egbase,
-                                       const FieldsToDraw::Field& field,
-                                       const float zoom,
-                                       const TextToDraw text_to_draw,
-                                       const Player* player,
-                                       RenderTarget* dst) {
-	BaseImmovable* const imm = field.fcoords.field->get_immovable();
-	if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) {
-		TextToDraw draw_text_for_this_immovable = text_to_draw;
-		const Player* owner = imm->get_owner();
-		if (player != nullptr && owner != nullptr && !player->see_all() &&
-		    player->is_hostile(*owner)) {
-			draw_text_for_this_immovable =
-			   static_cast<TextToDraw>(draw_text_for_this_immovable & ~TextToDraw::kStatistics);
-		}
-		imm->draw(
-		   egbase.get_gametime(), draw_text_for_this_immovable, field.rendertarget_pixel, zoom, dst);
-	}
-}
-
-void draw_bobs_for_visible_field(const EditorGameBase& egbase,
-                                 const FieldsToDraw::Field& field,
-                                 const float zoom,
-                                 const TextToDraw text_to_draw,
-                                 const Player* player,
-                                 RenderTarget* dst) {
-	for (Bob* bob = field.fcoords.field->get_first_bob(); bob; bob = bob->get_next_bob()) {
-		TextToDraw draw_text_for_this_bob = text_to_draw;
-		const Player* owner = bob->get_owner();
-		if (player != nullptr && owner != nullptr && !player->see_all() &&
-		    player->is_hostile(*owner)) {
-			draw_text_for_this_bob =
-			   static_cast<TextToDraw>(draw_text_for_this_bob & ~TextToDraw::kStatistics);
-		}
-		bob->draw(egbase, draw_text_for_this_bob, field.rendertarget_pixel, zoom, dst);
-	}
-}
-
-void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field,
-                                                const Player::Field& player_field,
-                                                const float zoom,
-                                                RenderTarget* dst) {
-	if (const MapObjectDescr* const map_object_descr =
-	       player_field.map_object_descr[TCoords<>::None]) {
-		if (player_field.constructionsite.becomes) {
-			assert(field.owner != nullptr);
-			const ConstructionsiteInformation& csinf = player_field.constructionsite;
-			// draw the partly finished constructionsite
-			uint32_t anim_idx;
-			try {
-				anim_idx = csinf.becomes->get_animation("build");
-			} catch (MapObjectDescr::AnimationNonexistent&) {
-				try {
-					anim_idx = csinf.becomes->get_animation("unoccupied");
-				} catch (MapObjectDescr::AnimationNonexistent) {
-					anim_idx = csinf.becomes->get_animation("idle");
-				}
-			}
-			const Animation& anim = g_gr->animations().get_animation(anim_idx);
-			const size_t nr_frames = anim.nr_frames();
-			uint32_t cur_frame =
-			   csinf.totaltime ? csinf.completedtime * nr_frames / csinf.totaltime : 0;
-			uint32_t tanim = cur_frame * FRAME_LENGTH;
-
-			uint32_t percent = 100 * csinf.completedtime * nr_frames;
-			if (csinf.totaltime) {
-				percent /= csinf.totaltime;
-			}
-			percent -= 100 * cur_frame;
-
-			if (cur_frame) {  // not the first frame
-				// Draw the prev frame
-				dst->blit_animation(field.rendertarget_pixel, zoom, anim_idx, tanim - FRAME_LENGTH,
-				                    field.owner->get_playercolor());
-			} else if (csinf.was) {
-				// Is the first frame, but there was another building here before,
-				// get its last build picture and draw it instead.
-				uint32_t a;
-				try {
-					a = csinf.was->get_animation("unoccupied");
-				} catch (MapObjectDescr::AnimationNonexistent&) {
-					a = csinf.was->get_animation("idle");
-				}
-				dst->blit_animation(field.rendertarget_pixel, zoom, a, tanim - FRAME_LENGTH,
-				                    field.owner->get_playercolor());
-			}
-			dst->blit_animation(field.rendertarget_pixel, zoom, anim_idx, tanim,
-			                    field.owner->get_playercolor(), percent);
-		} else if (upcast(const BuildingDescr, building, map_object_descr)) {
-			assert(field.owner != nullptr);
-			// this is a building therefore we either draw unoccupied or idle animation
-			uint32_t pic;
-			try {
-				pic = building->get_animation("unoccupied");
-			} catch (MapObjectDescr::AnimationNonexistent&) {
-				pic = building->get_animation("idle");
-			}
-			dst->blit_animation(
-			   field.rendertarget_pixel, zoom, pic, 0, field.owner->get_playercolor());
-		} else if (map_object_descr->type() == MapObjectType::FLAG) {
-			assert(field.owner != nullptr);
-			dst->blit_animation(field.rendertarget_pixel, zoom, field.owner->tribe().flag_animation(),
-			                    0, field.owner->get_playercolor());
-		} else if (const uint32_t pic = map_object_descr->main_animation()) {
-			if (field.owner != nullptr) {
-				dst->blit_animation(
-				   field.rendertarget_pixel, zoom, pic, 0, field.owner->get_playercolor());
-			} else {
-				dst->blit_animation(field.rendertarget_pixel, zoom, pic, 0);
-			}
-		}
-	}
-}
-
-// Draws the objects (animations & overlays).
-void draw_objects(const EditorGameBase& egbase,
-                  const float zoom,
+void draw_border_markers(const FieldsToDraw::Field& field,
+					  const float scale,
+                 const FieldsToDraw& fields_to_draw,
+                 RenderTarget* dst) {
+	if (!field.all_neighbors_valid() || !field.is_border) {
+		return;
+	}
+	assert(field.owner != nullptr);
+
+	uint32_t const anim_idx = field.owner->tribe().frontier_animation();
+	if (field.vision) {
+		dst->blit_animation(
+		   field.rendertarget_pixel, scale, anim_idx, 0, field.owner->get_playercolor());
+	}
+	for (const auto& nf : {fields_to_draw.at(field.rn_index), fields_to_draw.at(field.bln_index),
+	                       fields_to_draw.at(field.brn_index)}) {
+		if ((field.vision || nf.vision) && nf.is_border &&
+		    (field.owner == nf.owner || nf.owner == nullptr)) {
+			dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), scale,
+			                    anim_idx, 0, field.owner->get_playercolor());
+		}
+	}
+}
+
+void draw_terrain(const Widelands::EditorGameBase& egbase,
                   const FieldsToDraw& fields_to_draw,
-                  const Player* player,
-                  const TextToDraw text_to_draw,
-                  const GameRenderer::DrawImmovables& draw_immovables,
-                  const GameRenderer::DrawBobs& draw_bobs,
+                  const float scale,
                   RenderTarget* dst) {
-	for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
-		const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
-		if (!field.all_neighbors_valid()) {
-			continue;
-		}
-
-		const FieldsToDraw::Field& rn = fields_to_draw.at(field.rn_index);
-		const FieldsToDraw::Field& bln = fields_to_draw.at(field.bln_index);
-		const FieldsToDraw::Field& brn = fields_to_draw.at(field.brn_index);
-
-		if (field.is_border && draw_immovables == GameRenderer::DrawImmovables::kYes) {
-			assert(field.owner != nullptr);
-			uint32_t const anim_idx = field.owner->tribe().frontier_animation();
-			if (field.vision) {
-				dst->blit_animation(
-				   field.rendertarget_pixel, zoom, anim_idx, 0, field.owner->get_playercolor());
-			}
-			for (const auto& nf : {rn, bln, brn}) {
-				if ((field.vision || nf.vision) && nf.is_border &&
-				    (field.owner == nf.owner || nf.owner == nullptr)) {
-					dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), zoom,
-					                    anim_idx, 0, field.owner->get_playercolor());
-				}
-			}
-		}
-
-		if (1 < field.vision) {  // Render stuff that belongs to the node.
-			if (draw_immovables == GameRenderer::DrawImmovables::kYes) {
-				draw_immovables_for_visible_field(egbase, field, zoom, text_to_draw, player, dst);
-			}
-			if (draw_bobs == GameRenderer::DrawBobs::kYes) {
-				draw_bobs_for_visible_field(egbase, field, zoom, text_to_draw, player, dst);
-			}
-		} else if (field.vision == 1 && draw_immovables == GameRenderer::DrawImmovables::kYes) {
-			// We never show census or statistics for objects in the fog.
-			assert(player != nullptr);
-			const Map& map = egbase.map();
-			const Player::Field& player_field =
-			   player->fields()[map.get_index(field.fcoords, map.get_width())];
-			draw_immovables_for_formerly_visible_field(field, player_field, zoom, dst);
-		}
-
-		egbase.get_ibase()->field_overlay_manager().foreach_overlay(
-		   field.fcoords, [dst, &field, zoom](const Image* pic, const Vector2i& hotspot) {
-			   dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * zoom,
-			                             pic->width() * zoom, pic->height() * zoom),
-			                       pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
-			                       BlendMode::UseAlpha);
-			});
-	}
-}
-
-}  // namespace
-
-GameRenderer::GameRenderer() {
-}
-
-GameRenderer::~GameRenderer() {
-}
-
-void GameRenderer::render(const EditorGameBase& egbase,
-                          const Vector2f& viewpoint,
-                          const float zoom,
-                          const Player* player,
-                          const Overlays& overlays,
-                          const DrawImmovables& draw_immovables,
-                          const DrawBobs& draw_bobs,
-                          RenderTarget* dst) {
-	assert(viewpoint.x >= 0);  // divisions involving negative numbers are bad
-	assert(viewpoint.y >= 0);
-	assert(dst->get_offset().x <= 0);
-	assert(dst->get_offset().y <= 0);
-
-	int minfx = std::floor(viewpoint.x / kTriangleWidth);
-	int minfy = std::floor(viewpoint.y / kTriangleHeight);
-
-	// If a view window is partially moved outside of the display, its 'rect' is
-	// adjusted to be fully contained on the screen - i.e. x = 0 and width is
-	// made smaller. Its offset is made negative to account for this change.
-	// To figure out which fields we need to draw, we have to add the absolute
-	// value of 'offset' to the actual dimension of the 'rect' to get to desired
-	// dimension of the 'rect'
-	const Vector2f br_map = MapviewPixelFunctions::panel_to_map(
-	   viewpoint, zoom, Vector2f(dst->get_rect().w + std::abs(dst->get_offset().x),
-	                             dst->get_rect().h + std::abs(dst->get_offset().y)));
-	int maxfx = std::ceil(br_map.x / kTriangleWidth);
-	int maxfy = std::ceil(br_map.y / kTriangleHeight);
-
-	// Adjust for triangle boundary effects and for height differences.
-	minfx -= 2;
-	maxfx += 2;
-	minfy -= 2;
-	maxfy += 10;
-
-	Surface* surface = dst->get_surface();
-	if (!surface)
-		return;
-
 	const Recti& bounding_rect = dst->get_rect();
-	const int surface_width = surface->width();
-	const int surface_height = surface->height();
-
-	const Map& map = egbase.map();
-	const uint32_t gametime = egbase.get_gametime();
-
-	const float scale = 1.f / zoom;
-	fields_to_draw_.reset(minfx, maxfx, minfy, maxfy);
-	for (int32_t fy = minfy; fy <= maxfy; ++fy) {
-		for (int32_t fx = minfx; fx <= maxfx; ++fx) {
-			FieldsToDraw::Field& f =
-			   *fields_to_draw_.mutable_field(fields_to_draw_.calculate_index(fx, fy));
-
-			f.geometric_coords = Coords(fx, fy);
-
-			f.ln_index = fields_to_draw_.calculate_index(fx - 1, fy);
-			f.rn_index = fields_to_draw_.calculate_index(fx + 1, fy);
-			f.trn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy - 1);
-			f.bln_index = fields_to_draw_.calculate_index(fx + (fy & 1) - 1, fy + 1);
-			f.brn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy + 1);
-
-			// Texture coordinates for pseudo random tiling of terrain and road
-			// graphics. Since screen space X increases top-to-bottom and OpenGL
-			// increases bottom-to-top we flip the y coordinate to not have
-			// terrains and road graphics vertically mirrorerd.
-			Vector2f map_pixel =
-			   MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords);
-			f.texture_coords.x = map_pixel.x / kTextureSideLength;
-			f.texture_coords.y = -map_pixel.y / kTextureSideLength;
-
-			Coords normalized = f.geometric_coords;
-			map.normalize_coords(normalized);
-			f.fcoords = map.get_fcoords(normalized);
-
-			map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
-
-			f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);
-			f.gl_position = f.surface_pixel = f.rendertarget_pixel +
-			                                  dst->get_rect().origin().cast<float>() +
-			                                  dst->get_offset().cast<float>();
-			pixel_to_gl_renderbuffer(
-			   surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);
-
-			f.brightness = field_brightness(f.fcoords, gametime, map, player);
-
-			PlayerNumber owned_by = f.fcoords.field->get_owned_by();
-			f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
-			f.is_border = f.fcoords.field->is_border();
-			f.vision = 2;
-			f.roads = f.fcoords.field->get_roads();
-			if (player && !player->see_all()) {
-				const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
-				f.roads = pf.roads;
-				f.vision = pf.vision;
-				if (pf.vision == 1) {
-					f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
-					f.is_border = pf.border;
-				}
-			}
-
-			const auto it = overlays.road_building_preview.find(f.fcoords);
-			if (it != overlays.road_building_preview.end()) {
-				f.roads |= it->second;
-			}
-		}
-	}
+	const Surface& surface = dst->get_surface();
+	const int surface_width = surface.width();
+	const int surface_height = surface.height();
 
 	// Enqueue the drawing of the terrain.
 	RenderQueue::Item i;
@@ -392,11 +106,11 @@
 	i.terrain_arguments.destination_rect =
 	   Rectf(bounding_rect.x, surface_height - bounding_rect.y - bounding_rect.h, bounding_rect.w,
 	         bounding_rect.h);
-	i.terrain_arguments.gametime = gametime;
+	i.terrain_arguments.gametime = egbase.get_gametime();
 	i.terrain_arguments.renderbuffer_width = surface_width;
 	i.terrain_arguments.renderbuffer_height = surface_height;
 	i.terrain_arguments.terrains = &egbase.world().terrains();
-	i.terrain_arguments.fields_to_draw = &fields_to_draw_;
+	i.terrain_arguments.fields_to_draw = &fields_to_draw;
 	i.terrain_arguments.scale = scale;
 	RenderQueue::instance().enqueue(i);
 
@@ -408,7 +122,4 @@
 	// Enqueue the drawing of the road layer.
 	i.program_id = RenderQueue::Program::kTerrainRoad;
 	RenderQueue::instance().enqueue(i);
-
-	draw_objects(egbase, scale, fields_to_draw_, player, overlays.text_to_draw, draw_immovables,
-	             draw_bobs, dst);
 }

=== modified file 'src/graphic/game_renderer.h'
--- src/graphic/game_renderer.h	2017-08-28 11:52:39 +0000
+++ src/graphic/game_renderer.h	2017-08-28 12:55:36 +0000
@@ -26,47 +26,21 @@
 #include "base/macros.h"
 #include "base/vector.h"
 #include "graphic/gl/fields_to_draw.h"
+#include "logic/editor_game_base.h"
 #include "logic/map_objects/draw_text.h"
-
-namespace Widelands {
-class Player;
-class EditorGameBase;
-}
-
-class RenderTarget;
-
-// Renders the MapView on screen.
-class GameRenderer {
-public:
-	struct Overlays {
-		TextToDraw text_to_draw;
-		std::map<Widelands::Coords, uint8_t> road_building_preview;
-	};
-
-	enum class DrawImmovables { kNo, kYes };
-	enum class DrawBobs { kNo, kYes };
-
-	GameRenderer();
-	~GameRenderer();
-
-	// Renders the map from a 'player's point of view (or omniscient if nullptr)
-	// into the given drawing window. The 'viewpoint' is the top left screens
-	// pixel map pixel and 'scale' is the magnification of the view.
-	void render(const Widelands::EditorGameBase& egbase,
-	            const Vector2f& viewpoint,
-	            float zoom,
-	            const Widelands::Player* player,
-	            const Overlays& overlays,
-	            const DrawImmovables& draw_immovables,
-	            const DrawBobs& draw_bobs,
-	            RenderTarget* dst);
-
-private:
-	// This is owned and handled by us, but handed to the RenderQueue, so we
-	// basically promise that this stays valid for one frame.
-	FieldsToDraw fields_to_draw_;
-
-	DISALLOW_COPY_AND_ASSIGN(GameRenderer);
-};
+#include "logic/player.h"
+
+// Draw the terrain only.
+void draw_terrain(const Widelands::EditorGameBase& egbase,
+						const FieldsToDraw& fields_to_draw,
+						const float scale,
+						RenderTarget* dst);
+
+// Draw the border stones for 'field' if it is a border and 'visibility' is
+// correct.
+void draw_border_markers(const FieldsToDraw::Field& field,
+					  const float scale,
+                 const FieldsToDraw& fields_to_draw,
+                 RenderTarget* dst);
 
 #endif  // end of include guard: WL_GRAPHIC_GAME_RENDERER_H

=== added file 'src/graphic/gl/fields_to_draw.cc'
--- src/graphic/gl/fields_to_draw.cc	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/fields_to_draw.cc	2017-08-28 12:55:36 +0000
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006-2017 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/fields_to_draw.h"
+
+#include "graphic/gl/coordinate_conversion.h"
+#include "logic/map_objects/world/terrain_description.h"
+#include "wui/mapviewpixelconstants.h"
+#include "wui/mapviewpixelfunctions.h"
+
+namespace  {
+
+// Returns the brightness value in [0, 1.] for 'fcoords'.
+float field_brightness(const Widelands::FCoords& fcoords) {
+	uint32_t brightness = 144 + fcoords.field->get_brightness();
+	brightness = std::min<uint32_t>(255, (brightness * 255) / 160);
+	return brightness / 255.;
+}
+
+}  // namespace
+
+void FieldsToDraw::reset(const Widelands::EditorGameBase& egbase,
+                         const Vector2f& viewpoint,
+                         const float zoom,
+                         RenderTarget* dst) {
+	assert(viewpoint.x >= 0);  // divisions involving negative numbers are bad
+	assert(viewpoint.y >= 0);
+	assert(dst->get_offset().x <= 0);
+	assert(dst->get_offset().y <= 0);
+
+	int minfx = std::floor(viewpoint.x / kTriangleWidth);
+	int minfy = std::floor(viewpoint.y / kTriangleHeight);
+
+	// If a view window is partially moved outside of the display, its 'rect' is
+	// adjusted to be fully contained on the screen - i.e. x = 0 and width is
+	// made smaller. Its offset is made negative to account for this change.
+	// To figure out which fields we need to draw, we have to add the absolute
+	// value of 'offset' to the actual dimension of the 'rect' to get to desired
+	// dimension of the 'rect'
+	const Vector2f br_map = MapviewPixelFunctions::panel_to_map(
+	   viewpoint, zoom, Vector2f(dst->get_rect().w + std::abs(dst->get_offset().x),
+	                             dst->get_rect().h + std::abs(dst->get_offset().y)));
+	int maxfx = std::ceil(br_map.x / kTriangleWidth);
+	int maxfy = std::ceil(br_map.y / kTriangleHeight);
+
+	// Adjust for triangle boundary effects and for height differences.
+	minfx -= 2;
+	maxfx += 2;
+	minfy -= 2;
+	maxfy += 10;
+
+	const auto& surface = dst->get_surface();
+	const int surface_width = surface.width();
+	const int surface_height = surface.height();
+
+	const Widelands::Map& map = egbase.map();
+
+	min_fx_ = minfx;
+	max_fx_ = maxfx;
+	min_fy_ = minfy;
+	max_fy_ = maxfy;
+	w_ = max_fx_ - min_fx_ + 1;
+	h_ = max_fy_ - min_fy_ + 1;
+	const size_t dimension = w_ * h_;
+	if (fields_.size() != dimension) {
+		fields_.resize(dimension);
+	}
+
+	for (int32_t fy = minfy; fy <= maxfy; ++fy) {
+		for (int32_t fx = minfx; fx <= maxfx; ++fx) {
+			FieldsToDraw::Field& f = fields_[calculate_index(fx, fy)];
+
+			f.geometric_coords = Widelands::Coords(fx, fy);
+
+			f.ln_index = calculate_index(fx - 1, fy);
+			f.rn_index = calculate_index(fx + 1, fy);
+			f.trn_index = calculate_index(fx + (fy & 1), fy - 1);
+			f.bln_index = calculate_index(fx + (fy & 1) - 1, fy + 1);
+			f.brn_index = calculate_index(fx + (fy & 1), fy + 1);
+
+			// Texture coordinates for pseudo random tiling of terrain and road
+			// graphics. Since screen space X increases top-to-bottom and OpenGL
+			// increases bottom-to-top we flip the y coordinate to not have
+			// terrains and road graphics vertically mirrorerd.
+			Vector2f map_pixel =
+			   MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords);
+			f.texture_coords.x = map_pixel.x / Widelands::kTextureSideLength;
+			f.texture_coords.y = -map_pixel.y / Widelands::kTextureSideLength;
+
+			Widelands::Coords normalized = f.geometric_coords;
+			map.normalize_coords(normalized);
+			f.fcoords = map.get_fcoords(normalized);
+
+			map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
+
+			f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);
+			f.gl_position = f.surface_pixel = f.rendertarget_pixel +
+			                                  dst->get_rect().origin().cast<float>() +
+			                                  dst->get_offset().cast<float>();
+			pixel_to_gl_renderbuffer(
+			   surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);
+
+			f.brightness = field_brightness(f.fcoords);
+
+			Widelands::PlayerNumber owned_by = f.fcoords.field->get_owned_by();
+			f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
+			f.is_border = f.fcoords.field->is_border();
+			f.vision = 2;
+			f.roads = f.fcoords.field->get_roads();
+		}
+	}
+}

=== modified file 'src/graphic/gl/fields_to_draw.h'
--- src/graphic/gl/fields_to_draw.h	2017-08-09 17:53:24 +0000
+++ src/graphic/gl/fields_to_draw.h	2017-08-28 12:55:36 +0000
@@ -28,13 +28,12 @@
 #include <stdint.h>
 
 #include "base/vector.h"
-#include "logic/map_objects/tribes/road_textures.h"
-#include "logic/player.h"
+#include "graphic/rendertarget.h"
+#include "logic/editor_game_base.h"
 #include "logic/widelands.h"
 #include "logic/widelands_geometry.h"
 
-// Helper struct that contains the data needed for drawing all fields. All
-// methods are inlined for performance reasons.
+// Helper struct that contains the data needed for drawing all fields.
 class FieldsToDraw {
 public:
 	static constexpr int kInvalidIndex = std::numeric_limits<int>::min();
@@ -77,39 +76,11 @@
 		}
 	};
 
-	FieldsToDraw() {
-		// Initialize everything to make cppcheck happy.
-		reset(0, 0, 0, 0);
-	}
-
-	// Resize this fields to draw for reuse.
-	void reset(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;
-		const size_t dimension = w_ * h_;
-		if (fields_.size() != dimension) {
-			fields_.resize(dimension);
-		}
-	}
-
-	// Calculates the index of the given field with ('fx', 'fy') being geometric
-	// coordinates in the map. Returns kInvalidIndex 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 kInvalidIndex;
-		}
-		uint16_t yidx = fy - min_fy_;
-		if (yidx >= h_) {
-			return kInvalidIndex;
-		}
-		return yidx * w_ + xidx;
-	}
+	// Reinitialize for the given view parameters.
+	void reset(const Widelands::EditorGameBase& egbase,
+	           const Vector2f& viewpoint,
+	           const float zoom,
+	           RenderTarget* dst);
 
 	// The number of fields to draw.
 	inline size_t size() const {
@@ -127,15 +98,30 @@
 	}
 
 private:
+	// Calculates the index of the given field with ('fx', 'fy') being geometric
+	// coordinates in the map. Returns kInvalidIndex 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 kInvalidIndex;
+		}
+		uint16_t yidx = fy - min_fy_;
+		if (yidx >= h_) {
+			return kInvalidIndex;
+		}
+		return yidx * w_ + xidx;
+	}
+
 	// Minimum and maximum field coordinates (geometric) to render. Can be negative.
-	int min_fx_;
-	int max_fx_;
-	int min_fy_;
-	int max_fy_;
+	int min_fx_ = 0;
+	int max_fx_ = 0;
+	int min_fy_ = 0;
+	int max_fy_ = 0;
 
 	// Width and height in number of fields.
-	int w_;
-	int h_;
+	int w_ = 0;
+	int h_ = 0;
 
 	std::vector<Field> fields_;
 };

=== modified file 'src/graphic/gl/road_program.cc'
--- src/graphic/gl/road_program.cc	2017-05-19 06:40:44 +0000
+++ src/graphic/gl/road_program.cc	2017-08-28 12:55:36 +0000
@@ -29,6 +29,7 @@
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
 #include "graphic/texture.h"
+#include "logic/player.h"
 #include "logic/roadtype.h"
 
 // We target OpenGL 2.1 for the desktop here.

=== modified file 'src/graphic/render_queue.h'
--- src/graphic/render_queue.h	2017-08-09 17:53:24 +0000
+++ src/graphic/render_queue.h	2017-08-28 12:55:36 +0000
@@ -118,7 +118,7 @@
 		int renderbuffer_width = 0;
 		int renderbuffer_height = 0;
 		const DescriptionMaintainer<Widelands::TerrainDescription>* terrains = nullptr;
-		FieldsToDraw* fields_to_draw = nullptr;
+		const FieldsToDraw* fields_to_draw = nullptr;
 		float scale = 1.f;
 		Rectf destination_rect = Rectf(0.f, 0.f, 0.f, 0.f);
 	};

=== modified file 'src/graphic/rendertarget.cc'
--- src/graphic/rendertarget.cc	2017-05-21 19:07:27 +0000
+++ src/graphic/rendertarget.cc	2017-08-28 12:55:36 +0000
@@ -28,8 +28,7 @@
 /**
  * Build a render target for the given surface.
  */
-RenderTarget::RenderTarget(Surface* surf) {
-	surface_ = surf;
+RenderTarget::RenderTarget(Surface* surf) : surface_(surf) {
 	reset();
 }
 

=== modified file 'src/graphic/rendertarget.h'
--- src/graphic/rendertarget.h	2017-06-24 08:47:46 +0000
+++ src/graphic/rendertarget.h	2017-08-28 12:55:36 +0000
@@ -120,8 +120,8 @@
 
 	void reset();
 
-	Surface* get_surface() const {
-		return surface_;
+	const Surface& get_surface() const {
+		return *surface_;
 	}
 	const Recti& get_rect() const {
 		return rect_;
@@ -143,7 +143,7 @@
 	                       const int percent_from_bottom = 100);
 
 	/// The target surface
-	Surface* surface_;
+	Surface* const surface_;
 	/// The current clip rectangle
 	Recti rect_;
 	/// Drawing offset

=== modified file 'src/logic/widelands_geometry.h'
--- src/logic/widelands_geometry.h	2017-08-12 06:51:08 +0000
+++ src/logic/widelands_geometry.h	2017-08-28 12:55:36 +0000
@@ -122,6 +122,7 @@
 	Field* field;
 };
 
+// TODO(sirver): This should not derive from CoordsType. Replace with NodeAndTriangle.
 template <typename CoordsType = Coords> struct TCoords : public CoordsType {
 	enum TriangleIndex { D, R, None };
 

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2017-08-20 16:12:28 +0000
+++ src/wui/CMakeLists.txt	2017-08-28 12:55:36 +0000
@@ -106,6 +106,7 @@
     base_macros
     base_math
     graphic
+    graphic_fields_to_draw
     graphic_game_renderer
     logic
     logic_widelands_geometry

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2017-08-28 11:52:39 +0000
+++ src/wui/interactive_player.cc	2017-08-28 12:55:36 +0000
@@ -58,6 +58,146 @@
 using Widelands::Building;
 using Widelands::Map;
 
+namespace  {
+
+// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for
+// 'pf'. See 'field_brightness' in fields_to_draw.cc for scale of values.
+float adjusted_field_brightness(const Widelands::FCoords& fcoords,
+                       const uint32_t gametime,
+                       const Widelands::Player::Field& pf) {
+	if (pf.vision == 0) {
+		return 0.;
+	};
+
+	uint32_t brightness = 144 + fcoords.field->get_brightness();
+	brightness = std::min<uint32_t>(255, (brightness * 255) / 160);
+
+	if (pf.vision == 1) {
+		static const uint32_t kDecayTimeInMs = 20000;
+		const Widelands::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 / 255.;
+}
+
+void draw_immovables_for_visible_field(const Widelands::EditorGameBase& egbase,
+                                       const FieldsToDraw::Field& field,
+                                       const float scale,
+                                       const TextToDraw text_to_draw,
+                                       const Widelands::Player& player,
+                                       RenderTarget* dst) {
+	Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
+	if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) {
+		TextToDraw draw_text_for_this_immovable = text_to_draw;
+		const Widelands::Player* owner = imm->get_owner();
+		if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
+			draw_text_for_this_immovable =
+			   static_cast<TextToDraw>(draw_text_for_this_immovable & ~TextToDraw::kStatistics);
+		}
+		imm->draw(
+		   egbase.get_gametime(), draw_text_for_this_immovable, field.rendertarget_pixel, scale, dst);
+	}
+}
+
+void draw_bobs_for_visible_field(const Widelands::EditorGameBase& egbase,
+                                 const FieldsToDraw::Field& field,
+                                 const float scale,
+                                 const TextToDraw text_to_draw,
+                                 const Widelands::Player& player,
+                                 RenderTarget* dst) {
+	for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob; bob = bob->get_next_bob()) {
+		TextToDraw draw_text_for_this_bob = text_to_draw;
+		const Widelands::Player* owner = bob->get_owner();
+		if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
+			draw_text_for_this_bob =
+			   static_cast<TextToDraw>(draw_text_for_this_bob & ~TextToDraw::kStatistics);
+		}
+		bob->draw(egbase, draw_text_for_this_bob, field.rendertarget_pixel, scale, dst);
+	}
+}
+
+void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field,
+                                                const Widelands::Player::Field& player_field,
+                                                const float scale,
+                                                RenderTarget* dst) {
+	if (const Widelands::MapObjectDescr* const map_object_descr =
+	       player_field.map_object_descr[Widelands::TCoords<>::None]) {
+		if (player_field.constructionsite.becomes) {
+			assert(field.owner != nullptr);
+			const Widelands::ConstructionsiteInformation& csinf = player_field.constructionsite;
+			// draw the partly finished constructionsite
+			uint32_t anim_idx;
+			try {
+				anim_idx = csinf.becomes->get_animation("build");
+			} catch (Widelands::MapObjectDescr::AnimationNonexistent&) {
+				try {
+					anim_idx = csinf.becomes->get_animation("unoccupied");
+				} catch (Widelands::MapObjectDescr::AnimationNonexistent) {
+					anim_idx = csinf.becomes->get_animation("idle");
+				}
+			}
+			const Animation& anim = g_gr->animations().get_animation(anim_idx);
+			const size_t nr_frames = anim.nr_frames();
+			uint32_t cur_frame =
+			   csinf.totaltime ? csinf.completedtime * nr_frames / csinf.totaltime : 0;
+			uint32_t tanim = cur_frame * FRAME_LENGTH;
+
+			uint32_t percent = 100 * csinf.completedtime * nr_frames;
+			if (csinf.totaltime) {
+				percent /= csinf.totaltime;
+			}
+			percent -= 100 * cur_frame;
+
+			if (cur_frame) {  // not the first frame
+				// Draw the prev frame
+				dst->blit_animation(field.rendertarget_pixel, scale, anim_idx, tanim - FRAME_LENGTH,
+				                    field.owner->get_playercolor());
+			} else if (csinf.was) {
+				// Is the first frame, but there was another building here before,
+				// get its last build picture and draw it instead.
+				uint32_t a;
+				try {
+					a = csinf.was->get_animation("unoccupied");
+				} catch (Widelands::MapObjectDescr::AnimationNonexistent&) {
+					a = csinf.was->get_animation("idle");
+				}
+				dst->blit_animation(field.rendertarget_pixel, scale, a, tanim - FRAME_LENGTH,
+				                    field.owner->get_playercolor());
+			}
+			dst->blit_animation(field.rendertarget_pixel, scale, anim_idx, tanim,
+			                    field.owner->get_playercolor(), percent);
+		} else if (upcast(const Widelands::BuildingDescr, building, map_object_descr)) {
+			assert(field.owner != nullptr);
+			// this is a building therefore we either draw unoccupied or idle animation
+			uint32_t pic;
+			try {
+				pic = building->get_animation("unoccupied");
+			} catch (Widelands::MapObjectDescr::AnimationNonexistent&) {
+				pic = building->get_animation("idle");
+			}
+			dst->blit_animation(
+			   field.rendertarget_pixel, scale, pic, 0, field.owner->get_playercolor());
+		} else if (map_object_descr->type() == Widelands::MapObjectType::FLAG) {
+			assert(field.owner != nullptr);
+			dst->blit_animation(field.rendertarget_pixel, scale, field.owner->tribe().flag_animation(),
+			                    0, field.owner->get_playercolor());
+		} else if (const uint32_t pic = map_object_descr->main_animation()) {
+			if (field.owner != nullptr) {
+				dst->blit_animation(
+				   field.rendertarget_pixel, scale, pic, 0, field.owner->get_playercolor());
+			} else {
+				dst->blit_animation(field.rendertarget_pixel, scale, pic, 0);
+			}
+		}
+	}
+}
+
+}  // namespace
+
 InteractivePlayer::InteractivePlayer(Widelands::Game& g,
                                      Section& global_s,
                                      Widelands::PlayerNumber const plyn,
@@ -181,9 +321,63 @@
 }
 
 void InteractivePlayer::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
-	const GameRenderer::Overlays overlays{get_text_to_draw(), road_building_preview()};
-	given_map_view->draw_map_view(egbase(), overlays, GameRenderer::DrawImmovables::kYes,
-	                              GameRenderer::DrawBobs::kYes, &player(), dst);
+	const Widelands::Player& plr = player();
+	const auto& gbase = egbase();
+	const Widelands::Map& map = gbase.map();
+	const uint32_t gametime = gbase.get_gametime();
+
+	auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst);
+	const auto& roads_preview = road_building_preview();
+
+	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
+		auto* f = fields_to_draw->mutable_field(idx);
+
+		const Widelands::Player::Field& player_field =
+		   plr.fields()[map.get_index(f->fcoords, map.get_width())];
+
+		// Adjust this field for visibility for this player.
+		if (!plr.see_all()) {
+			f->brightness = adjusted_field_brightness(f->fcoords, gametime, player_field);
+			f->roads = player_field.roads;
+			f->vision = player_field.vision;
+			if (player_field.vision == 0) {
+				// If the player cannot see the field, no need to do any more work.
+				continue;
+			} else if (player_field.vision == 1) {
+				f->owner = player_field.owner != 0 ? &gbase.player(player_field.owner) : nullptr;
+				f->is_border = player_field.border;
+			}
+		}
+
+		// Add road building overlays if applicable.
+		const auto it = roads_preview.find(f->fcoords);
+		if (it != roads_preview.end()) {
+			f->roads |= it->second;
+		}
+
+		const float scale = 1.f / given_map_view->view().zoom;
+		draw_border_markers(*f, scale, *fields_to_draw, dst);
+
+		// Render stuff that belongs to the node.
+		if (f->vision > 1) {
+			const auto text_to_draw = get_text_to_draw();
+			draw_immovables_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
+			draw_bobs_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
+		} else if (f->vision == 1) {
+			// We never show census or statistics for objects in the fog.
+			draw_immovables_for_formerly_visible_field(*f, player_field, scale, dst);
+		}
+
+		// TODO(sirver): Do not use the field_overlay_manager, instead draw the
+		// overlays we are interested in here directly.
+		field_overlay_manager().foreach_overlay(
+		   f->fcoords, [dst, f, scale](const Image* pic, const Vector2i& hotspot) {
+			   dst->blitrect_scale(Rectf(f->rendertarget_pixel - hotspot.cast<float>() * scale,
+			                             pic->width() * scale, pic->height() * scale),
+			                       pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
+			                       BlendMode::UseAlpha);
+			});
+	}
 }
 
 void InteractivePlayer::popup_message(Widelands::MessageId const id,

=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc	2017-08-28 07:39:59 +0000
+++ src/wui/interactive_spectator.cc	2017-08-28 12:55:36 +0000
@@ -109,9 +109,40 @@
 }
 
 void InteractiveSpectator::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
-	const GameRenderer::Overlays overlays{get_text_to_draw(), road_building_preview()};
-	given_map_view->draw_map_view(egbase(), overlays, GameRenderer::DrawImmovables::kYes,
-	                              GameRenderer::DrawBobs::kYes, nullptr, dst);
+	// A spectator cannot build roads.
+	assert(road_building_preview().empty());
+
+	const auto& gbase = egbase();
+	auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst);
+	const float scale = 1.f / given_map_view->view().zoom;
+	const uint32_t gametime = gbase.get_gametime();
+
+	const auto text_to_draw = get_text_to_draw();
+	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
+		const FieldsToDraw::Field& field = fields_to_draw->at(idx);
+
+		draw_border_markers(field, scale, *fields_to_draw, dst);
+
+		Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
+		if (imm != nullptr && imm->get_positions(gbase).front() == field.fcoords) {
+			imm->draw(gametime, text_to_draw, field.rendertarget_pixel, scale, dst);
+		}
+
+		for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
+		     bob = bob->get_next_bob()) {
+			bob->draw(gbase, text_to_draw, field.rendertarget_pixel, scale, dst);
+		}
+
+		// TODO(sirver): Do not use the field_overlay_manager, instead draw the
+		// overlays we are interested in here directly.
+		field_overlay_manager().foreach_overlay(
+		   field.fcoords, [dst, &field, scale](const Image* pic, const Vector2i& hotspot) {
+			   dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale,
+			                             pic->width() * scale, pic->height() * scale),
+			                       pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
+			                       BlendMode::UseAlpha);
+			});
+	}
 }
 
 /**

=== modified file 'src/wui/mapview.cc'
--- src/wui/mapview.cc	2017-08-19 20:55:57 +0000
+++ src/wui/mapview.cc	2017-08-28 12:55:36 +0000
@@ -25,8 +25,6 @@
 #include "base/math.h"
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
-#include "logic/map_objects/draw_text.h"
-#include "logic/player.h"
 #include "wlapplication.h"
 #include "wui/mapviewpixelfunctions.h"
 
@@ -294,7 +292,6 @@
    UI::Panel* parent, const Widelands::Map& map, int32_t x, int32_t y, uint32_t w, uint32_t h)
    : UI::Panel(parent, x, y, w, h),
      map_(map),
-     renderer_(new GameRenderer()),
      view_(),
      last_mouse_pos_(Vector2i::zero()),
      dragging_(false) {
@@ -339,12 +336,7 @@
 	NEVER_HERE();
 }
 
-void MapView::draw_map_view(const Widelands::EditorGameBase& egbase,
-                            const GameRenderer::Overlays& overlays,
-                            const GameRenderer::DrawImmovables& draw_immovables,
-                            const GameRenderer::DrawBobs& draw_bobs,
-                            const Widelands::Player* player,
-                            RenderTarget* dst) {
+FieldsToDraw* MapView::draw_terrain(const Widelands::EditorGameBase& egbase, RenderTarget* dst) {
 	uint32_t now = SDL_GetTicks();
 	while (!view_plans_.empty()) {
 		auto& plan = view_plans_.front();
@@ -384,8 +376,10 @@
 		break;
 	}
 
-	renderer_->render(
-	   egbase, view_.viewpoint, view_.zoom, player, overlays, draw_immovables, draw_bobs, dst);
+	fields_to_draw_.reset(egbase, view_.viewpoint, view_.zoom, dst);
+	const float scale = 1.f / view_.zoom;
+	::draw_terrain(egbase, fields_to_draw_, scale, dst);
+	return &fields_to_draw_;
 }
 
 void MapView::set_view(const View& target_view, const Transition& transition) {

=== modified file 'src/wui/mapview.h'
--- src/wui/mapview.h	2017-08-19 20:55:57 +0000
+++ src/wui/mapview.h	2017-08-28 12:55:36 +0000
@@ -29,12 +29,11 @@
 #include "base/rect.h"
 #include "base/vector.h"
 #include "graphic/game_renderer.h"
+#include "graphic/gl/fields_to_draw.h"
 #include "logic/map.h"
 #include "logic/widelands_geometry.h"
 #include "ui_basic/panel.h"
 
-class GameRenderer;
-
 /**
  * Implements a view of a map. It is used to render a valid map on the screen.
  */
@@ -159,13 +158,12 @@
 	// True if a 'Transition::Smooth' animation is playing.
 	bool is_animating() const;
 
+	// Schedules drawing of the terrain of this MapView. The returned value can
+	// be used to override contents of 'fields_to_draw' for player knowledge and
+	// visibility, and to correctly draw map objects, overlays and text.
+	FieldsToDraw* draw_terrain(const Widelands::EditorGameBase& egbase, RenderTarget* dst);
+
 	// Not overriden from UI::Panel, instead we expect to be passed the data through.
-	void draw_map_view(const Widelands::EditorGameBase& egbase,
-	                   const GameRenderer::Overlays& overlays,
-	                   const GameRenderer::DrawImmovables& draw_immovables,
-	                   const GameRenderer::DrawBobs& draw_bobs,
-	                   const Widelands::Player* player,
-	                   RenderTarget* dst);
 	bool handle_mousepress(uint8_t btn, int32_t x, int32_t y);
 	bool handle_mouserelease(uint8_t btn, int32_t x, int32_t y);
 	bool handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff);
@@ -190,7 +188,11 @@
 	Vector2f to_map(const Vector2i& panel_pixel) const;
 
 	const Widelands::Map& map_;
-	std::unique_ptr<GameRenderer> renderer_;
+
+	// This is owned and handled by us, but handed to the RenderQueue, so we
+	// basically promise that this stays valid for one frame.
+	FieldsToDraw fields_to_draw_;
+
 	View view_;
 	Vector2i last_mouse_pos_;
 	bool dragging_;


References