← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)

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

Suggested commit message:

- Adds a TextureAtlas bin packer and packs the terrain textures into one OpenGL texture. This needed more features in class Texture: a Texture can now also be a rectangle in another, bigger texture.
- Changed the terrain rendering to use this new texture. This is the fastest terrain rendering we ever had.
- class Graphic no longer owns map textures and animates them. They are owned by their TerrainDescription class and animation is done through handing gametime through to this class. This cleaned it up quite a bit.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/texture_atlas into lp:widelands.
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2014-11-23 14:34:38 +0000
+++ src/editor/editorinteractive.cc	2014-11-28 07:21:07 +0000
@@ -243,8 +243,6 @@
 	frametime = m_realtime - lasttime;
 
 	egbase().get_gametime_pointer() += frametime;
-
-	g_gr->animate_maptextures(egbase().get_gametime());
 }
 
 

=== modified file 'src/editor/tools/editor_info_tool.cc'
--- src/editor/tools/editor_info_tool.cc	2014-10-13 10:48:33 +0000
+++ src/editor/tools/editor_info_tool.cc	2014-11-28 07:21:07 +0000
@@ -99,7 +99,6 @@
 	   center.triangle.t == Widelands::TCoords<>::D ? tf.terrain_d() : tf.terrain_r());
 
 	buf += "• " + (boost::format(_("Name: %s")) % ter.descname()).str() + "\n";
-	buf += "• " + (boost::format(_("Texture Number: %i")) % ter.get_texture()).str() + "\n";
 
 	// *** Resources info
 	buf += std::string("\n") + _("Resources:") + "\n";

=== modified file 'src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc'
--- src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2014-11-24 07:25:21 +0000
+++ src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2014-11-28 07:21:07 +0000
@@ -30,7 +30,6 @@
 #include "graphic/graphic.h"
 #include "graphic/in_memory_image.h"
 #include "graphic/rendertarget.h"
-#include "graphic/terrain_texture.h"
 #include "graphic/texture.h"
 #include "logic/map.h"
 #include "logic/world/editor_category.h"
@@ -75,11 +74,12 @@
 		if (ter_is != check[checkfor])
 			continue;
 
-		const Image* tex = g_gr->images().get(
-		   g_gr->get_maptexture_data(terrain_descr.get_texture())->get_texture_image());
-		Texture* texture = new Texture(tex->width(), tex->height());
-		texture->blit(Point(0, 0), tex->texture(), Rect(0, 0, tex->width(), tex->height()), BlendMode::Copy);
-		Point pt(1, tex->height() - kSmallPicHeight - 1);
+		const Texture& terrain_texture = terrain_descr.get_texture(0);
+		Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height());
+		texture->blit(Point(0, 0),
+		              &terrain_texture,
+		              Rect(0, 0, terrain_texture.width(), terrain_texture.height()));
+		Point pt(1, terrain_texture.height() - kSmallPicHeight - 1);
 
 		if (ter_is == TerrainDescription::GREEN) {
 			texture->blit(pt, green->texture(), Rect(0, 0, green->width(), green->height()));

=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2014-11-24 07:25:21 +0000
+++ src/graphic/CMakeLists.txt	2014-11-28 07:21:07 +0000
@@ -28,7 +28,6 @@
     image_cache.cc
     image_cache.h
   USES_SDL2
-  USES_SDL2_IMAGE
   DEPENDS
     base_log
     base_macros
@@ -68,6 +67,16 @@
     graphic_sdl_utils
 )
 
+wl_library(graphic_texture_atlas
+  SRCS
+    texture_atlas.h
+    texture_atlas.cc
+  DEPENDS
+    base_exceptions
+    base_macros
+    graphic_surface
+)
+
 wl_library(graphic
   SRCS
     align.cc
@@ -116,8 +125,6 @@
     rendertarget.h
     richtext.cc
     richtext.h
-    terrain_texture.cc
-    terrain_texture.h
     text_parser.cc
     text_parser.h
     wordwrap.cc
@@ -125,7 +132,6 @@
   USES_OPENGL
   USES_SDL2
   USES_SDL2_GFX
-  USES_SDL2_IMAGE
   USES_SDL2_TTF
   DEPENDS
     base_deprecated
@@ -150,7 +156,6 @@
     profile
     scripting
     sound
-    ui_basic
     wui
     wui_text_layout
 )

=== modified file 'src/graphic/gl/dither_program.cc'
--- src/graphic/gl/dither_program.cc	2014-11-24 07:10:03 +0000
+++ src/graphic/gl/dither_program.cc	2014-11-28 07:21:07 +0000
@@ -21,35 +21,33 @@
 
 #include "base/wexception.h"
 #include "graphic/gl/fields_to_draw.h"
-#include "graphic/graphic.h"
 #include "graphic/image_io.h"
-#include "graphic/terrain_texture.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_dither_texture_position;
 attribute vec2 attr_position;
+attribute vec2 attr_texture_offset;
 attribute vec2 attr_texture_position;
-attribute vec2 attr_dither_texture_position;
 
 // Output of vertex shader.
+varying float var_brightness;
+varying vec2 var_dither_texture_position;
+varying vec2 var_texture_offset;
 varying vec2 var_texture_position;
-varying vec2 var_dither_texture_position;
-varying float var_brightness;
 
 void main() {
+	var_brightness = attr_brightness;
+	var_dither_texture_position = attr_dither_texture_position;
+	var_texture_offset = attr_texture_offset;
 	var_texture_position = attr_texture_position;
-	var_dither_texture_position = attr_dither_texture_position;
-	var_brightness = attr_brightness;
 	gl_Position = vec4(attr_position, 0., 1.);
 }
 )";
@@ -59,52 +57,62 @@
 
 uniform sampler2D u_dither_texture;
 uniform sampler2D u_terrain_texture;
+uniform vec2 u_texture_dimensions;
 
 varying float var_brightness;
+varying vec2 var_dither_texture_position;
 varying vec2 var_texture_position;
-varying vec2 var_dither_texture_position;
+varying vec2 var_texture_offset;
 
 void main() {
-	vec4 clr = texture2D(u_terrain_texture, var_texture_position);
+	vec4 clr = texture2D(u_terrain_texture,
+			var_texture_offset + u_texture_dimensions * fract(var_texture_position));
 	clr.rgb *= var_brightness;
 	clr.a = 1. - texture2D(u_dither_texture, var_dither_texture_position).a;
 	gl_FragColor = clr;
 }
 )";
 
-
 }  // 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_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");
+	attr_texture_offset_ = glGetAttribLocation(gl_program_.object(), "attr_texture_offset");
+	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");
-
-	SDL_Surface* sdlsurf = load_image_as_sdl_surface("world/pics/edge.png", g_fs);
-	dither_mask_.reset(new Texture(sdlsurf, true));
+	u_texture_dimensions_ = glGetUniformLocation(gl_program_.object(), "u_texture_dimensions");
+
+	dither_mask_.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
+
+	glBindTexture(GL_TEXTURE_2D, dither_mask_->get_gl_texture());
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glBindTexture(GL_TEXTURE_2D, 0);
 }
 
 DitherProgram::~DitherProgram() {}
 
 void DitherProgram::add_vertex(const FieldsToDraw::Field& field,
                                const int order_index,
-                               const int terrain) {
-	vertices_[terrain].emplace_back();
-	PerVertexData& back = vertices_[terrain].back();
+                               const FloatPoint& texture_offset) {
+	vertices_.emplace_back();
+	PerVertexData& back = vertices_.back();
 
 	back.gl_x = field.gl_x;
 	back.gl_y = field.gl_y;
 	back.texture_x = field.texture_x;
 	back.texture_y = field.texture_y;
 	back.brightness = field.brightness;
+	back.texture_offset_x = texture_offset.x;
+	back.texture_offset_y = texture_offset.y;
 
 	switch (order_index) {
 	case 0:
@@ -125,6 +133,7 @@
 }
 
 void DitherProgram::maybe_add_dithering_triangle(
+   const uint32_t gametime,
    const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
    const FieldsToDraw& fields_to_draw,
    const int idx1,
@@ -135,29 +144,82 @@
 	if (my_terrain == other_terrain) {
 		return;
 	}
+	const Widelands::TerrainDescription& other_terrain_description =
+	   terrains.get_unmutable(other_terrain);
 	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);
+	    other_terrain_description.dither_layer()) {
+		const FloatPoint texture_offset =
+		   other_terrain_description.get_texture(gametime).texture_coordinates().top_left();
+		add_vertex(fields_to_draw.at(idx1), 0, texture_offset);
+		add_vertex(fields_to_draw.at(idx2), 1, texture_offset);
+		add_vertex(fields_to_draw.at(idx3), 2, texture_offset);
 	}
 }
 
