Skip to content

Commit

Permalink
Added new minimap drawing method that works wholly with textures
Browse files Browse the repository at this point in the history
I've left the old getMinimap function alone and added a new function that will eventually replace it
that utilizes texture rendering to a given texture instead of relying on surface blits and scaling.
This has the benefit of, well, reducing surface ops, for one, as well as removing the need to create
a new texture from the minimap surface every time it's updated.
  • Loading branch information
Vultraz committed Jul 26, 2017
1 parent caa4944 commit b0c80e0
Show file tree
Hide file tree
Showing 2 changed files with 273 additions and 12 deletions.
262 changes: 254 additions & 8 deletions src/minimap.cpp
Expand Up @@ -16,29 +16,28 @@

#include "minimap.hpp"

#include "color.hpp"
#include "formula/string_utils.hpp"
#include "game_board.hpp"
#include "game_display.hpp"
#include "gettext.hpp"
#include "image.hpp"
#include "log.hpp"
#include "map/map.hpp"
#include "preferences/general.hpp"
#include "resources.hpp"
#include "color.hpp"
#include "sdl/render_utils.hpp"
#include "sdl/surface.hpp"
#include "team.hpp"
#include "terrain/type_data.hpp"
#include "wml_exception.hpp"
#include "formula/string_utils.hpp"

#include "game_display.hpp"

#include "preferences/general.hpp"

static lg::log_domain log_display("display");
#define DBG_DP LOG_STREAM(debug, log_display)
#define WRN_DP LOG_STREAM(warn, log_display)


