Review: Resubmit

Addressed all comments and merged trunk.

Diff comments:

> === 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);
> +

This is best explained with a picture: 
http://codeincomplete.com/posts/2011/5/7/bin_packing/

> +                     // 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);
>       }
>  }
>  
> 


-- 
https://code.launchpad.net/~widelands-dev/widelands/texture_atlas/+merge/243119
Your team Widelands Developers is subscribed to branch 
lp:~widelands-dev/widelands/texture_atlas.

_______________________________________________
Mailing list: https://launchpad.net/~widelands-dev
Post to     : widelands-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~widelands-dev
More help   : https://help.launchpad.net/ListHelp

Reply via email to