Some comments inline, now going to compile / run this ...

Diff comments:

> === modified file 'cmake/codecheck/rules/correct_include_order'
> --- cmake/codecheck/rules/correct_include_order       2015-04-04 15:03:14 
> +0000
> +++ cmake/codecheck/rules/correct_include_order       2017-01-07 12:36:16 
> +0000
> @@ -45,9 +45,10 @@
>              delims = set(entry[1] for entry in block)
>              if len(delims) != 1:
>                  errors.append((fn, block[0][0], """Use either '"' or '<' for 
> all includes in a block."""))
> -            if sorted(includes) != includes:
> -                errors.append((fn, block[0][0], "Include block is not sorted 
> alphabetically."))
> -                return errors
> +            for include_sorted, include_actual in zip(sorted(includes), 
> includes):
> +                if include_sorted != include_actual:
> +                    errors.append((fn, block[0][0], "Include block is not 
> sorted alphabetically: '%s' must be included before '%s'" % (include_sorted, 
> include_actual)))
> +                    return errors

How can I provoke that message :-)

>  
>          if ".cc" in fn:
>              base_file = os.path.basename(fn)[:-3]
> 
> === added file 'data/shaders/blit_gl4.vp'
> --- data/shaders/blit_gl4.vp  1970-01-01 00:00:00 +0000
> +++ data/shaders/blit_gl4.vp  2017-01-07 12:36:16 +0000
> @@ -0,0 +1,70 @@
> +#version 130
> +#extension GL_ARB_separate_shader_objects : enable
> +#extension GL_ARB_shader_storage_buffer_object : enable
> +#extension GL_ARB_uniform_buffer_object : enable
> +
> +// Varyings.
> +out vec4 out_texture_coordinates;
> +out vec4 out_blend;
> +out float out_program_flavor;
> +
> +struct Rect {
> +     float dst_x, dst_y, dst_width, dst_height;
> +     uint src_x, src_y, src_width, src_height;
> +     uint src_parent_width, src_parent_height;
> +     uint mask_x, mask_y, mask_width, mask_height;
> +     uint mask_parent_width, mask_parent_height;
> +     uint blend_r, blend_g, blend_b, blend_a;
> +     float program_flavor, z;
> +};
> +
> +layout(std140, binding=0) buffer ssbo_rects {
> +     Rect rects[];
> +};
> +
> +void main() {
> +     // Determine rect and vertex relative to rect
> +     uint rect_idx = uint(gl_VertexID) >> 2;
> +     uint vertex_idx = uint(gl_VertexID) & 3u;
> +     Rect r = rects[rect_idx];
> +
> +     out_program_flavor = r.program_flavor;
> +
> +     // Position
> +     gl_Position = vec4(r.dst_x, r.dst_y, r.z, 1.);
> +     if ((vertex_idx & 1u) != 0u)
> +             gl_Position.x += r.dst_width;
> +     if ((vertex_idx & 2u) != 0u)
> +             gl_Position.y += r.dst_height;
> +
> +     // Texture coordinate
> +     uint tx = r.src_x;
> +     uint ty = r.src_y;
> +     if ((vertex_idx & 1u) != 0u)
> +             tx += r.src_width;
> +     if ((vertex_idx & 2u) == 0u)
> +             ty += r.src_height;
> +     out_texture_coordinates.x = tx * (1. / r.src_parent_width);
> +     out_texture_coordinates.y = 1.0 - ty * (1. / r.src_parent_height);
> +
> +     // Blending
> +     out_blend.a = r.blend_a * (1. / 255.);
> +
> +//   if (out_program_flavor >= 1.) {

Please comment why theses ifs are not used ....

> +             out_blend.r = r.blend_r * (1. / 255.);
> +             out_blend.g = r.blend_g * (1. / 255.);
> +             out_blend.b = r.blend_b * (1. / 255.);
> +
> +//           if (out_program_flavor >= 2.) {
> +                     // Mask texture coordinate
> +                     tx = r.mask_x;
> +                     ty = r.mask_y;
> +                     if ((vertex_idx & 1u) != 0u)
> +                             tx += r.mask_width;
> +                     if ((vertex_idx & 2u) == 0u)
> +                             ty += r.mask_height;
> +                     out_texture_coordinates.z = tx * (1. / 
> r.mask_parent_width);
> +                     out_texture_coordinates.w = 1.0 - ty * (1. / 
> r.mask_parent_height);
> +//           }
> +//   }
> +}
> 
> === added file 'src/graphic/gl/terrain_program_gl4.cc'
> --- src/graphic/gl/terrain_program_gl4.cc     1970-01-01 00:00:00 +0000
> +++ src/graphic/gl/terrain_program_gl4.cc     2017-01-07 12:36:16 +0000
> @@ -0,0 +1,1065 @@
> +/*
> + * Copyright (C) 2006-2016 by the Widelands Development Team
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include "graphic/gl/terrain_program_gl4.h"
> +
> +#include "graphic/game_renderer_gl4.h"
> +#include "graphic/gl/coordinate_conversion.h"
> +#include "graphic/gl/utils.h"
> +#include "graphic/image_io.h"
> +#include "io/filesystem/layered_filesystem.h"
> +#include "logic/editor_game_base.h"
> +#include "logic/map.h"
> +#include "logic/map_objects/tribes/tribe_descr.h"
> +#include "logic/map_objects/world/terrain_description.h"
> +#include "logic/map_objects/world/world.h"
> +#include "profile/profile.h"
> +#include "wui/mapviewpixelfunctions.h"
> +
> +using namespace Widelands;
> +
> +/**
> + * This is the back-end of the GL4 rendering path.
> + *
> + * Per-field data is uploaded directly into integer-valued textures that span
> + * the entire map, and vertex shaders do most of the heavy lifting. The
> + * following textures are used:
> + *
> + * Fields texture: data that is usually constant throughout a game, 
> GL_RGBA8UI:
> + *  R = r-triangle texture index
> + *  G = d-triangle texture index
> + *  B = height
> + *  A = brightness
> + * This information can be modified in the editor and in scenarios. Note that
> + * the triangle textures depend on the player perspective.
> + *
> + * Player brightness texture:
> + *  R = player-perspective-dependent brightness modulation
> + * This information is re-uploaded every frame.
> + *
> + * Semi-permanent information (GL_R8UI):
> + *  R = bits 0..5: field ownership (player number)
> + *      bits 6..7: road/flag/building data:
> + *                 0: nothing, 1: road, 2: flag, 3: building
> + * This information is only needed for the minimap, and re-uploaded every 
> frame
> + * when it is shown.
> + *
> + * Terrain is rendered in patches of a fixed structure, and many patches are
> + * rendered in one call via instancing. Per-instance data is processed in a
> + * vertex shader.
> + *
> + * Each patch consists of the triangles associated to a WxH "rectangle" of
> + * fields, where the top-left field must be at an even y-coordinate, and H is
> + * even, e.g. a 2x4-patch:
> + *
> + *       (0,0)
> + *           O-------O-------*
> + *          / \     / \     /
> + *         /   \   /   \   /
> + *        /     \ /     \ /
> + *       *-------O-------O-------*
> + *              / \     / \     /
> + *             /   \   /   \   /
> + *            /     \ /     \ /
> + *           O-------O-------*
> + *          / \     / \     /
> + *         /   \   /   \   /
> + *        /     \ /     \ /
> + *       *-------O-------O-------*
> + *              / \     / \     /
> + *             /   \   /   \   /
> + *            /     \ /     \ /
> + *           *-------*-------*
> + *
> + * OpenGL vertices of triangles are not shared; this allows separate textures
> + * and dithering in a single pass.
> + *
> + * Road rendering is also handled here. Roads are rendered as two triangles 
> per
> + * segment. Only per-road data is uploaded; the vertex shader sources data
> + * from the per-road buffer based on the vertex ID, and an index buffer
> + * (element array buffer in OpenGL terms) is used to share two vertices 
> between
> + * the triangles that make up each segment.
> + */
> +
> +TerrainInformationGl4::GlobalMap TerrainInformationGl4::global_map_;
> +
> +std::shared_ptr<TerrainInformationGl4>
> +TerrainInformationGl4::get(const Widelands::EditorGameBase& egbase,
> +                           const Widelands::Player* player) {
> +     GlobalKey key(&egbase, player);
> +     auto it = global_map_.find(key);
> +     if (it != global_map_.end())
> +             return it->second.lock();
> +
> +     std::shared_ptr<TerrainInformationGl4> instance(
> +             new TerrainInformationGl4(egbase, player));
> +     global_map_[key] = instance;
> +     return instance;
> +}
> +
> +TerrainInformationGl4::TerrainInformationGl4(const 
> Widelands::EditorGameBase& egbase,
> +                                             const Widelands::Player* player)
> +  : egbase_(egbase), player_(player), uploads_(GL_PIXEL_UNPACK_BUFFER) {
> +     glGenTextures(1, &brightness_texture_);
> +     glGenTextures(1, &fields_texture_);
> +     glGenTextures(1, &minimap_texture_);
> +
> +     const Map& map = egbase.map();
> +     auto& gl = Gl::State::instance();
> +     gl.bind(GL_TEXTURE0, fields_texture_);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
> +
> +     gl.bind(GL_TEXTURE0, brightness_texture_);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
> +     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, map.get_width(), 
> map.get_height(), 0,
> +                  GL_RED, GL_UNSIGNED_BYTE, NULL);
> +     brightness_see_all_ = false;
> +
> +     gl.bind(GL_TEXTURE0, minimap_texture_);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
> +     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, map.get_width(), 
> map.get_height(), 0,
> +                  GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
> +
> +     fields_update();
> +     upload_road_textures();
> +     upload_constant_textures();
> +
> +     updated_minimap_ = false;
> +     need_update_minimap_ = false;
> +     minimap_update_next_ = 0;
> +}
> +
> +TerrainInformationGl4::~TerrainInformationGl4() {
> +     if (brightness_texture_)
> +             glDeleteTextures(1, &brightness_texture_);
> +
> +     if (fields_texture_)
> +             glDeleteTextures(1, &fields_texture_);
> +
> +     if (minimap_texture_)
> +             glDeleteTextures(1, &minimap_texture_);
> +
> +     if (terrain_color_texture_)
> +             glDeleteTextures(1, &terrain_color_texture_);
> +
> +     global_map_.erase(GlobalKey(&egbase_, player_));
> +}
> +
> +/// Add @p rect to @p rects, merging it with any overlapping or touching
> +/// pre-existing rects (where merging means that the rectangles are replaced 
> by
> +/// a single rectangle that contains their union). The order of rectangles in
> +/// @p rects is not preserved.
> +///
> +/// Rectangles are interpreted as half-open.
> +static void add_rect(std::vector<Recti>& rects, const Recti& rect) {
> +     Recti new_rect = rect;
> +
> +     for (size_t i = 0; i < rects.size(); ++i) {
> +             // Merge rectangles even if they only touch instead of fully 
> overlapping.
> +             // The rationale is that reducing the number of uploads is 
> often a
> +             // benefit even when the total size of uploads becomes larger.
> +             if (new_rect.x + new_rect.w < rects[i].x ||
> +                 rects[i].x + rects[i].w < new_rect.x ||
> +                 new_rect.y + new_rect.h < rects[i].y ||
> +                 rects[i].y + rects[i].h < new_rect.y)
> +                     continue;
> +
> +             rects[i] = rects.back();
> +             rects.pop_back();
> +             i--;
> +     }
> +
> +     rects.push_back(new_rect);
> +}
> +
> +void TerrainInformationGl4::update(int minfx, int maxfx, int minfy, int 
> maxfy) {
> +     const Map& map = egbase().map();
> +     int width = map.get_width();
> +     int height = map.get_height();
> +
> +     auto normalize = [](int& min, int& max, int size) {
> +             while (min < 0) {
> +                     min += size;
> +                     max += size;
> +             }
> +             while (min >= size) {
> +                     min -= size;
> +                     max -= size;
> +             }
> +     };
> +
> +     normalize(minfx, maxfx, width);
> +     normalize(minfy, maxfy, height);
> +
> +     // Ensure proper row alignment during texture uploads.
> +     minfx = (minfx / 4) * 4;
> +     maxfx = ((maxfx + 4) / 4) * 4 - 1;
> +
> +     auto add = [&](int startx, int endx) {
> +             add_rect(update_, Recti(startx, minfy, endx - startx, 
> std::min(maxfy + 1, height) - minfy));
> +             if (maxfy >= height)
> +                     add_rect(update_, Recti(startx, 0, endx - startx, 
> std::min(maxfy + 1 - height, height)));
> +     };
> +
> +     add(minfx, std::min(maxfx + 1, width));
> +     if (maxfx >= width)
> +             add(0, std::min(maxfx + 1 - width, width));
> +}
> +
> +void TerrainInformationGl4::update_minimap() {
> +     need_update_minimap_ = true;
> +}
> +
> +void TerrainInformationGl4::do_prepare_frame() {

This function is quite long can you break it into smaller pieces?
thos commetns should be actually function names (I got lost after the first 3d 
od that code)

> +     const Map& map = egbase().map();
> +
> +     upload_terrain_data();
> +
> +     if (need_update_minimap_) {
> +             if (!updated_minimap_) {
> +                     // Need a full update when the minimap is drawn for the 
> first
> +                     // time.
> +                     update_.clear();
> +                     update_.emplace_back(0, 0, map.get_width(), 
> map.get_height());
> +             } else {
> +                     // For the minimap, we want to do rolling texture 
> updates of
> +                     // stripes that cover the whole width or height of the 
> map,
> +                     // depending on which is smaller. For consistency and 
> simplicity,
> +                     // expand all other dirty rectangles to full strips as 
> well.
> +                     //
> +                     // Furthermore, use stripes of a size that is a 
> multiple of a small
> +                     // power of two, since that likely has bandwidth 
> benefits due to
> +                     // how textures are laid out in memory. This also 
> avoids confusion
> +                     // due to pixel (un)packing row alignments.
> +                     unsigned width = map.get_width();
> +                     unsigned height = map.get_height();
> +                     bool horiz = width <= height;
> +                     std::vector<std::pair<unsigned, unsigned>> stripes;
> +
> +                     // Massage existing stripes, effectively a form of 
> insertion sort.
> +                     stripes.reserve(update_.size() + 1);
> +                     for (size_t i = 0; i < update_.size(); ++i) {
> +                             unsigned min = horiz ? update_[i].y : 
> update_[i].x;
> +                             unsigned max = min + (horiz ? update_[i].h : 
> update_[i].w);
> +
> +                             min = (min / 8) * 8;
> +                             max = ((max + 7) / 8) * 8;
> +
> +                             size_t j;
> +                             for (j = 0; j < stripes.size(); ++j) {
> +                                     if (max < stripes[j].first) {
> +                                             stripes.insert(stripes.begin() 
> + j, std::make_pair(min, max));
> +                                             break;
> +                                     }
> +
> +                                     if (min <= stripes[j].second) {
> +                                             stripes[j].first = 
> std::min(stripes[j].first, min);
> +                                             stripes[j].second = 
> std::max(stripes[j].second, max);
> +
> +                                             size_t k;
> +                                             for (k = j + 1; k < 
> stripes.size(); ++k) {
> +                                                     if (stripes[j].second < 
> stripes[k].first)
> +                                                             break;
> +
> +                                                     stripes[j].second = 
> std::max(stripes[j].second, stripes[k].second);
> +                                             }
> +
> +                                             stripes.erase(stripes.begin() + 
> j + 1, stripes.begin() + k);
> +                                             break;
> +                                     }
> +                             }
> +                             if (j >= stripes.size())
> +                                     stripes.emplace_back(min, max);
> +                     }
> +
> +                     if (stripes.empty() || stripes[0].first != 0 ||
> +                         stripes[0].second < (horiz ? height : width)) {
> +                             // Add a stripe (or expand an existing one) for 
> the rolling minimap
> +                             // update.
> +                             if (minimap_update_next_ >= (horiz ? height : 
> width))
> +                                     minimap_update_next_ = 0;
> +
> +                             unsigned min = minimap_update_next_;
> +                             size_t j;
> +                             for (j = 0; j < stripes.size(); ++j) {
> +                                     unsigned max = min + 8;
> +                                     if (max < stripes[j].first) {
> +                                             stripes.insert(stripes.begin() 
> + j, std::make_pair(min, max));
> +                                             break;
> +                                     }
> +
> +                                     if (min <= stripes[j].second) {
> +                                             if (min < stripes[j].first) {
> +                                                     assert(max == 
> stripes[j].first); // due to multiples of 8
> +                                                     stripes[j].first = min;
> +                                                     break;
> +                                             }
> +
> +                                             min = minimap_update_next_ = 
> stripes[j].second;
> +                                             if (min >= (horiz ? height : 
> width)) {
> +                                                     min = 0;
> +                                                     j = 0;
> +                                                     continue;
> +                                             }
> +                                             max = std::min(min + 8, horiz ? 
> height : width);
> +                                             stripes[j].second = max;
> +
> +                                             size_t k;
> +                                             for (k = j + 1; k < 
> stripes.size(); ++k) {
> +                                                     if (stripes[j].second < 
> stripes[k].first)
> +                                                             break;
> +
> +                                                     stripes[j].second = 
> std::max(stripes[j].second, stripes[k].second);
> +                                             }
> +                                             stripes.erase(stripes.begin() + 
> j + 1, stripes.begin() + k);
> +                                             break;
> +                                     }
> +                             }
> +                             if (j >= stripes.size())
> +                                     stripes.emplace_back(min, min + 8);
> +
> +                             minimap_update_next_ += 8;
> +                     }
> +
> +                     // Convert stripes back to update rectangles.
> +                     update_.resize(stripes.size());
> +                     for (size_t i = 0; i < stripes.size(); ++i) {
> +                             if (horiz) {
> +                                     update_[i].x = 0;
> +                                     update_[i].w = width;
> +                                     update_[i].y = stripes[i].first;
> +                                     update_[i].h = stripes[i].second - 
> stripes[i].first;
> +                             } else {
> +                                     update_[i].y = 0;
> +                                     update_[i].h = height;
> +                                     update_[i].x = stripes[i].first;
> +                                     update_[i].w = stripes[i].second - 
> stripes[i].first;
> +                             }
> +                     }
> +             }
> +     }
> +
> +     // Fields data updates are guarded by version numbers instead of
> +     // rectangles.
> +     if (fields_base_version_ != map.get_fields_base_version() ||
> +         (player() && terrain_vision_version_ != 
> player()->get_terrain_vision_version()))
> +             fields_update();
> +
> +     brightness_update();
> +
> +     if (need_update_minimap_)
> +             do_update_minimap();
> +
> +     update_.clear();
> +     updated_minimap_ = need_update_minimap_;
> +     need_update_minimap_ = false;
> +}
> +
> +void TerrainInformationGl4::prepare_frame() {
> +     for (auto& entries : global_map_) {
> +             std::shared_ptr<TerrainInformationGl4> ti = 
> entries.second.lock();
> +
> +             ti->do_prepare_frame();
> +     }
> +}
> +
> +void TerrainInformationGl4::do_update_minimap() {
> +     // Re-upload minimap data.
> +     auto& gl = Gl::State::instance();
> +     const Map& map = egbase().map();
> +     unsigned width = map.get_width();
> +     std::vector<uint8_t> data;
> +     const bool see_all = !player() || player()->see_all();
> +
> +     auto detail_bits = [&](const Widelands::BaseImmovable* imm) -> uint8_t {
> +             if (imm) {
> +                     Widelands::MapObjectType type = imm->descr().type();
> +                     if (type == Widelands::MapObjectType::ROAD)
> +                             return 1u << 6;
> +                     if (type == Widelands::MapObjectType::FLAG)
> +                             return 2u << 6;
> +                     if (type >= Widelands::MapObjectType::BUILDING)
> +                             return 3u << 6;
> +             }
> +             return 0;
> +     };
> +
> +     gl.bind(GL_TEXTURE0, minimap_texture_);
> +
> +     for (const Recti& rect : update_) {
> +             data.resize(rect.w * rect.h);
> +             if (see_all) {
> +                     unsigned i = 0;
> +                     for (unsigned y = 0; y < unsigned(rect.h); ++y) {
> +                             unsigned idx = (rect.y + y) * width + rect.x;
> +                             for (unsigned x = 0; x < unsigned(rect.w); ++x, 
> ++i, ++idx) {
> +                                     const Field& f = map[idx];
> +                                     data[i] = f.get_owned_by();
> +                                     data[i] |= 
> detail_bits(f.get_immovable());
> +                             }
> +                     }
> +             } else {
> +                     unsigned i = 0;
> +                     for (unsigned y = 0; y < unsigned(rect.h); ++y) {
> +                             unsigned idx = (rect.y + y) * width + rect.x;
> +                             for (unsigned x = 0; x < unsigned(rect.w); ++x, 
> ++i, ++idx) {
> +                                     const Player::Field& pf = 
> player()->fields()[idx];
> +                                     data[i] = pf.owner;
> +
> +                                     if (pf.vision >= 2) {
> +                                             const Field& f = map[idx];
> +                                             data[i] |= 
> detail_bits(f.get_immovable());
> +                                     }
> +                             }
> +                     }
> +             }
> +
> +             glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.w, 
> rect.h,
> +                             GL_RED_INTEGER, GL_UNSIGNED_BYTE, data.data());
> +     }
> +}
> +
> +void TerrainInformationGl4::fields_update() {
> +     auto& gl = Gl::State::instance();
> +     const Map& map = egbase().map();
> +     auto stream = uploads_.stream(sizeof(PerFieldData) * 
> uint(map.get_width()) * map.get_height());
> +     PerFieldData* fd =
> +             reinterpret_cast<PerFieldData*>
> +                     (stream.add(sizeof(PerFieldData) * 
> uint(map.get_width()) * map.get_height()));
> +     MapIndex max_index = map.max_index();
> +     const bool see_all = !player() || player()->see_all();
> +
> +     if (see_all) {
> +             for (MapIndex i = 0; i < max_index; ++i) {
> +                     const Field& f = map[i];
> +                     fd[i].terrain_r = f.terrain_r();
> +                     fd[i].terrain_d = f.terrain_d();
> +                     fd[i].height = f.get_height();
> +                     fd[i].brightness = f.get_brightness();
> +             }
> +     } else {
> +             const Player::Field* player_fields = player()->fields();
> +
> +             for (MapIndex i = 0; i < max_index; ++i) {
> +                     const Field& f = map[i];
> +                     const Player::Field& pf = player_fields[i];
> +                     fd[i].terrain_r = pf.terrains.r;
> +                     fd[i].terrain_d = pf.terrains.d;
> +                     fd[i].height = f.get_height();
> +                     fd[i].brightness = f.get_brightness();
> +             }
> +     }
> +
> +     GLintptr offset = stream.unmap();
> +     uploads_.bind();
> +
> +     gl.bind(GL_TEXTURE0, fields_texture_);
> +     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, map.get_width(), 
> map.get_height(), 0,
> +                              GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, 
> reinterpret_cast<void*>(offset));
> +
> +     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
> +
> +     fields_base_version_ = map.get_fields_base_version();
> +     if (player())
> +             terrain_vision_version_ = 
> player()->get_terrain_vision_version();
> +}
> +
> +TerrainInformationGl4::PerRoadTextureData::PerRoadTextureData(const Rectf& 
> rect)
> +  : x(rect.x), y(rect.y), w(rect.w), h(rect.h) {
> +}
> +
> +void TerrainInformationGl4::upload_road_textures() {
> +     std::vector<PerRoadTextureData> roads;
> +     std::map<const TribeDescr*, unsigned> tribe_map;
> +     PlayerNumber const nr_players = egbase().map().get_nrplayers();
> +
> +     player_roads_.resize(nr_players + 1);
> +
> +     iterate_players_existing_const(p, nr_players, egbase(), player) {
> +             const TribeDescr& tribe = player->tribe();
> +             auto it = tribe_map.find(&tribe);
> +             if (it != tribe_map.end()) {
> +                     player_roads_[p] = player_roads_[it->second];
> +             } else {
> +                     const auto& normal_textures = 
> tribe.road_textures().get_normal_textures();
> +                     player_roads_[p].normal_roads = roads.size();
> +                     player_roads_[p].num_normal_roads = 
> normal_textures.size();
> +                     for (const Image* image : normal_textures) {
> +                             const BlitData& blit_data = image->blit_data();
> +                             roads.emplace_back(to_gl_texture(blit_data));
> +                             road_texture_object_ = blit_data.texture_id;
> +                     }
> +
> +                     const auto& busy_textures = 
> tribe.road_textures().get_busy_textures();
> +                     player_roads_[p].busy_roads = roads.size();
> +                     player_roads_[p].num_busy_roads = busy_textures.size();
> +                     for (const Image* image : busy_textures)
> +                             
> roads.emplace_back(to_gl_texture(image->blit_data()));
> +
> +                     tribe_map[&tribe] = p;
> +             }
> +     }
> +
> +     road_textures_.bind();
> +     road_textures_.update(roads);
> +}
> +
> +unsigned TerrainInformationGl4::road_texture_idx(PlayerNumber owner,
> +                                                 RoadType road_type,
> +                                                 const Coords& coords,
> +                                                 WalkingDir direction) const 
> {
> +     const PlayerRoads& roads = player_roads_[owner];
> +     unsigned base, count;
> +
> +     if (road_type == RoadType::kNormal) {
> +             base = roads.normal_roads;
> +             count = roads.num_normal_roads;
> +     } else {
> +             base = roads.busy_roads;
> +             count = roads.num_busy_roads;
> +     }
> +
> +     return base + unsigned(coords.x + coords.y + direction) % count;
> +}
> +
> +// Upload the per-terrain texture data. This is done on every draw call 
> because
> +// it depends on the gametime.
> +void TerrainInformationGl4::upload_terrain_data() {
> +     uint32_t gametime = egbase().get_gametime();
> +     const auto& terrains = egbase().world().terrains();
> +     std::vector<PerTerrainData> data;
> +
> +     data.resize(terrains.size());
> +
> +     for (unsigned i = 0; i < terrains.size(); ++i) {
> +             PerTerrainData& terrain = data[i];
> +             const TerrainDescription& descr = terrains.get(i);
> +             terrain.offset =
> +                     
> to_gl_texture(descr.get_texture(gametime).blit_data()).origin();
> +             terrain.dither_layer = descr.dither_layer();
> +     }
> +
> +     terrain_data_.bind();
> +     terrain_data_.update(data);
> +}
> +
> +void TerrainInformationGl4::brightness_update() {
> +     auto& gl = Gl::State::instance();
> +     bool see_all = !player_ || player_->see_all();
> +
> +     gl.bind(GL_TEXTURE0, brightness_texture_);
> +
> +     if (see_all) {
> +             if (!brightness_see_all_) {
> +                     // Pixel unpacking has a per-row alignment of 4 bytes. 
> Usually this
> +                     // is not a problem for us, because maps' widths are 
> always multiples
> +                     // of 4, but in this particular case, OpenGL 
> implementations disagree
> +                     // about whether the alignment should be considered for 
> the bounds
> +                     // check in glTexImage2D. If we only allocate 1 byte, 
> some
> +                     // implementations flag a GL_INVALID_OPERATION.
> +                     static const uint8_t data[4] = {255, 255, 255, 255};
> +
> +                     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, 
> GL_UNSIGNED_BYTE,
> +                                  data);
> +                     brightness_see_all_ = true;
> +             }
> +     } else {
> +             const Map& map = egbase().map();
> +             int width = map.get_width();
> +             int height = map.get_height();
> +             uint32_t gametime = egbase().get_gametime();
> +             std::vector<uint8_t> data;
> +
> +             if (brightness_see_all_) {
> +                     // Resize the texture when switching between see-all 
> and not-see-all.
> +                     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, 
> GL_RED,
> +                                  GL_UNSIGNED_BYTE, NULL);
> +                     brightness_see_all_ = false;
> +             }
> +
> +             for (const Recti& rect : update_) {
> +                     data.resize(rect.w * rect.h);
> +
> +                     unsigned dst = 0;
> +                     for (unsigned y = 0; y < unsigned(rect.h); ++y) {
> +                             unsigned src = (rect.y + y) * width + rect.x;
> +                             for (unsigned x = 0; x < unsigned(rect.w); ++x, 
> ++src, ++dst) {
> +                                     const Player::Field& pf = 
> player_->fields()[src];
> +                                     if (pf.vision == 0) {
> +                                             data[dst] = 0;
> +                                     } else if (pf.vision == 1) {
> +                                             static const uint32_t 
> kDecayTimeInMs = 20000;
> +                                             const Duration time_ago = 
> gametime - pf.time_node_last_unseen;
> +                                             if (time_ago < kDecayTimeInMs) {
> +                                                     data[dst] = 255 * (2 * 
> kDecayTimeInMs - time_ago) / (2 * kDecayTimeInMs);
> +                                             } else {
> +                                                     data[dst] = 128;
> +                                             }
> +                                     } else {
> +                                             data[dst] = 255;
> +                                     }
> +                             }
> +                     }
> +
> +                     glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, 
> rect.w, rect.h,
> +                                     GL_RED, GL_UNSIGNED_BYTE, &data[0]);
> +             }
> +     }
> +}
> +
> +void TerrainInformationGl4::upload_constant_textures() {
> +     auto& gl = Gl::State::instance();
> +     const auto& terrains = egbase().world().terrains();
> +     std::vector<RGBColor> colors;
> +
> +     for (Widelands::DescriptionIndex i = 0; i < terrains.size(); ++i)
> +             colors.push_back(terrains.get(i).get_minimap_color(0));
> +
> +     glGenTextures(1, &terrain_color_texture_);
> +
> +     gl.bind(GL_TEXTURE0, terrain_color_texture_);
> +     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colors.size(), 1, 0, GL_RGB, 
> GL_UNSIGNED_BYTE,
> +                  colors.data());
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
> +
> +     colors.resize(kMaxPlayers);
> +     for (int i = 1; i <= kMaxPlayers; ++i) {
> +             const Widelands::Player* player = egbase().get_player(i);
> +             if (player)
> +                     colors[i - 1] = player->get_playercolor();
> +     }
> +
> +     glGenTextures(1, &player_color_texture_);
> +
> +     gl.bind(GL_TEXTURE0, player_color_texture_);
> +     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colors.size(), 1, 0, GL_RGB, 
> GL_UNSIGNED_BYTE,
> +                  colors.data());
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
> +}
> +
> +TerrainProgramGl4::Terrain::Terrain()
> +  : instance_data(GL_ARRAY_BUFFER) {
> +     // Initialize program.
> +     gl_program.build_vp_fp({"terrain_gl4", "terrain_common_gl4"}, 
> {"terrain_gl4"});
> +
> +     in_vertex_coordinate = glGetAttribLocation(gl_program.object(), 
> "in_vertex_coordinate");
> +     in_patch_coordinate = glGetAttribLocation(gl_program.object(), 
> "in_patch_coordinate");
> +
> +     u_position_scale = glGetUniformLocation(gl_program.object(), 
> "u_position_scale");
> +     u_position_offset = glGetUniformLocation(gl_program.object(), 
> "u_position_offset");
> +     u_z_value = glGetUniformLocation(gl_program.object(), "u_z_value");
> +     u_texture_dimensions = glGetUniformLocation(gl_program.object(), 
> "u_texture_dimensions");
> +
> +     u_terrain_base = glGetUniformLocation(gl_program.object(), 
> "u_terrain_base");
> +     u_player_brightness = glGetUniformLocation(gl_program.object(), 
> "u_player_brightness");
> +     u_terrain_texture = glGetUniformLocation(gl_program.object(), 
> "u_terrain_texture");
> +     u_dither_texture = glGetUniformLocation(gl_program.object(), 
> "u_dither_texture");
> +
> +     block_terrains_idx = glGetUniformBlockIndex(gl_program.object(), 
> "block_terrains");
> +}
> +
> +TerrainProgramGl4::Terrain::~Terrain() {
> +}
> +
> +TerrainProgramGl4::Roads::Roads()
> +  : road_data(GL_SHADER_STORAGE_BUFFER) {
> +     num_index_roads = 0;
> +
> +     // Initialize program.
> +     gl_program.build_vp_fp({"road_gl4", "terrain_common_gl4"}, {"road"});
> +
> +     u_position_scale = glGetUniformLocation(gl_program.object(), 
> "u_position_scale");
> +     u_position_offset = glGetUniformLocation(gl_program.object(), 
> "u_position_offset");
> +     u_z_value = glGetUniformLocation(gl_program.object(), "u_z_value");
> +
> +     u_terrain_base = glGetUniformLocation(gl_program.object(), 
> "u_terrain_base");
> +     u_player_brightness = glGetUniformLocation(gl_program.object(), 
> "u_player_brightness");
> +     u_texture = glGetUniformLocation(gl_program.object(), "u_texture");
> +
> +     block_textures_idx = glGetUniformBlockIndex(gl_program.object(), 
> "block_textures");
> +}
> +
> +TerrainProgramGl4::Roads::~Roads() {
> +}
> +
> +TerrainProgramGl4::MiniMap::MiniMap()
> +  : vertex_data(GL_ARRAY_BUFFER) {
> +     gl_program.build_vp_fp({"minimap_gl4"}, {"minimap_gl4"});
> +
> +     in_position = glGetAttribLocation(gl_program.object(), "in_position");
> +     in_field = glGetAttribLocation(gl_program.object(), "in_field");
> +
> +     u_layer_terrain = glGetUniformLocation(gl_program.object(), 
> "u_layer_terrain");
> +     u_layer_owner = glGetUniformLocation(gl_program.object(), 
> "u_layer_owner");
> +     u_layer_details = glGetUniformLocation(gl_program.object(), 
> "u_layer_details");
> +
> +     u_frame_topleft = glGetUniformLocation(gl_program.object(), 
> "u_frame_topleft");
> +     u_frame_bottomright = glGetUniformLocation(gl_program.object(), 
> "u_frame_bottomright");
> +
> +     u_terrain_base = glGetUniformLocation(gl_program.object(), 
> "u_terrain_base");
> +     u_player_brightness = glGetUniformLocation(gl_program.object(), 
> "u_player_brightness");
> +     u_minimap_extra = glGetUniformLocation(gl_program.object(), 
> "u_minimap_extra");
> +     u_terrain_color = glGetUniformLocation(gl_program.object(), 
> "u_terrain_color");
> +     u_player_color = glGetUniformLocation(gl_program.object(), 
> "u_player_color");
> +}
> +
> +TerrainProgramGl4::MiniMap::~MiniMap() {
> +}
> +
> +TerrainProgramGl4::TerrainProgramGl4() {
> +     log("Using GL4 terrain rendering path\n");
> +
> +     // Initialize vertex buffer (every instance/path has the same 
> structure).
> +     init_vertex_data();
> +
> +     // Load mask texture for dithering.
> +     terrain_.dither_mask.reset(new 
> Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
> +
> +     Gl::State::instance().bind(GL_TEXTURE0, 
> terrain_.dither_mask->blit_data().texture_id);
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
> static_cast<GLint>(GL_CLAMP_TO_EDGE));
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
> static_cast<GLint>(GL_CLAMP_TO_EDGE));
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
> static_cast<GLint>(GL_LINEAR));
> +     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 
> static_cast<GLint>(GL_LINEAR));
> +}
> +
> +TerrainProgramGl4::~TerrainProgramGl4() {
> +}
> +
> +bool TerrainProgramGl4::supported() {
> +     const auto& caps = Gl::State::instance().capabilities();
> +
> +     if (caps.glsl_version < 130)
> +             return false;
> +
> +     if (!caps.ARB_separate_shader_objects ||
> +         !caps.ARB_shader_storage_buffer_object ||
> +         !caps.ARB_uniform_buffer_object)
> +             return false;
> +
> +     return !g_options.pull_section("global").get_bool("disable_gl4", false);
> +}
> +
> +void TerrainProgramGl4::draw(const TerrainGl4Arguments* args,
> +                             float z_value) {
> +     auto& gl = Gl::State::instance();
> +
> +     // First, draw the terrain.
> +     glUseProgram(terrain_.gl_program.object());
> +
> +     // Coordinate transform from map coordinates to GL coordinates.
> +     float scale_x = 2.0 / args->surface_width * args->scale;
> +     float scale_y = -2.0 / args->surface_height * args->scale;
> +     float offset_x = args->surface_offset.x * scale_x - 1.0;
> +     float offset_y = args->surface_offset.y * scale_y + 1.0;
> +
> +     // Texture size
> +     const BlitData& blit_data = 
> args->terrain->egbase().world().terrains().get(0).get_texture(0).blit_data();
> +     const Rectf texture_coordinates = to_gl_texture(blit_data);
> +
> +     // Prepare uniforms.
> +     glBindBufferBase(GL_UNIFORM_BUFFER, terrain_.block_terrains_idx,
> +                      args->terrain->terrain_data_buffer_object());
> +
> +     glUniform2f(terrain_.u_position_scale, scale_x, scale_y);
> +     glUniform2f(terrain_.u_position_offset, offset_x, offset_y);
> +     glUniform1f(terrain_.u_z_value, z_value);
> +     glUniform2f(terrain_.u_texture_dimensions, texture_coordinates.w, 
> texture_coordinates.h);
> +
> +     // Prepare textures & sampler uniforms.
> +     glUniform1i(terrain_.u_terrain_base, 0);
> +     gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
> +
> +     glUniform1i(terrain_.u_player_brightness, 1);
> +     gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
> +
> +     glUniform1i(terrain_.u_terrain_texture, 2);
> +     gl.bind(GL_TEXTURE2, blit_data.texture_id);
> +
> +     glUniform1i(terrain_.u_dither_texture, 3);
> +     gl.bind(GL_TEXTURE3, terrain_.dither_mask->blit_data().texture_id);
> +
> +     // Setup vertex and instance attribute data.
> +     gl.enable_vertex_attrib_array(
> +        {terrain_.in_vertex_coordinate, terrain_.in_patch_coordinate});
> +
> +     unsigned num_instances = upload_instance_data(args);
> +
> +     terrain_.vertex_data.bind();
> +     glVertexAttribIPointer(terrain_.in_vertex_coordinate, 4, GL_INT, 
> sizeof(PerVertexData), nullptr);
> +
> +     glDrawArraysInstanced(GL_TRIANGLES, 0, 6 * kPatchWidth * kPatchHeight, 
> num_instances);
> +
> +     glVertexBindingDivisor(terrain_.in_patch_coordinate, 0);
> +}
> +
> +void TerrainProgramGl4::draw_minimap(const TerrainGl4Arguments* args,
> +                                     float z_value) {
> +     auto& gl = Gl::State::instance();
> +     const Widelands::Map& map = args->terrain->egbase().map();
> +     float width = map.get_width();
> +     float height = map.get_height();
> +
> +     glUseProgram(minimap_.gl_program.object());
> +
> +     // Prepare minimap setting uniforms
> +     glUniform1i(minimap_.u_layer_terrain, (args->minimap_layers & 
> MiniMapLayer::Terrain) ? 1 : 0);
> +     glUniform1i(minimap_.u_layer_owner, (args->minimap_layers & 
> MiniMapLayer::Owner) ? 1 : 0);
> +
> +     uint details = 0;
> +     if (args->minimap_layers & MiniMapLayer::Road)
> +             details |= 1;
> +     if (args->minimap_layers & MiniMapLayer::Flag)
> +             details |= 2;
> +     if (args->minimap_layers & MiniMapLayer::Building)
> +             details |= 4;
> +
> +     glUniform1ui(minimap_.u_layer_details, details);
> +
> +     // Prepare textures & sampler uniforms.
> +     glUniform1i(minimap_.u_terrain_base, 0);
> +     gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
> +
> +     glUniform1i(minimap_.u_player_brightness, 1);
> +     gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
> +
> +     glUniform1i(minimap_.u_minimap_extra, 2);
> +     gl.bind(GL_TEXTURE2, args->terrain->minimap_texture());
> +
> +     glUniform1i(minimap_.u_terrain_color, 3);
> +     gl.bind(GL_TEXTURE3, args->terrain->terrain_color_texture());
> +
> +     glUniform1i(minimap_.u_player_color, 4);
> +     gl.bind(GL_TEXTURE4, args->terrain->player_color_texture());
> +
> +     glUniform2f(minimap_.u_frame_topleft, (args->minfx + 0.001) / width, 
> (args->minfy + 0.001) / height);
> +     glUniform2f(minimap_.u_frame_bottomright, (args->maxfx - 0.001) / 
> width, (args->maxfy - 0.001) / height);
> +
> +     // Compute coordinates and upload vertex data.
> +     if (args->minimap_layers & MiniMapLayer::Zoom2) {
> +             width *= 2;
> +             height *= 2;
> +     }
> +
> +     float left = args->surface_offset.x;
> +     float right = left + width;
> +     float top = args->surface_offset.y;
> +     float bottom = top + height;
> +
> +     pixel_to_gl_renderbuffer(args->surface_width, args->surface_height, 
> &left, &top);
> +     pixel_to_gl_renderbuffer(args->surface_width, args->surface_height, 
> &right, &bottom);
> +
> +     float tx = args->minimap_tl_fx * (1.0 / map.get_width());
> +     float ty = args->minimap_tl_fy * (1.0 / map.get_height());
> +
> +     auto stream = minimap_.vertex_data.stream(4);
> +     MiniMap::VertexData* v = stream.add(4);
> +
> +     v[0].x = left;

Could this be done via some object/struct assigend like v[0] = {left, top, ,,, 
} ?

> +     v[0].y = top;
> +     v[0].z = z_value;
> +     v[0].tx = tx;
> +     v[0].ty = ty;
> +
> +     v[1].x = left;
> +     v[1].y = bottom;
> +     v[1].z = z_value;
> +     v[1].tx = tx;
> +     v[1].ty = ty + 1.0;
> +
> +     v[2].x = right;
> +     v[2].y = top;
> +     v[2].z = z_value;
> +     v[2].tx = tx + 1.0;
> +     v[2].ty = ty;
> +
> +     v[3].x = right;
> +     v[3].y = bottom;
> +     v[3].z = z_value;
> +     v[3].tx = tx + 1.0;
> +     v[3].ty = ty + 1.0;
> +
> +     GLintptr offset = stream.unmap();
> +
> +     gl.enable_vertex_attrib_array({minimap_.in_position, 
> minimap_.in_field});
> +
> +     minimap_.vertex_data.bind();
> +     Gl::vertex_attrib_pointer(minimap_.in_position, 3, 
> sizeof(MiniMap::VertexData), offset);
> +     Gl::vertex_attrib_pointer(minimap_.in_field, 2, 
> sizeof(MiniMap::VertexData),
> +                               offset + offsetof(MiniMap::VertexData, tx));
> +
> +     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
> +}
> +
> +void TerrainProgramGl4::draw_roads(const TerrainGl4Arguments* args,
> +                                   float z_value) {
> +     auto& gl = Gl::State::instance();
> +
> +     // Coordinate transform from map coordinates to GL coordinates.
> +     float scale_x = 2.0 / args->surface_width * args->scale;
> +     float scale_y = -2.0 / args->surface_height * args->scale;
> +     float offset_x = args->surface_offset.x * scale_x - 1.0;
> +     float offset_y = args->surface_offset.y * scale_y + 1.0;
> +
> +     glUseProgram(roads_.gl_program.object());
> +
> +     setup_road_index_buffer(args->roads.size());
> +
> +     // Prepare uniforms.
> +     glUniform2f(roads_.u_position_scale, scale_x, scale_y);
> +     glUniform2f(roads_.u_position_offset, offset_x, offset_y);
> +     glUniform1f(roads_.u_z_value, z_value);
> +
> +     // Prepare textures & sampler uniforms.
> +     glUniform1i(roads_.u_terrain_base, 0);
> +     gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
> +
> +     glUniform1i(roads_.u_player_brightness, 1);
> +     gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
> +
> +     glUniform1i(roads_.u_texture, 2);
> +     gl.bind(GL_TEXTURE2, args->terrain->road_texture_object());
> +
> +     glBindBufferBase(GL_UNIFORM_BUFFER, roads_.block_textures_idx,
> +                      args->terrain->road_textures_buffer_object());
> +
> +     upload_road_data(args);
> +
> +     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, roads_.gl_index_buffer.object());
> +     glDrawElements(GL_TRIANGLES, 6 * args->roads.size(), GL_UNSIGNED_SHORT, 
> nullptr);
> +     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
> +}
> +
> +void TerrainProgramGl4::init_vertex_data() {
> +     std::vector<PerVertexData> vertices;
> +
> +     for (int y = 0; y < int(kPatchHeight); ++y) {
> +             for (int x = 0; x < int(kPatchWidth); ++x) {
> +                     int blx = (y & 1) ? x : (x - 1);
> +
> +                     // Down triangle.
> +                     vertices.emplace_back(x,       y,     x, y, false, 2);
> +                     vertices.emplace_back(blx,     y + 1, x, y, false, 0);
> +                     vertices.emplace_back(blx + 1, y + 1, x, y, false, 1);
> +
> +                     // Right triangle.
> +                     vertices.emplace_back(x,       y,     x, y, true, 1);
> +                     vertices.emplace_back(blx + 1, y + 1, x, y, true, 2);
> +                     vertices.emplace_back(x + 1,   y,     x, y, true, 0);
> +             }
> +     }
> +
> +     assert(vertices.size() == 6 * kPatchWidth * kPatchHeight);
> +
> +     terrain_.vertex_data.bind();
> +     terrain_.vertex_data.update(vertices);
> +}
> +
> +// Determine which instances/patches to draw, upload the data and set up the
> +// vertex attributes.
> +unsigned TerrainProgramGl4::upload_instance_data(const TerrainGl4Arguments* 
> args) {
> +     int minfx = args->minfx;
> +     int minfy = args->minfy;
> +     int maxfx = args->maxfx;
> +     int maxfy = args->maxfy;
> +     if (minfy & 1)
> +             minfy--;
> +
> +     int ph = (maxfy - minfy + kPatchHeight) / kPatchHeight;
> +     int pw = (maxfx - minfx + kPatchWidth) / kPatchWidth;
> +     int num_patches = pw * ph;
> +
> +     auto stream = terrain_.instance_data.stream(num_patches);
> +     for (int py = 0; py < ph; ++py) {
> +             for (int px = 0; px < pw; ++px) {
> +                     const int fx = minfx + px * kPatchWidth;
> +                     const int fy = minfy + py * kPatchHeight;
> +
> +                     stream.emplace_back();
> +                     PerInstanceData& i = stream.back();
> +                     i.coordinate.x = fx;
> +                     i.coordinate.y = fy;
> +             }
> +     }
> +
> +     GLintptr offset = stream.unmap();
> +
> +     glVertexAttribIPointer(terrain_.in_patch_coordinate, 2, GL_INT, 
> sizeof(PerInstanceData),
> +                            reinterpret_cast<void*>(offset + 
> offsetof(PerInstanceData, coordinate)));
> +
> +     glVertexBindingDivisor(terrain_.in_patch_coordinate, 1);
> +
> +     return num_patches;
> +}
> +
> +void TerrainProgramGl4::setup_road_index_buffer(unsigned num_roads) {
> +     if (num_roads <= roads_.num_index_roads)
> +             return;
> +
> +     if (num_roads > 65536 / 4)
> +             throw wexception("Too many roads for 16-bit indices");
> +
> +     std::vector<uint16_t> indices;
> +     indices.reserve(num_roads * 6);
> +
> +     for (unsigned i = 0; i < num_roads; ++i) {
> +             indices.push_back(4 * i);
> +             indices.push_back(4 * i + 1);
> +             indices.push_back(4 * i + 2);
> +
> +             indices.push_back(4 * i + 2);
> +             indices.push_back(4 * i + 1);
> +             indices.push_back(4 * i + 3);
> +     }
> +
> +     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, roads_.gl_index_buffer.object());
> +     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * indices.size(),
> +                  indices.data(), GL_STATIC_DRAW);
> +     roads_.num_index_roads = num_roads;
> +}
> +
> +void TerrainProgramGl4::upload_road_data(const TerrainGl4Arguments* args) {
> +     assert(!args->roads.empty());
> +
> +     auto stream = roads_.road_data.stream(args->roads.size());
> +
> +     for (const TerrainGl4Arguments::Road& road : args->roads) {
> +             stream.emplace_back(
> +                     Vector2i(road.coord.x, road.coord.y), road.direction,
> +                     args->terrain->road_texture_idx(
> +                             road.owner, RoadType(road.type), road.coord, 
> WalkingDir(road.direction)));
> +     }
> +
> +     glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 
> roads_.road_data.object(),
> +                       stream.unmap(), args->roads.size() * 
> sizeof(PerRoadData));
> +}
> 
> === modified file 'src/graphic/gl/utils.cc'
> --- src/graphic/gl/utils.cc   2016-09-06 07:59:30 +0000
> +++ src/graphic/gl/utils.cc   2017-01-07 12:36:16 +0000
> @@ -169,8 +183,49 @@
>       }
>  }
>  
> +void Program::build(const std::string& program_name) {
> +     build_vp_fp({program_name}, {program_name});
> +}
> +
> +void Capabilities::check() {
> +     // Reset all variables
> +     *this = {};
> +
> +     const char* glsl_version_string = reinterpret_cast<const 
> char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
> +     int major = 0, minor = 0;
> +
> +     if (sscanf(glsl_version_string, "%d.%d", &major, &minor) != 2)
> +             log("Warning: Malformed GLSL version string: %s\n", 
> glsl_version_string);
> +
> +     glsl_version = major * 100 + minor;
> +
> +     GLint num_extensions;
> +     glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
> +
> +     for (GLint i = 0; i < num_extensions; ++i) {
> +             const char* extension = reinterpret_cast<const 
> char*>(glGetStringi(GL_EXTENSIONS, i));
> +
> +#define EXTENSION(basename) \

can this not be done without such an ugly macro?

> +             do { \
> +                     if (!strcmp(extension, "GL_" #basename)) \
> +                             basename = true; \
> +             } while (false)
> +
> +             EXTENSION(ARB_separate_shader_objects);
> +             EXTENSION(ARB_shader_storage_buffer_object);
> +             EXTENSION(ARB_uniform_buffer_object);
> +
> +#undef EXTENSION
> +     }
> +}
> +
>  State::State()
> -   : last_active_texture_(NONE), current_framebuffer_(0), 
> current_framebuffer_texture_(0) {
> +   : target_to_texture_(kMaxTextureTargets), last_active_texture_(NONE),
> +     current_framebuffer_(0), current_framebuffer_texture_(0) {
> +}
> +
> +void State::check_capabilities() {
> +     caps_.check();
>  }
>  
>  void State::bind(const GLenum target, const GLuint texture) {


-- 
https://code.launchpad.net/~nha/widelands/graphics/+merge/314279
Your team Widelands Developers is requested to review the proposed merge of 
lp:~nha/widelands/graphics into lp:widelands.

_______________________________________________
Mailing list: https://launchpad.net/~widelands-dev
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~widelands-dev
More help   : https://help.launchpad.net/ListHelp

Reply via email to