-void DitherProgram::draw(const DescriptionMaintainer<TerrainDescription>& terrains,
-                         const FieldsToDraw& fields_to_draw) {
+void DitherProgram::gl_draw(int gl_texture, float texture_w, float texture_h) {
 	glUseProgram(gl_program_.object());
 
 	glEnableVertexAttribArray(attr_brightness_);
 	glEnableVertexAttribArray(attr_dither_texture_position_);
 	glEnableVertexAttribArray(attr_position_);
+	glEnableVertexAttribArray(attr_texture_offset_);
 	glEnableVertexAttribArray(attr_texture_position_);
 
-	if (vertices_.size() != terrains.size()) {
-		vertices_.resize(terrains.size());
-	}
-	for (auto& container : vertices_) {
-		container.clear();
-	}
+	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_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, gl_x));
+	set_attrib_pointer(attr_texture_offset_, 2, offsetof(PerVertexData, texture_offset_x));
+	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);
+	glBindTexture(GL_TEXTURE_2D, dither_mask_->get_gl_texture());
+
+	glActiveTexture(GL_TEXTURE1);
+	glBindTexture(GL_TEXTURE_2D, gl_texture);
+
+	glUniform1i(u_dither_texture_, 0);
+	glUniform1i(u_terrain_texture_, 1);
+	glUniform2f(u_texture_dimensions_, texture_w, texture_h);
+
+	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
+
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, 0);
+
+	glDisableVertexAttribArray(attr_brightness_);
+	glDisableVertexAttribArray(attr_dither_texture_position_);
+	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_texture_offset_);
+	glDisableVertexAttribArray(attr_texture_position_);
+}
+
+void DitherProgram::draw(const uint32_t gametime,
+                         const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+                         const FieldsToDraw& fields_to_draw) {
+	// This method expects that all terrains have the same dimensions and that
+	// all are packed into the same texture atlas, i.e. all are in the same GL
+	// texture. It does not check for this invariance for speeds sake.
+
+	vertices_.clear();
+	vertices_.reserve(fields_to_draw.size() * 3);
 
 	for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
 		const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
@@ -174,17 +236,17 @@
 		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,
+			maybe_add_dithering_triangle(gametime, 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,
+			maybe_add_dithering_triangle(gametime, 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,
+				maybe_add_dithering_triangle(gametime, terrains, fields_to_draw,
 				   current_index, bln_index, brn_index, field.ter_d, terrain_l);
 			}
 		}
@@ -192,70 +254,22 @@
 		// 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,
+			maybe_add_dithering_triangle(gametime, 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);
+			maybe_add_dithering_triangle(gametime, 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);
+				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,
+				maybe_add_dithering_triangle(gametime, 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, dither_mask_->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())
-		                 ->texture()
-		                 .get_gl_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, gl_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);
+	const Texture& texture = terrains.get_unmutable(0).get_texture(0);
+	gl_draw(texture.get_gl_texture(), texture.texture_coordinates().w, texture.texture_coordinates().h);
 }

=== modified file 'src/graphic/gl/dither_program.h'
--- src/graphic/gl/dither_program.h	2014-11-24 07:10:03 +0000
+++ src/graphic/gl/dither_program.h	2014-11-28 07:21:07 +0000
@@ -22,6 +22,7 @@
 
 #include <memory>
 
+#include "base/point.h"
 #include "graphic/gl/fields_to_draw.h"
 #include "graphic/gl/utils.h"
 #include "logic/description_maintainer.h"
@@ -35,14 +36,16 @@
 	~DitherProgram();
 
 	// Draws the terrain.
-	void draw(const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+	void draw(uint32_t gametime,
+	          const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
 	          const FieldsToDraw& fields_to_draw);
 
 private:
-	// Adds the triangle between the indexes (which index 'fields_to_draw' to
+	// 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(
+	   uint32_t gametime,
 	   const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
 	   const FieldsToDraw& fields_to_draw,
 	   int idx1,
@@ -51,10 +54,10 @@
 	   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);
+	// Adds the 'field' as an vertex to the 'vertices_'. The 'order_index'
+	// defines which texture position in the dithering texture will be used for
+	// this vertex.
+	void add_vertex(const FieldsToDraw::Field& field, int order_index, const FloatPoint& texture_offset);
 
 	struct PerVertexData {
 		float gl_x;
@@ -64,8 +67,13 @@
 		float brightness;
 		float dither_texture_x;
 		float dither_texture_y;
+		float texture_offset_x;
+		float texture_offset_y;
 	};
 
+	// Call through to GL.
+	void gl_draw(int gl_texture, float texture_w, float texture_h);
+
 	// The program used for drawing the terrain.
 	Gl::Program gl_program_;
 
@@ -73,22 +81,23 @@
 	Gl::Buffer gl_array_buffer_;
 
 	// Attributes.
+	GLint attr_brightness_;
+	GLint attr_dither_texture_position_;
 	GLint attr_position_;
+	GLint attr_texture_offset_;
 	GLint attr_texture_position_;
-	GLint attr_dither_texture_position_;
-	GLint attr_brightness_;
 
 	// Uniforms.
+	GLint u_dither_texture_;
 	GLint u_terrain_texture_;
-	GLint u_dither_texture_;
+	GLint u_texture_dimensions_;
 
 	// The texture mask for the dithering step.
 	std::unique_ptr<Texture> dither_mask_;
 
 	// 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_;
+	// could theoretically also always be recreated.
+	std::vector<PerVertexData> vertices_;
 };
 
 #endif  // end of include guard: WL_GRAPHIC_GL_DITHER_PROGRAM_H

=== modified file 'src/graphic/gl/game_renderer.cc'
--- src/graphic/gl/game_renderer.cc	2014-11-24 06:21:16 +0000
+++ src/graphic/gl/game_renderer.cc	2014-11-28 07:21:07 +0000
@@ -28,7 +28,6 @@
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
 #include "graphic/surface.h"
-#include "graphic/terrain_texture.h"
 #include "logic/editor_game_base.h"
 #include "logic/player.h"
 #include "logic/world/world.h"
@@ -165,8 +164,8 @@
 			map.normalize_coords(coords);
 			const FCoords& fcoords = map.get_fcoords(coords);
 
-			f.texture_x = float(x) / kTextureWidth;
-			f.texture_y = float(y) / kTextureHeight;
+			f.texture_x = float(x) / kTextureSideLength;
+			f.texture_y = float(y) / kTextureSideLength;
 
 			f.gl_x = f.pixel_x = x + surface_offset.x;
 			f.gl_y = f.pixel_y = y + surface_offset.y - fcoords.field->get_height() * HEIGHT_FACTOR;
@@ -182,8 +181,8 @@
 	}
 
 	const World& world = m_egbase->world();
-	terrain_program_->draw(world.terrains(), fields_to_draw);
-	dither_program_->draw(world.terrains(), fields_to_draw);
+	terrain_program_->draw(gametime, world.terrains(), fields_to_draw);
+	dither_program_->draw(gametime, world.terrains(), fields_to_draw);
 	road_program_->draw(*surface, fields_to_draw);
 
 	draw_objects();