namespace image {
namespace image
{

surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map<map_location,unsigned int> *reach_map)
{
Expand Down Expand Up @@ -289,5 +288,252 @@ surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::
return minimap;
}

void render_minimap(texture& tex, const gamemap& map, const team* vw, const std::map<map_location, unsigned int>* reach_map)
{
if(tex.null()) {
return;
}

CVideo& video = CVideo::get_singleton();

// Validate that the passed texture is the current render target.
// TODO: if it's not, maybe set it here?
assert(SDL_GetRenderTarget(video.get_renderer()) == tex);

const terrain_type_data& tdata = *map.tdata();

// Drawing mode flags.
const bool preferences_minimap_draw_terrain = preferences::minimap_draw_terrain();
const bool preferences_minimap_terrain_coding = preferences::minimap_terrain_coding();
const bool preferences_minimap_draw_villages = preferences::minimap_draw_villages();
const bool preferences_minimap_unit_coding = preferences::minimap_movement_coding();

const int scale = (preferences_minimap_draw_terrain && preferences_minimap_terrain_coding) ? 24 : 4;

DBG_DP << "Creating minimap: " << static_cast<int>(map.w() * scale * 0.75) << ", " << map.h() * scale << std::endl;

const size_t map_width = map.w() * scale * 3 / 4;
const size_t map_height = map.h() * scale;

// No map!
if(map_width == 0 || map_height == 0) {
return;
}

// Nothing to draw!
if(!preferences_minimap_draw_villages && !preferences_minimap_draw_terrain) {
return;
}

// We want to draw the minimap with NN scaling.
set_texture_scale_quality("nearest");

// Create a temp surface a bit larger than we want. This allows us to compose the minimap and then
// scale the whole result down the desired destination texture size.
texture minimap(map_width, map_height, SDL_TEXTUREACCESS_TARGET);
if(minimap.null()) {
return;
}

{
// Point rendering to the temp minimap texture.
render_target_setter target_setter(minimap);

for(int y = 0; y <= map.total_height(); ++y) {
for(int x = 0; x <= map.total_width(); ++x) {
const map_location loc(x, y);

if(!map.on_board_with_border(loc)) {
continue;
}

const bool highlighted = reach_map && reach_map->count(loc) != 0;

const bool shrouded =
(resources::screen != nullptr && resources::screen->is_blindfolded()) ||
(vw != nullptr && vw->shrouded(loc));

// Shrouded hex are not considered fogged (no need to fog a black image)
const bool fogged = (vw != nullptr && !shrouded && vw->fogged(loc));

const t_translation::terrain_code terrain = shrouded ? t_translation::VOID_TERRAIN : map[loc];
const terrain_type& terrain_info = tdata.get_terrain_info(terrain);

// Destination rect for drawing the current hex.
// We need a balanced shift up and down of the hexes.
// If not, only the bottom half-hexes are clipped and it looks asymmetrical.
SDL_Rect map_dst_rect {
x * scale * 3 / 4 - (scale / 4),
y * scale + scale / 4 * (is_odd(x) ? 1 : -1) - (scale / 4),
scale,
scale
};

//
// Draw map terrain...
//
if(preferences_minimap_draw_terrain) {
if(preferences_minimap_terrain_coding) {
//
// ...either using terrain images...
//
if(!terrain_info.minimap_image().empty()) {
const std::string base_file = "terrain/" + terrain_info.minimap_image() + ".png";
const texture& tile = image::get_texture(base_file); // image::HEXED

// TODO: handle fog (was a -50, -50, -50 adjust) and highlight (was a 50, 50, 50 adjust).
video.render_copy(tile, nullptr, &map_dst_rect);

// NOTE: we skip the overlay when base is missing (to avoid hiding the error)
if(tile && tdata.get_terrain_info(terrain).is_combined()
&& !terrain_info.minimap_image_overlay().empty())
{
const std::string overlay_file = "terrain/" + terrain_info.minimap_image_overlay() + ".png";

const texture& overlay = image::get_texture(overlay_file); // image::HEXED

// TODO: crop/center overlays?
video.render_copy(overlay, nullptr, &map_dst_rect);
}
}
} else {
//
// ... or color coding.
//
color_t col(0, 0, 0, 0);

auto it = game_config::team_rgb_range.find(terrain_info.id());
if(it == game_config::team_rgb_range.end()) {
col = it->second.rep();
}

bool first = true;
const t_translation::ter_list& underlying_terrains = tdata.underlying_union_terrain(terrain);

for(const t_translation::terrain_code& underlying_terrain : underlying_terrains) {
const std::string& terrain_id = tdata.get_terrain_info(underlying_terrain).id();

it = game_config::team_rgb_range.find(terrain_id);
if(it == game_config::team_rgb_range.end()) {
continue;
}

color_t tmp = it->second.rep();

if(fogged) {
if(tmp.b < 50) {
tmp.b = 0;
} else {
tmp.b -= 50;
}

if(tmp.g < 50) {
tmp.g = 0;
} else {
tmp.g -= 50;
}

if(tmp.r < 50) {
tmp.r = 0;
} else {
tmp.r -= 50;
}
}

if(highlighted) {
if(tmp.b > 205) {
tmp.b = 255;
} else {
tmp.b += 50;
}

if(tmp.g > 205) {
tmp.g = 255;
} else {
tmp.g += 50;
}

if(tmp.r > 205) {
tmp.r = 255;
} else {
tmp.r += 50;
}
}

if(first) {
first = false;
col = tmp;
} else {
col.r = col.r - (col.r - tmp.r) / 2;
col.g = col.g - (col.g - tmp.g) / 2;
col.b = col.b - (col.b - tmp.b) / 2;
}
}

SDL_Rect fillrect {map_dst_rect.x, map_dst_rect.y, scale * 3 / 4, scale};
sdl::fill_rectangle(fillrect, col);
}
}

//
// Draw village markers independent of terrain.
//
if(terrain_info.is_village() && preferences_minimap_draw_villages) {
// Check needed for mp create dialog
const int side = resources::gameboard
? resources::gameboard->village_owner(loc)
: -1;

color_t col(255, 255, 255);

// TODO: Add a key to [game_config][colors] for this
auto iter = game_config::team_rgb_range.find("white");
if(iter != game_config::team_rgb_range.end()) {
col = iter->second.min();
}

if(!fogged && side > -1) {
if(preferences_minimap_unit_coding || !vw) {
col = team::get_minimap_color(side + 1);
} else {
if(vw->owns_village(loc)) {
col = game_config::color_info(preferences::unmoved_color()).rep();
} else if(vw->is_enemy(side + 1)) {
col = game_config::color_info(preferences::enemy_color()).rep();
} else {
col = game_config::color_info(preferences::allied_color()).rep();
}
}
}

SDL_Rect fillrect {map_dst_rect.x, map_dst_rect.y, scale * 3 / 4, scale};
sdl::fill_rectangle(fillrect, col);
}
}
}
}

texture::info src_info = minimap.get_info();
texture::info dst_info = tex.get_info();

const double wratio = dst_info.w * 1.0 / src_info.w;
const double hratio = dst_info.h * 1.0 / src_info.h;

const double ratio = std::min<double>(wratio, hratio);

// TODO: maybe add arguments so we can set render origin?
SDL_Rect final_dst_rect {
0,
0,
static_cast<int>(src_info.w * ratio),
static_cast<int>(src_info.h * ratio)
};

// Finally, render the composited minimap texture (scaled down) to the render target,
// which should be the passed texture.
video.render_copy(minimap, nullptr, &final_dst_rect);

DBG_DP << "done generating minimap" << std::endl;
}

} // end namespace image
23 changes: 19 additions & 4 deletions src/minimap.hpp
Expand Up @@ -14,18 +14,33 @@

#pragma once

#include "global.hpp"

#include <cstddef>
#include <map>

class gamemap;
class surface;
class team;
class texture;
struct map_location;
class gamemap;

namespace image
{
/// function to create the minimap for a given map
/// the surface returned must be freed by the user
DEPRECATED("") surface getMinimap(int w,
int h,
const gamemap& map_,
const team* vm = nullptr,
const std::map<map_location, unsigned int>* reach_map = nullptr);

namespace image {
///function to create the minimap for a given map
///the surface returned must be freed by the user
surface getMinimap(int w, int h, const gamemap &map_, const team *vm = nullptr, const std::map<map_location,unsigned int> *reach_map = nullptr);
/**
* Renders the minimap to the given texture.
*/
void render_minimap(texture& tex,
const gamemap& map,
const team* vw = nullptr,
const std::map<map_location, unsigned int>* reach_map = nullptr);
}

0 comments on commit b0c80e0

Please sign in to comment.