← Back to team overview

widelands-dev team mailing list archive

Re: [Merge] lp:~nha/widelands/graphics into lp:widelands

 

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.


References