=== modified file 'src/graphic/gl/terrain_program.cc'
--- src/graphic/gl/terrain_program.cc	2014-11-24 07:10:03 +0000
+++ src/graphic/gl/terrain_program.cc	2014-11-28 07:21:07 +0000
@@ -20,8 +20,6 @@
 #include "graphic/gl/terrain_program.h"
 
 #include "graphic/gl/fields_to_draw.h"
-#include "graphic/graphic.h"
-#include "graphic/terrain_texture.h"
 #include "graphic/texture.h"
 
 namespace  {
@@ -37,17 +35,20 @@
 #version 120
 
 // Attributes.
+attribute float attr_brightness;
 attribute vec2 attr_position;
-attribute float attr_brightness;
+attribute vec2 attr_texture_offset;
 attribute vec2 attr_texture_position;
 
 // Output of vertex shader.
 varying float var_brightness;
+varying vec2 var_texture_offset;
 varying vec2 var_texture_position;
 
 void main() {
 	var_texture_position = attr_texture_position;
 	var_brightness = attr_brightness;
+	var_texture_offset = attr_texture_offset;
 	gl_Position = vec4(attr_position, 0., 1.);
 }
 )";
@@ -56,12 +57,15 @@
 #version 120
 
 uniform sampler2D u_terrain_texture;
+uniform vec2 u_texture_dimensions;
 
 varying float var_brightness;
 varying vec2 var_texture_position;
+varying vec2 var_texture_offset;
 
 void main() {
-	vec4 clr = texture2D(u_terrain_texture, var_texture_position);
+	vec4 clr = texture2D(u_terrain_texture,
+			var_texture_offset + u_texture_dimensions * fract(var_texture_position));
 	clr.rgb *= var_brightness;
 	gl_FragColor = clr;
 }
@@ -72,25 +76,26 @@
 TerrainProgram::TerrainProgram() {
 	gl_program_.build(kTerrainVertexShader, kTerrainFragmentShader);
 
+	attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
 	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");
+	attr_texture_offset_ = glGetAttribLocation(gl_program_.object(), "attr_texture_offset");
+	attr_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_texture_position");
 
 	u_terrain_texture_ = glGetUniformLocation(gl_program_.object(), "u_terrain_texture");
+	u_texture_dimensions_ = glGetUniformLocation(gl_program_.object(), "u_texture_dimensions");
 }
 
-void TerrainProgram::gl_draw(int num_vertices,
-                             const DescriptionMaintainer<TerrainDescription>& terrains) {
+void TerrainProgram::gl_draw(int gl_texture, float texture_w, float texture_h) {
 	glUseProgram(gl_program_.object());
 
+	glEnableVertexAttribArray(attr_brightness_);
 	glEnableVertexAttribArray(attr_position_);
+	glEnableVertexAttribArray(attr_texture_offset_);
 	glEnableVertexAttribArray(attr_texture_position_);
-	glEnableVertexAttribArray(attr_brightness_);
 
 	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
 	glBufferData(GL_ARRAY_BUFFER,
-	             sizeof(TerrainProgram::PerVertexData) * num_vertices,
+	             sizeof(TerrainProgram::PerVertexData) * vertices_.size(),
 	             vertices_.data(),
 	             GL_STREAM_DRAW);
 
@@ -102,55 +107,56 @@
 		                      sizeof(TerrainProgram::PerVertexData),
 		                      reinterpret_cast<void*>(offset));
 	};
+	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
 	set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, gl_x));
-	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
+	set_attrib_pointer(attr_texture_offset_, 2, offsetof(PerVertexData, texture_offset_x));
 	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);
+	glBindTexture(GL_TEXTURE_2D, gl_texture);
+
 	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())
-		                 ->texture()
-		                 .get_gl_texture());
-		glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, indices.data());
-	}
-
+	glUniform2f(u_texture_dimensions_, texture_w, texture_h);
+
+	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
+
+	glBindTexture(GL_TEXTURE_2D, 0);
+
+	glDisableVertexAttribArray(attr_brightness_);
 	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_texture_offset_);
 	glDisableVertexAttribArray(attr_texture_position_);
-	glDisableVertexAttribArray(attr_brightness_);
-}
-
-void TerrainProgram::draw(const DescriptionMaintainer<TerrainDescription>& terrains,
+}
+
+void TerrainProgram::add_vertex(const FieldsToDraw::Field& field,
+                                const FloatPoint& texture_offset) {
+	vertices_.emplace_back();
+	PerVertexData& back = vertices_.back();
+
+	back.gl_x = field.gl_x;
+	back.gl_y = field.gl_y;
+	back.brightness = field.brightness;
+	back.texture_x = field.texture_x;
+	back.texture_y = field.texture_y;
+	back.texture_offset_x = texture_offset.x;
+	back.texture_offset_y = texture_offset.y;
+}
+
+void TerrainProgram::draw(uint32_t gametime,
+                          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());
-	}
+	// This method expects that all terrains have the same dimensions and that
+	// all are packed into the same texture atlas, i.e. all are in the same GL
+	// texture. It does not check for this invariance for speeds sake.
+
+	vertices_.clear();
+	vertices_.reserve(fields_to_draw.size() * 3);
 
 	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.gl_x = field.gl_x;
-		vertex.gl_y = field.gl_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.
@@ -163,19 +169,24 @@
 		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);
+			const FloatPoint texture_offset =
+			   terrains.get_unmutable(field.ter_d).get_texture(gametime).texture_coordinates().top_left();
+			add_vertex(fields_to_draw.at(current_index), texture_offset);
+			add_vertex(fields_to_draw.at(bln_index), texture_offset);
+			add_vertex(fields_to_draw.at(brn_index), texture_offset);
 		}
 
 		// 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);
+			const FloatPoint texture_offset =
+			   terrains.get_unmutable(field.ter_r).get_texture(gametime).texture_coordinates().top_left();
+			add_vertex(fields_to_draw.at(current_index), texture_offset);
+			add_vertex(fields_to_draw.at(brn_index), texture_offset);
+			add_vertex(fields_to_draw.at(rn_index), texture_offset);
 		}
 	}
 
-	gl_draw(fields_to_draw.size(), terrains);
+	const Texture& texture = terrains.get_unmutable(0).get_texture(0);
+	gl_draw(texture.get_gl_texture(), texture.texture_coordinates().w, texture.texture_coordinates().h);
 }

=== modified file 'src/graphic/gl/terrain_program.h'
--- src/graphic/gl/terrain_program.h	2014-11-08 18:06:17 +0000
+++ src/graphic/gl/terrain_program.h	2014-11-28 07:21:07 +0000
@@ -22,11 +22,12 @@
 
 #include <vector>
 
+#include "base/point.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 FieldsToDraw;
 
 class TerrainProgram {
 public:
@@ -34,7 +35,7 @@
 	TerrainProgram();
 
 	// Draws the terrain.
-	void draw(const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+	void draw(uint32_t gametime, const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
 	          const FieldsToDraw& fields_to_draw);
 
 private:
@@ -44,36 +45,36 @@
 		float brightness;
 		float texture_x;
 		float texture_y;
+		float texture_offset_x;
+		float texture_offset_y;
 	};
-	static_assert(sizeof(PerVertexData) == 20, "Wrong padding.");
-
-	void gl_draw(int num_vertices,
-	             const DescriptionMaintainer<Widelands::TerrainDescription>& terrains);
+	static_assert(sizeof(PerVertexData) == 28, "Wrong padding.");
+
+	void gl_draw(int gl_texture, float texture_w, float texture_h);
+
+	// Adds a vertex to the end of vertices with data from 'field' and 'texture_coordinates'.
+	void add_vertex(const FieldsToDraw::Field& field, const FloatPoint& texture_coordinates);
+
+	// The program used for drawing the terrain.
+	Gl::Program gl_program_;
 
 	// 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_brightness_;
 	GLint attr_position_;
+	GLint attr_texture_offset_;
 	GLint attr_texture_position_;
-	GLint attr_brightness_;
 
 	// Uniforms.
 	GLint u_terrain_texture_;
+	GLint u_texture_dimensions_;
 
 	// 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);
 };
 

=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc	2014-11-24 07:10:03 +0000
+++ src/graphic/graphic.cc	2014-11-28 07:21:07 +0000
@@ -19,21 +19,10 @@
 
 #include "graphic/graphic.h"
 
-#include <cstring>
-#include <iostream>
-#include <memory>
-
-#include <SDL_image.h>
-
-#include "base/deprecated.h"
-#include "base/i18n.h"
 #include "base/log.h"
-#include "base/macros.h"
 #include "base/wexception.h"
 #include "build_info.h"
-#include "config.h"
 #include "graphic/animation.h"
-#include "graphic/diranimations.h"
 #include "graphic/font_handler.h"
 #include "graphic/gl/system_headers.h"
 #include "graphic/image.h"
@@ -41,15 +30,11 @@
 #include "graphic/image_transformations.h"
 #include "graphic/rendertarget.h"
 #include "graphic/screen.h"
-#include "graphic/terrain_texture.h"
 #include "graphic/texture.h"
 #include "graphic/texture_cache.h"
-#include "io/fileread.h"
 #include "io/filesystem/layered_filesystem.h"
 #include "io/streamwrite.h"
-#include "logic/roadtype.h"
 #include "notifications/notifications.h"
-#include "ui_basic/progresswindow.h"
 
 using namespace std;
 
@@ -162,7 +147,6 @@
 
 Graphic::~Graphic()
 {
-	m_maptextures.clear();
 	texture_cache_->flush();
 	// TODO(unknown): this should really not be needed, but currently is :(
 	if (UI::g_fh)
@@ -303,22 +287,6 @@
 	save_surface_to_png(image->texture(), sw);
 }
 
-uint32_t Graphic::new_maptexture(const std::vector<std::string>& texture_files, const uint32_t frametime)
-{
-	m_maptextures.emplace_back(new TerrainTexture(texture_files, frametime));
-	return m_maptextures.size(); // ID 1 is at m_maptextures[0]
-}
-
-/**
- * Advance frames for animated textures
-*/
-void Graphic::animate_maptextures(uint32_t time)
-{
-	for (uint32_t i = 0; i < m_maptextures.size(); ++i) {
-		m_maptextures[i]->animate(time);
-	}
-}
-
 /**
  * Save a screenshot to the given file.
 */
@@ -329,15 +297,3 @@
 	save_surface_to_png(screen_.get(), sw);
 	delete sw;
 }
-
-/**
- * Retrieve the map texture with the given number
- * \return the actual texture data associated with the given ID.
- */
-TerrainTexture * Graphic::get_maptexture_data(uint32_t id)
-{
-	--id; // ID 1 is at m_maptextures[0]
-
-	assert(id < m_maptextures.size());
-	return m_maptextures[id].get();
-}

=== modified file 'src/graphic/graphic.h'
--- src/graphic/graphic.h	2014-11-24 07:12:35 +0000
+++ src/graphic/graphic.h	2014-11-28 07:21:07 +0000
@@ -20,13 +20,10 @@
 #ifndef WL_GRAPHIC_GRAPHIC_H
 #define WL_GRAPHIC_GRAPHIC_H
 
-#include <map>
 #include <memory>
-#include <vector>
 
 #include <SDL.h>
 
-#include "base/rect.h"
 #include "graphic/image_cache.h"
 #include "notifications/notifications.h"
 #include "notifications/note_ids.h"
@@ -38,7 +35,6 @@
 class Surface;
 class TextureCache;
 class StreamWrite;
-struct TerrainTexture;
 
 // Will be send whenever the resolution changes.
 struct GraphicResolutionChanged {
@@ -83,13 +79,7 @@
 
 	void save_png(const Image*, StreamWrite*) const;
 
-	// Creates a new TerrainTexture() with the given 'frametime' and using the given
-	// 'texture_files' as the images for it and returns it id.
-	uint32_t new_maptexture(const std::vector<std::string>& texture_files, uint32_t frametime);
-	void animate_maptextures(uint32_t time);
-
 	void screenshot(const std::string& fname) const;
-	TerrainTexture * get_maptexture_data(uint32_t id);
 
 private:
 	// Called when the resolution (might) have changed.
@@ -119,8 +109,6 @@
 	std::unique_ptr<ImageCache> image_cache_;
 	/// This holds all animations.
 	std::unique_ptr<AnimationManager> animation_manager_;
-
-	std::vector<std::unique_ptr<TerrainTexture>> m_maptextures;
 };
 
 extern Graphic * g_gr;

=== modified file 'src/graphic/minimap_renderer.cc'
--- src/graphic/minimap_renderer.cc	2014-11-24 07:10:03 +0000
+++ src/graphic/minimap_renderer.cc	2014-11-28 07:21:07 +0000
@@ -27,7 +27,6 @@
 #include "graphic/graphic.h"
 #include "graphic/image.h"
 #include "graphic/in_memory_image.h"
-#include "graphic/terrain_texture.h"
 #include "graphic/texture.h"
 #include "logic/field.h"
 #include "logic/map.h"
@@ -61,9 +60,8 @@
 	uint32_t pixelcolor = 0;
 
 	if (layers & MiniMapLayer::Terrain) {
-		const RGBColor color =
-		   g_gr->get_maptexture_data(egbase.world().terrain_descr(f.field->terrain_d()).get_texture())
-		      ->get_minimap_color(f.field->get_brightness());
+		const RGBColor& color =  egbase.world().terrain_descr(f.field->terrain_d()).get_minimap_color(
+		   f.field->get_brightness());
 
 		pixelcolor = SDL_MapRGBA(&format, color.r, color.g, color.b, 255);
 	}

=== modified file 'src/graphic/surface.cc'
--- src/graphic/surface.cc	2014-11-24 07:25:21 +0000
+++ src/graphic/surface.cc	2014-11-28 07:21:07 +0000
@@ -162,15 +162,26 @@
 {
 	glViewport(0, 0, width(), height());
 
-	// Source Rectangle.
+	// Source Rectangle. We have to take into account that the texture might be
+	// a subtexture in another bigger texture. So we first figure out the pixel
+	// coordinates given it is a full texture (values between 0 and 1) and then
+	// adjust these for the texture coordinates in the parent texture.
 	FloatRect gl_src_rect;
 	{
+		const FloatRect& texture_coordinates = texture->texture_coordinates();
+
 		float x1 = srcrc.x;
 		float y1 = srcrc.y;
 		pixel_to_gl_texture(texture->width(), texture->height(), &x1, &y1);
+		x1 = texture_coordinates.x + x1 * texture_coordinates.w;
+		y1 = texture_coordinates.y + y1 * texture_coordinates.h;
+
 		float x2 = srcrc.x + srcrc.w;
 		float y2 = srcrc.y + srcrc.h;
 		pixel_to_gl_texture(texture->width(), texture->height(), &x2, &y2);
+		x2 = texture_coordinates.x + x2 * texture_coordinates.w;
+		y2 = texture_coordinates.y + y2 * texture_coordinates.h;
+
 		gl_src_rect.x = x1;
 		gl_src_rect.y = y1;
 		gl_src_rect.w = x2 - x1;

=== removed file 'src/graphic/terrain_texture.cc'
--- src/graphic/terrain_texture.cc	2014-11-24 07:10:03 +0000
+++ src/graphic/terrain_texture.cc	1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2002-2004, 2006, 2010, 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *
- */
-
-#include "graphic/terrain_texture.h"
-
-#include <SDL_image.h>
-
-#include "base/deprecated.h"
-#include "base/log.h"
-#include "base/wexception.h"
-#include "graphic/image_io.h"
-#include "graphic/texture.h"
-#include "io/fileread.h"
-#include "io/filesystem/layered_filesystem.h"
-
-using namespace std;
-
-/**
- * Create a texture, taking the pixel data from an Image.
- * Currently it converts a 16 bit image to a 8 bit texture. This should
- * be changed to load a 8 bit file directly, however.
- */
-TerrainTexture::TerrainTexture(const std::vector<std::string>& texture_files, const uint32_t frametime)
-   : m_frame_num(0), m_frametime(frametime) {
-	if (texture_files.empty()) {
-		throw wexception("No images for texture.");
-	}
-
-	for (const std::string& fname : texture_files) {
-		if (!g_fs->file_exists(fname)) {
-			throw wexception("Could not find %s.", fname.c_str());
-		}
-
-		m_texture_image = fname;
-		SDL_Surface* sdl_surface = load_image_as_sdl_surface(fname, g_fs);
-		if (!sdl_surface) {
-			throw wexception(
-			   "WARNING: Failed to load texture frame %s: %s\n", fname.c_str(), IMG_GetError());
-		}
-		if (sdl_surface->w != kTextureWidth || sdl_surface->h != kTextureHeight) {
-			SDL_FreeSurface(sdl_surface);
-			throw wexception("WARNING: %s: texture must be %ix%i pixels big\n",
-			                 fname.c_str(),
-			                 kTextureWidth,
-			                 kTextureHeight);
-		}
-
-		// calculate shades on the first frame
-		if (m_textures.empty()) {
-			uint8_t top_left_pixel = static_cast<uint8_t*>(sdl_surface->pixels)[0];
-			SDL_Color top_left_pixel_color = sdl_surface->format->palette->colors[top_left_pixel];
-			for (int i = -128; i < 128; i++) {
-				const int shade = 128 + i;
-				int32_t r = std::min<int32_t>((top_left_pixel_color.r * shade) >> 7, 255);
-				int32_t g = std::min<int32_t>((top_left_pixel_color.g * shade) >> 7, 255);
-				int32_t b = std::min<int32_t>((top_left_pixel_color.b * shade) >> 7, 255);
-				m_minimap_colors[shade] = RGBColor(r, g, b);
-			}
-		}
-		m_textures.emplace_back(new Texture(sdl_surface));
-	}
-
-	if (m_textures.empty())
-		throw wexception("TerrainTexture has no frames");
-}
-
-RGBColor TerrainTexture::get_minimap_color(int8_t shade) {
-	return m_minimap_colors[128 + shade];
-}
-
-void TerrainTexture::animate(uint32_t time)
-{
-	m_frame_num = (time / m_frametime) % m_textures.size();
-}
-
-const std::string& TerrainTexture::get_texture_image() const {
-	return m_texture_image;
-}
-
-const Texture& TerrainTexture::texture() const {
-	return *m_textures.at(m_frame_num);
-}

=== removed file 'src/graphic/terrain_texture.h'
--- src/graphic/terrain_texture.h	2014-11-24 07:10:03 +0000
+++ src/graphic/terrain_texture.h	1970-01-01 00:00:00 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2002-2004, 2006, 2008-2010, 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *
- */
-
-#ifndef WL_GRAPHIC_TERRAIN_TEXTURE_H
-#define WL_GRAPHIC_TERRAIN_TEXTURE_H
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <stdint.h>
-
-#include "graphic/colormap.h"
-
-class Texture;
-
-/// TerrainTextures have a fixed size and are squares.
-constexpr int kTextureWidth = 64;
-constexpr int kTextureHeight = kTextureWidth;
-
-// TerrainTexture represents are terrain texture, which is strictly kTextureWidth by
-// kTextureHeight pixels in size.
-struct TerrainTexture {
-	TerrainTexture(const std::vector<std::string>& texture_files, uint32_t frametime);
-
-	// Returns the path to a representative image for this texture.
-	const std::string& get_texture_image() const;
-
-	// Returns the texture for the current animation phase.
-	const Texture& texture() const;
-
-	// Return the basic terrain colour to be used in the minimap.
-	RGBColor get_minimap_color(int8_t shade);
-
-	// Set the current frame according to the game time.
-	void animate(uint32_t time);
-
-private:
-	RGBColor    m_minimap_colors[256];
-	int32_t     m_frame_num;
-	std::string m_texture_image;
-	uint32_t    m_frametime;
-	std::vector<std::unique_ptr<Texture>> m_textures;
-};
-
-#endif  // end of include guard: WL_GRAPHIC_TERRAIN_TEXTURE_H

=== modified file 'src/graphic/texture.cc'
--- src/graphic/texture.cc	2014-11-24 07:25:21 +0000
+++ src/graphic/texture.cc	2014-11-28 07:21:07 +0000
@@ -72,11 +72,6 @@
 
 }  // namespace
 
-/**
- * Initialize an OpenGL texture of the given dimensions.
- *
- * The initial data of the texture is undefined.
- */
 Texture::Texture(int w, int h)
 {
 	init(w, h);
@@ -89,11 +84,6 @@
 		 GL_UNSIGNED_BYTE, nullptr);
 }
 
-/**
- * Initialize an OpenGL texture with the contents of the given surface.
- *
- * \note Takes ownership of the given surface.
- */
 Texture::Texture(SDL_Surface * surface, bool intensity)
 {
 	init(surface->w, surface->h);
@@ -132,9 +122,24 @@
 	SDL_FreeSurface(surface);
 }
 
+Texture::Texture(const GLuint texture, const Rect& subrect, int parent_w, int parent_h) {
+	m_w = subrect.w;
+	m_h = subrect.h;
+
+	m_texture = texture;
+	m_owns_texture = false;
+
+	m_texture_coordinates.w = static_cast<float>(m_w - 1) / parent_w;
+	m_texture_coordinates.h = static_cast<float>(m_h - 1) / parent_h;
+	m_texture_coordinates.x = (static_cast<float>(subrect.x) + 0.5) / parent_w;
+	m_texture_coordinates.y = (static_cast<float>(subrect.y) + 0.5) / parent_h;
+}
+
 Texture::~Texture()
 {
-	glDeleteTextures(1, &m_texture);
+	if (m_owns_texture) {
+		glDeleteTextures(1, &m_texture);
+	}
 }
 
 void Texture::pixel_to_gl(float* x, float* y) const {
@@ -150,6 +155,12 @@
 		return;
 	}
 
+	m_owns_texture = true;
+	m_texture_coordinates.x = 0.f;
+	m_texture_coordinates.y = 0.f;
+	m_texture_coordinates.w = 1.f;
+	m_texture_coordinates.h = 1.f;
+
 	glGenTextures(1, &m_texture);
 	glBindTexture(GL_TEXTURE_2D, m_texture);
 
@@ -164,13 +175,20 @@
 	if (m_w <= 0 || m_h <= 0) {
 		return;
 	}
-	assert(!m_pixels);
+
+	if (m_pixels) {
+		throw wexception("Called lock() on locked surface.");
+	}
+	if (!m_owns_texture) {
+		throw wexception("A surface that does not own its pixels can not be locked..");
+	}
 
 	m_pixels.reset(new uint8_t[m_w * m_h * 4]);
 
 	if (mode == Lock_Normal) {
 		glBindTexture(GL_TEXTURE_2D, m_texture);
 		glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
+		glBindTexture(GL_TEXTURE_2D, 0);
 	}
 }
 
@@ -185,6 +203,7 @@
 		glTexImage2D
 			(GL_TEXTURE_2D, 0, GL_RGBA, m_w, m_h, 0, GL_RGBA,
 			 GL_UNSIGNED_BYTE,  m_pixels.get());
+		glBindTexture(GL_TEXTURE_2D, 0);
 	}
 
 	m_pixels.reset(nullptr);

=== modified file 'src/graphic/texture.h'
--- src/graphic/texture.h	2014-11-24 07:25:21 +0000
+++ src/graphic/texture.h	2014-11-28 07:21:07 +0000
@@ -19,6 +19,7 @@
 #ifndef WL_GRAPHIC_TEXTURE_H
 #define WL_GRAPHIC_TEXTURE_H
 
+#include "base/rect.h"
 #include "graphic/gl/system_headers.h"
 #include "graphic/surface.h"
 
@@ -34,6 +35,10 @@
 	// dimensions.
 	Texture(int w, int h);
 
+	// Create a logical texture that is a 'subrect' (in Pixel) in
+	// another texture. Ownership of 'texture' is not taken.
+	Texture(const GLuint texture, const Rect& subrect, int parent_w, int parent_h);
+
 	virtual ~Texture();
 
 	/// Interface implementation
@@ -60,10 +65,20 @@
 
 	GLuint get_gl_texture() const {return m_texture;}
 
+	const FloatRect& texture_coordinates() const {
+		return m_texture_coordinates;
+	}
+
 private:
 	void pixel_to_gl(float* x, float* y) const override;
 	void init(uint16_t w, uint16_t h);
 
+	// True if we own the texture, i.e. if we need to delete it.
+	bool m_owns_texture;
+
+	// Texture coordinates in m_texture.
+	FloatRect m_texture_coordinates;
+
 	GLuint m_texture;
 };
 

=== added file 'src/graphic/texture_atlas.cc'
--- src/graphic/texture_atlas.cc	1970-01-01 00:00:00 +0000
+++ src/graphic/texture_atlas.cc	2014-11-28 07:21:07 +0000
@@ -0,0 +1,153 @@
+/*
+ * 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/texture_atlas.h"
+
+#include <cassert>
+#include <memory>
+
+#include "base/wexception.h"
+
+TextureAtlas::Node::Node(const Rect& init_r) : used(false), r(init_r) {
+}
+
+void TextureAtlas::Node::split(int item_w, int item_h) {
+	assert(!used);
+
+	down.reset(new Node(Rect(r.x, r.y + item_h, r.w, r.h - item_h)));
+	right.reset(new Node(Rect(r.x + item_w, r.y, r.w - item_w, item_h)));
+	used = true;
+
+	// Note: we do not change the size of the root. It is not needed
+	// for the remaining algorithm, but we use it to remember the
+	// size of the full canvas.
+}
+
+
+TextureAtlas::TextureAtlas() :
+	next_index_(0)
+{
+}
+
+void TextureAtlas::add(const Texture& texture) {
+	blocks_.emplace_back(next_index_++, &texture);
+}
+
+// static
+TextureAtlas::Node* TextureAtlas::find_node(Node* node, int w, int h) {
+	if (node->used) {
+		Node* child_node = find_node(node->right.get(), w, h);
+		if (child_node != nullptr) {
+			return child_node;
+		}
+		return find_node(node->down.get(), w, h);
+	}
+	assert(!node->used);
+
+	if ((w <= node->r.w) && (h <= node->r.h)) {
+		return node;
+	}
+
+	return nullptr;
+}
+
+std::unique_ptr<Texture> TextureAtlas::pack(std::vector<std::unique_ptr<Texture>>* textures) {
+	if (blocks_.empty()) {
+		throw wexception("Called pack() without blocks.");
+	}
+
+	// Sort blocks by their biggest side length. This heuristically gives the
+	// best packing.
+	std::sort(blocks_.begin(), blocks_.end(), [](const Block& i, const Block& j) {
+		return std::max(i.texture->width(), i.texture->height()) >
+		       std::max(j.texture->width(), j.texture->height());
+	});
+
+	std::unique_ptr<Node> root(
+	   new Node(Rect(0, 0, blocks_.begin()->texture->width(), blocks_.begin()->texture->height())));
+
+	// TODO(sirver): when growing, keep maximum size of gl textures in mind.
+	const auto grow_right = [&root](int delta_w) {
+		std::unique_ptr<Node> new_root(new Node(Rect(0, 0, root->r.w + delta_w, root->r.h)));
+		new_root->used = true;
+		new_root->right.reset(new Node(Rect(root->r.w, 0, delta_w, root->r.h)));
+		new_root->down.reset(root.release());
+		root.reset(new_root.release());
+	};
+
+	const auto grow_down = [&root](int delta_h) {
+		std::unique_ptr<Node> new_root(new Node(Rect(0, 0, root->r.w, root->r.h + delta_h)));
+		new_root->used = true;
+		new_root->down.reset(new Node(Rect(0, root->r.h, root->r.w, delta_h)));
+		new_root->right.reset(root.release());
+		root.reset(new_root.release());
+	};
+
+	for (Block& block : blocks_) {
+		const int block_width = block.texture->width();
+		const int block_height = block.texture->height();
+
+		Node* fitting_node = find_node(root.get(), block_width, block_height);
+		if (fitting_node == nullptr) {
+			// Atlas is not big enough to contain this. Grow it and try again.
+			bool can_grow_down = (block_width <= root->r.w);
+			bool can_grow_right = (block_height <= root->r.h);
+
+			// Attempt to keep the texture square-ish.
+			bool should_grow_right = can_grow_right && (root->r.h >= root->r.w + block_width);
+			bool should_grow_down = can_grow_down && (root->r.w >= root->r.h + block_height);
+
+			if (should_grow_right) {
+				grow_right(block_width);
+			} else if (should_grow_down) {
+				grow_down(block_height);
+			} else if (can_grow_right) {
+				grow_right(block_width);
+			} else if (can_grow_down) {
+				grow_down(block_height);
+			}
+			fitting_node = find_node(root.get(), block_width, block_height);
+		}
+		if (!fitting_node) {
+			throw wexception("Unable to fit node in texture atlas.");
+		}
+		fitting_node->split(block_width, block_height);
+		block.node = fitting_node;
+	}
+
+	std::unique_ptr<Texture> packed_texture(new Texture(root->r.w, root->r.h));
+	packed_texture->fill_rect(Rect(0, 0, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0));
+
+	// Sort blocks by index so that they come back in the correct ordering.
+	std::sort(blocks_.begin(), blocks_.end(), [](const Block& i, const Block& j) {
+		return i.index < j.index;
+	});
+
+	for (Block& block : blocks_) {
+		packed_texture->blit(block.node->r.top_left(),
+		                     block.texture,
+		                     Rect(0, 0, block.texture->width(), block.texture->height()));
+		textures->emplace_back(new Texture(
+		   packed_texture->get_gl_texture(),
+		   Rect(block.node->r.top_left(), block.texture->width(), block.texture->height()),
+		   root->r.w,
+		   root->r.h));
+	}
+	return packed_texture;
+}

=== added file 'src/graphic/texture_atlas.h'
--- src/graphic/texture_atlas.h	1970-01-01 00:00:00 +0000
+++ src/graphic/texture_atlas.h	2014-11-28 07:21:07 +0000
@@ -0,0 +1,78 @@
+/*
+ * 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_TEXTURE_ATLAS_H
+#define WL_GRAPHIC_TEXTURE_ATLAS_H
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "graphic/texture.h"
+
+// A 2d bin packer based on the blog post
+// http://codeincomplete.com/posts/2011/5/7/bin_packing/.
+class TextureAtlas {
+public:
+	TextureAtlas();
+
+	// Add 'texture' as one of the textures to be packed. Ownership is
+	// not taken, but 'texture' must be valid until pack() has been
+	// called.
+	void add(const Texture& texture);
+
+	// Packs the textures and returns the packed texture. 'textures'
+	// contains the individual sub textures (that do not own their
+	// memory) in the order they have been added by 'add'.
+	std::unique_ptr<Texture> pack(std::vector<std::unique_ptr<Texture>>* textures);
+
+private:
+	struct Node {
+		Node(const Rect& init_r);
+		void split(int w, int h);
+
+		bool used;
+		Rect r;
+		std::unique_ptr<Node> right;
+		std::unique_ptr<Node> down;
+
+		DISALLOW_COPY_AND_ASSIGN(Node);
+	};
+
+	struct Block {
+		Block(int init_index, const Texture* init_texture)
+		   : index(init_index), texture(init_texture) {
+		}
+
+		int index;
+		const Texture* texture;
+		Node* node;
+	};
+
+	static Node* find_node(Node* root, int w, int h);
+
+	int next_index_;
+
+	// Unpacked items.
+	std::vector<Block> blocks_;
+
+	DISALLOW_COPY_AND_ASSIGN(TextureAtlas);
+};
+
+#endif  // end of include guard: WL_GRAPHIC_TEXTURE_ATLAS_H

=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt	2014-10-13 15:04:50 +0000
+++ src/logic/CMakeLists.txt	2014-11-28 07:21:07 +0000
@@ -230,6 +230,9 @@
     game_io
     graphic
     graphic_color
+    graphic_image_io
+    graphic_surface
+    graphic_texture_atlas
     helper
     io_fileread
     io_filesystem

=== modified file 'src/logic/description_maintainer.h'
--- src/logic/description_maintainer.h	2014-09-19 23:33:35 +0000
+++ src/logic/description_maintainer.h	2014-11-28 07:21:07 +0000
@@ -55,9 +55,13 @@
 
 	// Returns the entry with the given 'idx' or nullptr if 'idx' is out of
 	// bound. Ownership is retained.
+	// TODO(sirver): remove get() and use get_mutable
 	T* get(const int32_t idx) const {
 		return (idx >= 0 && idx < static_cast<int32_t>(items_.size())) ? items_[idx].get() : nullptr;
 	}
+	T* get_mutable(const int32_t idx) const {
+		return get(idx);
+	}
 
 	// Returns the entry at 'index'. If 'index' is out of bounds the result is
 	// undefined.

=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc	2014-09-19 12:54:54 +0000
+++ src/logic/editor_game_base.cc	2014-11-28 07:21:07 +0000
@@ -20,6 +20,7 @@
 #include "logic/editor_game_base.h"
 
 #include <algorithm>
+#include <memory>
 #include <set>
 
 #include "base/i18n.h"
@@ -113,11 +114,12 @@
 
 		try {
 			lua_->run_script("world/init.lua");
-		}
-		catch (const WException& e) {
+		} catch (const WException& e) {
 			log("Could not read world information: %s", e.what());
 			throw;
 		}
+
+		world_->load_graphics();
 	}
 	return world_.get();
 }

=== modified file 'src/logic/field.cc'
--- src/logic/field.cc	2014-07-22 09:54:49 +0000
+++ src/logic/field.cc	2014-11-28 07:21:07 +0000
@@ -50,7 +50,7 @@
 		b = -128;
 	else if (b >  127)
 		b =  127;
-	brightness = static_cast<int8_t>(b); //TODO(unknown): ARGH !!
+	brightness = static_cast<int8_t>(b);
 }
 
 }

=== modified file 'src/logic/game.cc'
--- src/logic/game.cc	2014-10-11 15:56:02 +0000
+++ src/logic/game.cc	2014-11-28 07:21:07 +0000
@@ -613,9 +613,6 @@
 		// the timings of savings.
 		cmdqueue().run_queue(m_ctrl->get_frametime(), get_gametime_pointer());
 
-		if (g_gr) // not in dedicated server mode
-			g_gr->animate_maptextures(get_gametime());
-
 		// check if autosave is needed
 		m_savehandler.think(*this, WLApplication::get()->get_time());
 	}

=== modified file 'src/logic/world/terrain_description.cc'
--- src/logic/world/terrain_description.cc	2014-09-19 12:54:54 +0000
+++ src/logic/world/terrain_description.cc	2014-11-28 07:21:07 +0000
@@ -19,9 +19,12 @@
 
 #include "logic/world/terrain_description.h"
 
+#include <memory>
+
 #include <boost/format.hpp>
 
 #include "graphic/graphic.h"
+#include "graphic/texture.h"
 #include "logic/game_data_error.h"
 #include "logic/world/editor_category.h"
 #include "logic/world/world.h"
@@ -86,19 +89,18 @@
 		throw GameDataError("%s: temperature is not in Kelvin.", name_.c_str());
 	}
 
-	const std::vector<std::string> textures =
+	 texture_paths_ =
 	   table.get_table("textures")->array_entries<std::string>();
-	int frame_length = FRAME_LENGTH;
-	if (textures.empty()) {
+	frame_length_ = FRAME_LENGTH;
+	if (texture_paths_.empty()) {
 		throw GameDataError("Terrain %s has no images.", name_.c_str());
-	} else if (textures.size() == 1) {
+	} else if (texture_paths_.size() == 1) {
 		if (table.has_key("fps")) {
 			throw GameDataError("Terrain %s with one images must not have fps.", name_.c_str());
 		}
 	} else {
-		frame_length = 1000 / get_positive_int(table, "fps");
+		frame_length_ = 1000 / get_positive_int(table, "fps");
 	}
-	texture_ = g_gr->new_maptexture(textures, frame_length);
 
 	for (const std::string& resource :
 	     table.get_table("valid_resources")->array_entries<std::string>()) {
@@ -121,8 +123,19 @@
 TerrainDescription::~TerrainDescription() {
 }
 
-uint32_t TerrainDescription::get_texture() const {
-	return texture_;
+const Texture& TerrainDescription::get_texture(uint32_t gametime) const {
+	return *textures_.at((gametime / frame_length_) % textures_.size());
+}
+
+void TerrainDescription::add_texture(std::unique_ptr<Texture> texture) {
+	if (texture->width() != kTextureSideLength || texture->height() != kTextureSideLength) {
+		throw wexception("Tried to add a texture with wrong size.");
+	}
+	textures_.emplace_back(std::move(texture));
+}
+
+const std::vector<std::string>& TerrainDescription::texture_paths() const {
+	return texture_paths_;
 }
 
 TerrainDescription::Type TerrainDescription::get_is() const {
@@ -149,7 +162,7 @@
 	return valid_resources_.size();
 }
 
-bool TerrainDescription::is_resource_valid(const int32_t res) const {
+bool TerrainDescription::is_resource_valid(const int res) const {
 	for (const uint8_t resource_index : valid_resources_) {
 		if (resource_index == res) {
 			return true;
@@ -158,15 +171,15 @@
 	return false;
 }
 
-int8_t TerrainDescription::get_default_resource() const {
+int TerrainDescription::get_default_resource() const {
 	return default_resource_index_;
 }
 
-int32_t TerrainDescription::get_default_resource_amount() const {
+int TerrainDescription::get_default_resource_amount() const {
 	return default_resource_amount_;
 }
 
-int32_t TerrainDescription::dither_layer() const {
+int TerrainDescription::dither_layer() const {
 	return dither_layer_;
 }
 
@@ -182,4 +195,19 @@
 	return fertility_;
 }
 
+void TerrainDescription::set_minimap_color(const RGBColor& color) {
+	for (int i = -128; i < 128; i++) {
+		const int shade = 128 + i;
+		int new_r = std::min<int>((color.r * shade) >> 7, 255);
+		int new_g = std::min<int>((color.g * shade) >> 7, 255);
+		int new_b = std::min<int>((color.b * shade) >> 7, 255);
+		minimap_colors_[shade] = RGBColor(new_r, new_g, new_b);
+	}
+}
+
+const RGBColor& TerrainDescription::get_minimap_color(int shade) {
+	assert(-128 <= shade && shade <= 127);
+	return minimap_colors_[128 + shade];
+}
+
 }  // namespace Widelands

=== modified file 'src/logic/world/terrain_description.h'
--- src/logic/world/terrain_description.h	2014-09-10 10:18:46 +0000
+++ src/logic/world/terrain_description.h	2014-11-28 07:21:07 +0000
@@ -20,19 +20,26 @@
 #ifndef WL_LOGIC_WORLD_TERRAIN_DESCRIPTION_H
 #define WL_LOGIC_WORLD_TERRAIN_DESCRIPTION_H
 
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
+#include "graphic/color.h"
 #include "logic/widelands.h"
 #include "logic/world/resource_description.h"
 
 class LuaTable;
+class Texture;
 
 namespace Widelands {
 
 class EditorCategory;
 class World;
 
+/// TerrainTextures have a fixed size and are squares.
+constexpr int kTextureSideLength = 64;
+
 class TerrainDescription {
 public:
 	enum Type {
@@ -53,8 +60,19 @@
 	/// The name showed to users of Widelands. Usually translated.
 	const std::string& descname() const;
 
-	/// Returns the texture index for this terrain.
-	uint32_t get_texture() const;
+
+	const std::vector<std::string>& texture_paths() const;
+
+	/// Returns the texture for the given gametime.
+	const Texture& get_texture(uint32_t gametime) const;
+	void add_texture(std::unique_ptr<Texture> texture);
+
+	// Sets the base minimap color.
+	void set_minimap_color(const RGBColor& color);
+
+	// Return the basic terrain colour to be used in the minimap.
+	// 'shade' must be a brightness value, i.e. in [-128, 127].
+	const RGBColor& get_minimap_color(int shade);
 
 	/// Returns the type of terrain this is (water, walkable, and so on).
 	Type get_is() const;
@@ -70,7 +88,7 @@
 
 	/// Returns the resource index that can by default always be found in this
 	/// terrain.
-	int8_t get_default_resource() const;
+	int get_default_resource() const;
 
 	/// Returns the default amount of resources you can find in this terrain.
 	int32_t get_default_resource_amount() const;
@@ -98,14 +116,16 @@
 	const EditorCategory* editor_category_;  ///< not owned.
 	Type is_;
 	std::vector<uint8_t> valid_resources_;
-	int8_t default_resource_index_;
-	int32_t default_resource_amount_;
-	const std::vector<std::string> texture_paths_;
-	int32_t dither_layer_;
-	uint32_t texture_;  ///< renderer's texture
+	int default_resource_index_;
+	int default_resource_amount_;
+	int dither_layer_;
+	int frame_length_;
 	double temperature_;
 	double fertility_;
 	double humidity_;
+	std::vector<std::string> texture_paths_;
+	std::vector<std::unique_ptr<Texture>> textures_;
+	RGBColor    minimap_colors_[256];
 
 	DISALLOW_COPY_AND_ASSIGN(TerrainDescription);
 };

=== modified file 'src/logic/world/world.cc'
--- src/logic/world/world.cc	2014-09-10 10:18:46 +0000
+++ src/logic/world/world.cc	2014-11-28 07:21:07 +0000
@@ -19,26 +19,18 @@
 
 #include "logic/world/world.h"
 
-#include <iostream>
 #include <memory>
-#include <sstream>
 
-#include "base/i18n.h"
-#include "base/log.h"
-#include "base/wexception.h"
-#include "graphic/graphic.h"
-#include "io/fileread.h"
-#include "io/filesystem/layered_filesystem.h"
-#include "io/filewrite.h"
+#include "graphic/image_io.h"
+#include "graphic/texture.h"
+#include "graphic/texture_atlas.h"
+#include "logic/bob.h"
 #include "logic/critter.h"
 #include "logic/game_data_error.h"
 #include "logic/immovable.h"
-#include "logic/parse_map_object_types.h"
-#include "logic/widelands.h"
 #include "logic/world/editor_category.h"
 #include "logic/world/resource_description.h"
 #include "logic/world/terrain_description.h"
-#include "profile/profile.h"
 
 namespace Widelands {
 
@@ -54,6 +46,42 @@
 World::~World() {
 }
 
+void World::load_graphics() {
+	TextureAtlas ta;
+
+	// These will be deleted at the end of the method.
+	std::vector<std::unique_ptr<Texture>> individual_textures_;
+
+	for (size_t i = 0; i < terrains_->size(); ++i) {
+		TerrainDescription* terrain = terrains_->get_mutable(i);
+		for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {
+			SDL_Surface* sdl_surface = load_image_as_sdl_surface(terrain->texture_paths()[j]);
+
+			// Set the minimap color on the first loaded image.
+			if (j == 0) {
+				uint8_t top_left_pixel = static_cast<uint8_t*>(sdl_surface->pixels)[0];
+				const SDL_Color top_left_pixel_color =
+				   sdl_surface->format->palette->colors[top_left_pixel];
+				terrain->set_minimap_color(
+				   RGBColor(top_left_pixel_color.r, top_left_pixel_color.g, top_left_pixel_color.b));
+			}
+			individual_textures_.emplace_back(new Texture(sdl_surface));
+			ta.add(*individual_textures_.back());
+		}
+	}
+
+	std::vector<std::unique_ptr<Texture>> textures;
+	terrain_texture_ = ta.pack(&textures);
+
+	int next_texture_to_move = 0;
+	for (size_t i = 0; i < terrains_->size(); ++i) {
+		TerrainDescription* terrain = terrains_->get_mutable(i);
+		for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {
+			terrain->add_texture(std::move(textures.at(next_texture_to_move++)));
+		}
+	}
+}
+
 const DescriptionMaintainer<TerrainDescription>& World::terrains() const {
 	return *terrains_;
 }

=== modified file 'src/logic/world/world.h'
--- src/logic/world/world.h	2014-09-10 10:18:46 +0000
+++ src/logic/world/world.h	2014-11-28 07:21:07 +0000
@@ -23,11 +23,16 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "logic/bob.h"
 #include "logic/description_maintainer.h"
+#include "logic/widelands.h"
+
+class LuaInterface;
+class LuaTable;
+class Texture;
 
 namespace Widelands {
 
+class BobDescr;
 class EditorCategory;
 class EditorGameBase;
 class ResourceDescription;
@@ -40,7 +45,7 @@
 class World {
 public:
 	World();
-	~World();  // Defined in .cc because all forward declarations are known then.
+	~World();
 
 	// TODO(sirver): Refactor these to only return the description_maintainer so that world
 	// becomes a pure container.
@@ -83,6 +88,10 @@
 	const DescriptionMaintainer<EditorCategory>& editor_terrain_categories() const;
 	const DescriptionMaintainer<EditorCategory>& editor_immovable_categories() const;
 
+	// Load the graphics for the world. Animations are loaded on
+	// demand.
+	void load_graphics();
+
 private:
 	std::unique_ptr<DescriptionMaintainer<BobDescr>> bobs_;
 	std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_;
@@ -90,6 +99,7 @@
 	std::unique_ptr<DescriptionMaintainer<ResourceDescription>> resources_;
 	std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_terrain_categories_;
 	std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_immovable_categories_;
+	std::unique_ptr<Texture> terrain_texture_;
 
 	DISALLOW_COPY_AND_ASSIGN(World);
 };

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2014-11-27 11:15:34 +0000
+++ src/wui/interactive_base.cc	2014-11-28 07:21:07 +0000
@@ -446,7 +446,10 @@
 			((fps_format %
 			  (1000.0 / m_frametime) % (1000.0 / (m_avg_usframetime / 1000)))
 			 .str(), UI_FONT_SIZE_SMALL);
-		dst.blit(Point(5, (is_game) ? 25 : 5), UI::g_fh1->render(fps_text), BlendMode::UseAlpha, UI::Align_Left);
+		dst.blit(Point(5, (is_game) ? 25 : 5),
+		         UI::g_fh1->render(fps_text),
+		         BlendMode::UseAlpha,
+		         UI::Align_Left);
 	}
 }
 


Follow ups