SirVer has proposed merging lp:~widelands-dev/widelands/fields_to_draw into lp:widelands.
Commit message: Split 'GameRenderer' into individual functions and move drawing logic into individual classes. Requested reviews: Widelands Developers (widelands-dev) For more details, see: https://code.launchpad.net/~widelands-dev/widelands/fields_to_draw/+merge/329723 -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/fields_to_draw into lp:widelands.
=== modified file 'src/editor/editorinteractive.cc' --- src/editor/editorinteractive.cc 2017-08-28 07:39:59 +0000 +++ src/editor/editorinteractive.cc 2017-08-28 12:55:36 +0000 @@ -312,11 +312,38 @@ } void EditorInteractive::draw(RenderTarget& dst) { - const GameRenderer::Overlays overlays{get_text_to_draw(), {}}; - map_view()->draw_map_view( - egbase(), overlays, - draw_immovables_ ? GameRenderer::DrawImmovables::kYes : GameRenderer::DrawImmovables::kNo, - draw_bobs_ ? GameRenderer::DrawBobs::kYes : GameRenderer::DrawBobs::kNo, nullptr, &dst); + const auto& ebase = egbase(); + auto* fields_to_draw = map_view()->draw_terrain(ebase, &dst); + + const float scale = 1.f / map_view()->view().zoom; + const uint32_t gametime = ebase.get_gametime(); + + for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) { + const FieldsToDraw::Field& field = fields_to_draw->at(idx); + if (draw_immovables_) { + Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable(); + if (imm != nullptr && imm->get_positions(ebase).front() == field.fcoords) { + imm->draw(gametime, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst); + } + } + + if (draw_bobs_) { + for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob; + bob = bob->get_next_bob()) { + bob->draw(ebase, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst); + } + } + + // TODO(sirver): Do not use the field_overlay_manager, instead draw the + // overlays we are interested in here directly. + field_overlay_manager().foreach_overlay( + field.fcoords, [&dst, &field, scale](const Image* pic, const Vector2i& hotspot) { + dst.blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale, + pic->width() * scale, pic->height() * scale), + pic, Recti(0, 0, pic->width(), pic->height()), 1.f, + BlendMode::UseAlpha); + }); + } } /// Needed to get freehand painting tools (hold down mouse and move to edit). === modified file 'src/graphic/CMakeLists.txt' --- src/graphic/CMakeLists.txt 2017-08-11 20:01:03 +0000 +++ src/graphic/CMakeLists.txt 2017-08-28 12:55:36 +0000 @@ -164,8 +164,9 @@ base_geometry base_macros graphic_color + graphic_draw_programs + graphic_fields_to_draw graphic_terrain_programs - graphic_draw_programs logic ) @@ -177,10 +178,10 @@ base_geometry base_macros graphic + graphic_fields_to_draw graphic_gl_utils graphic_render_queue graphic_surface - graphic_terrain_programs logic wui wui_field_overlay_manager @@ -201,9 +202,22 @@ wui_mapview_pixelfunctions ) +wl_library(graphic_fields_to_draw + SRCS + gl/fields_to_draw.cc + gl/fields_to_draw.h + DEPENDS + base_geometry + graphic + graphic_gl_utils + logic + logic_constants + logic_widelands_geometry + wui_mapview_pixelfunctions +) + wl_library(graphic_terrain_programs SRCS - gl/fields_to_draw.h gl/road_program.cc gl/road_program.h gl/terrain_program.cc @@ -216,13 +230,12 @@ base_log base_macros graphic + graphic_fields_to_draw graphic_gl_utils graphic_image_io graphic_surface io_filesystem logic - logic_constants - logic_widelands_geometry ) === modified file 'src/graphic/game_renderer.cc' --- src/graphic/game_renderer.cc 2017-08-28 11:52:39 +0000 +++ src/graphic/game_renderer.cc 2017-08-28 12:55:36 +0000 @@ -66,324 +66,38 @@ * d.dither_layer, then we will repaint d with the dither texture as mask. */ -namespace { - -using namespace Widelands; - -// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for -// 'player' (which can be nullptr). -float field_brightness(const FCoords& fcoords, - const uint32_t gametime, - const Map& map, - const Player* const player) { - uint32_t brightness = 144 + fcoords.field->get_brightness(); - brightness = std::min<uint32_t>(255, (brightness * 255) / 160); - - if (player && !player->see_all()) { - const Player::Field& pf = player->fields()[Map::get_index(fcoords, map.get_width())]; - if (pf.vision == 0) { - return 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) { - brightness = (brightness * (2 * kDecayTimeInMs - time_ago)) / (2 * kDecayTimeInMs); - } else { - brightness = brightness / 2; - } - } - } - return brightness / 255.; -} - -void draw_immovables_for_visible_field(const EditorGameBase& egbase, - const FieldsToDraw::Field& field, - const float zoom, - const TextToDraw text_to_draw, - const Player* player, - RenderTarget* dst) { - BaseImmovable* const imm = field.fcoords.field->get_immovable(); - if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) { - TextToDraw draw_text_for_this_immovable = text_to_draw; - const Player* owner = imm->get_owner(); - if (player != nullptr && owner != nullptr && !player->see_all() && - player->is_hostile(*owner)) { - draw_text_for_this_immovable = - static_cast<TextToDraw>(draw_text_for_this_immovable & ~TextToDraw::kStatistics); - } - imm->draw( - egbase.get_gametime(), draw_text_for_this_immovable, field.rendertarget_pixel, zoom, dst); - } -} - -void draw_bobs_for_visible_field(const EditorGameBase& egbase, - const FieldsToDraw::Field& field, - const float zoom, - const TextToDraw text_to_draw, - const Player* player, - RenderTarget* dst) { - for (Bob* bob = field.fcoords.field->get_first_bob(); bob; bob = bob->get_next_bob()) { - TextToDraw draw_text_for_this_bob = text_to_draw; - const Player* owner = bob->get_owner(); - if (player != nullptr && owner != nullptr && !player->see_all() && - player->is_hostile(*owner)) { - draw_text_for_this_bob = - static_cast<TextToDraw>(draw_text_for_this_bob & ~TextToDraw::kStatistics); - } - bob->draw(egbase, draw_text_for_this_bob, field.rendertarget_pixel, zoom, dst); - } -} - -void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field, - const Player::Field& player_field, - const float zoom, - RenderTarget* dst) { - if (const MapObjectDescr* const map_object_descr = - player_field.map_object_descr[TCoords<>::None]) { - if (player_field.constructionsite.becomes) { - assert(field.owner != nullptr); - const ConstructionsiteInformation& csinf = player_field.constructionsite; - // draw the partly finished constructionsite - uint32_t anim_idx; - try { - anim_idx = csinf.becomes->get_animation("build"); - } catch (MapObjectDescr::AnimationNonexistent&) { - try { - anim_idx = csinf.becomes->get_animation("unoccupied"); - } catch (MapObjectDescr::AnimationNonexistent) { - anim_idx = csinf.becomes->get_animation("idle"); - } - } - const Animation& anim = g_gr->animations().get_animation(anim_idx); - const size_t nr_frames = anim.nr_frames(); - uint32_t cur_frame = - csinf.totaltime ? csinf.completedtime * nr_frames / csinf.totaltime : 0; - uint32_t tanim = cur_frame * FRAME_LENGTH; - - uint32_t percent = 100 * csinf.completedtime * nr_frames; - if (csinf.totaltime) { - percent /= csinf.totaltime; - } - percent -= 100 * cur_frame; - - if (cur_frame) { // not the first frame - // Draw the prev frame - dst->blit_animation(field.rendertarget_pixel, zoom, anim_idx, tanim - FRAME_LENGTH, - field.owner->get_playercolor()); - } else if (csinf.was) { - // Is the first frame, but there was another building here before, - // get its last build picture and draw it instead. - uint32_t a; - try { - a = csinf.was->get_animation("unoccupied"); - } catch (MapObjectDescr::AnimationNonexistent&) { - a = csinf.was->get_animation("idle"); - } - dst->blit_animation(field.rendertarget_pixel, zoom, a, tanim - FRAME_LENGTH, - field.owner->get_playercolor()); - } - dst->blit_animation(field.rendertarget_pixel, zoom, anim_idx, tanim, - field.owner->get_playercolor(), percent); - } else if (upcast(const BuildingDescr, building, map_object_descr)) { - assert(field.owner != nullptr); - // this is a building therefore we either draw unoccupied or idle animation - uint32_t pic; - try { - pic = building->get_animation("unoccupied"); - } catch (MapObjectDescr::AnimationNonexistent&) { - pic = building->get_animation("idle"); - } - dst->blit_animation( - field.rendertarget_pixel, zoom, pic, 0, field.owner->get_playercolor()); - } else if (map_object_descr->type() == MapObjectType::FLAG) { - assert(field.owner != nullptr); - dst->blit_animation(field.rendertarget_pixel, zoom, field.owner->tribe().flag_animation(), - 0, field.owner->get_playercolor()); - } else if (const uint32_t pic = map_object_descr->main_animation()) { - if (field.owner != nullptr) { - dst->blit_animation( - field.rendertarget_pixel, zoom, pic, 0, field.owner->get_playercolor()); - } else { - dst->blit_animation(field.rendertarget_pixel, zoom, pic, 0); - } - } - } -} - -// Draws the objects (animations & overlays). -void draw_objects(const EditorGameBase& egbase, - const float zoom, +void draw_border_markers(const FieldsToDraw::Field& field, + const float scale, + const FieldsToDraw& fields_to_draw, + RenderTarget* dst) { + if (!field.all_neighbors_valid() || !field.is_border) { + return; + } + assert(field.owner != nullptr); + + uint32_t const anim_idx = field.owner->tribe().frontier_animation(); + if (field.vision) { + dst->blit_animation( + field.rendertarget_pixel, scale, anim_idx, 0, field.owner->get_playercolor()); + } + for (const auto& nf : {fields_to_draw.at(field.rn_index), fields_to_draw.at(field.bln_index), + fields_to_draw.at(field.brn_index)}) { + if ((field.vision || nf.vision) && nf.is_border && + (field.owner == nf.owner || nf.owner == nullptr)) { + dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), scale, + anim_idx, 0, field.owner->get_playercolor()); + } + } +} + +void draw_terrain(const Widelands::EditorGameBase& egbase, const FieldsToDraw& fields_to_draw, - const Player* player, - const TextToDraw text_to_draw, - const GameRenderer::DrawImmovables& draw_immovables, - const GameRenderer::DrawBobs& draw_bobs, + const float scale, RenderTarget* dst) { - for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) { - const FieldsToDraw::Field& field = fields_to_draw.at(current_index); - if (!field.all_neighbors_valid()) { - continue; - } - - const FieldsToDraw::Field& rn = fields_to_draw.at(field.rn_index); - const FieldsToDraw::Field& bln = fields_to_draw.at(field.bln_index); - const FieldsToDraw::Field& brn = fields_to_draw.at(field.brn_index); - - if (field.is_border && draw_immovables == GameRenderer::DrawImmovables::kYes) { - assert(field.owner != nullptr); - uint32_t const anim_idx = field.owner->tribe().frontier_animation(); - if (field.vision) { - dst->blit_animation( - field.rendertarget_pixel, zoom, anim_idx, 0, field.owner->get_playercolor()); - } - for (const auto& nf : {rn, bln, brn}) { - if ((field.vision || nf.vision) && nf.is_border && - (field.owner == nf.owner || nf.owner == nullptr)) { - dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), zoom, - anim_idx, 0, field.owner->get_playercolor()); - } - } - } - - if (1 < field.vision) { // Render stuff that belongs to the node. - if (draw_immovables == GameRenderer::DrawImmovables::kYes) { - draw_immovables_for_visible_field(egbase, field, zoom, text_to_draw, player, dst); - } - if (draw_bobs == GameRenderer::DrawBobs::kYes) { - draw_bobs_for_visible_field(egbase, field, zoom, text_to_draw, player, dst); - } - } else if (field.vision == 1 && draw_immovables == GameRenderer::DrawImmovables::kYes) { - // We never show census or statistics for objects in the fog. - assert(player != nullptr); - const Map& map = egbase.map(); - const Player::Field& player_field = - player->fields()[map.get_index(field.fcoords, map.get_width())]; - draw_immovables_for_formerly_visible_field(field, player_field, zoom, dst); - } - - egbase.get_ibase()->field_overlay_manager().foreach_overlay( - field.fcoords, [dst, &field, zoom](const Image* pic, const Vector2i& hotspot) { - dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * zoom, - pic->width() * zoom, pic->height() * zoom), - pic, Recti(0, 0, pic->width(), pic->height()), 1.f, - BlendMode::UseAlpha); - }); - } -} - -} // namespace - -GameRenderer::GameRenderer() { -} - -GameRenderer::~GameRenderer() { -} - -void GameRenderer::render(const EditorGameBase& egbase, - const Vector2f& viewpoint, - const float zoom, - const Player* player, - const Overlays& overlays, - const DrawImmovables& draw_immovables, - const DrawBobs& draw_bobs, - RenderTarget* dst) { - assert(viewpoint.x >= 0); // divisions involving negative numbers are bad - assert(viewpoint.y >= 0); - assert(dst->get_offset().x <= 0); - assert(dst->get_offset().y <= 0); - - int minfx = std::floor(viewpoint.x / kTriangleWidth); - int minfy = std::floor(viewpoint.y / kTriangleHeight); - - // If a view window is partially moved outside of the display, its 'rect' is - // adjusted to be fully contained on the screen - i.e. x = 0 and width is - // made smaller. Its offset is made negative to account for this change. - // To figure out which fields we need to draw, we have to add the absolute - // value of 'offset' to the actual dimension of the 'rect' to get to desired - // dimension of the 'rect' - const Vector2f br_map = MapviewPixelFunctions::panel_to_map( - viewpoint, zoom, Vector2f(dst->get_rect().w + std::abs(dst->get_offset().x), - dst->get_rect().h + std::abs(dst->get_offset().y))); - int maxfx = std::ceil(br_map.x / kTriangleWidth); - int maxfy = std::ceil(br_map.y / kTriangleHeight); - - // Adjust for triangle boundary effects and for height differences. - minfx -= 2; - maxfx += 2; - minfy -= 2; - maxfy += 10; - - Surface* surface = dst->get_surface(); - if (!surface) - return; - const Recti& bounding_rect = dst->get_rect(); - const int surface_width = surface->width(); - const int surface_height = surface->height(); - - const Map& map = egbase.map(); - const uint32_t gametime = egbase.get_gametime(); - - const float scale = 1.f / zoom; - fields_to_draw_.reset(minfx, maxfx, minfy, maxfy); - for (int32_t fy = minfy; fy <= maxfy; ++fy) { - for (int32_t fx = minfx; fx <= maxfx; ++fx) { - FieldsToDraw::Field& f = - *fields_to_draw_.mutable_field(fields_to_draw_.calculate_index(fx, fy)); - - f.geometric_coords = Coords(fx, fy); - - f.ln_index = fields_to_draw_.calculate_index(fx - 1, fy); - f.rn_index = fields_to_draw_.calculate_index(fx + 1, fy); - f.trn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy - 1); - f.bln_index = fields_to_draw_.calculate_index(fx + (fy & 1) - 1, fy + 1); - f.brn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy + 1); - - // Texture coordinates for pseudo random tiling of terrain and road - // graphics. Since screen space X increases top-to-bottom and OpenGL - // increases bottom-to-top we flip the y coordinate to not have - // terrains and road graphics vertically mirrorerd. - Vector2f map_pixel = - MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords); - f.texture_coords.x = map_pixel.x / kTextureSideLength; - f.texture_coords.y = -map_pixel.y / kTextureSideLength; - - Coords normalized = f.geometric_coords; - map.normalize_coords(normalized); - f.fcoords = map.get_fcoords(normalized); - - map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor; - - f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel); - f.gl_position = f.surface_pixel = f.rendertarget_pixel + - dst->get_rect().origin().cast<float>() + - dst->get_offset().cast<float>(); - pixel_to_gl_renderbuffer( - surface_width, surface_height, &f.gl_position.x, &f.gl_position.y); - - f.brightness = field_brightness(f.fcoords, gametime, map, player); - - PlayerNumber owned_by = f.fcoords.field->get_owned_by(); - f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr; - f.is_border = f.fcoords.field->is_border(); - f.vision = 2; - f.roads = f.fcoords.field->get_roads(); - if (player && !player->see_all()) { - const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())]; - f.roads = pf.roads; - f.vision = pf.vision; - if (pf.vision == 1) { - f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr; - f.is_border = pf.border; - } - } - - const auto it = overlays.road_building_preview.find(f.fcoords); - if (it != overlays.road_building_preview.end()) { - f.roads |= it->second; - } - } - } + const Surface& surface = dst->get_surface(); + const int surface_width = surface.width(); + const int surface_height = surface.height(); // Enqueue the drawing of the terrain. RenderQueue::Item i; @@ -392,11 +106,11 @@ i.terrain_arguments.destination_rect = Rectf(bounding_rect.x, surface_height - bounding_rect.y - bounding_rect.h, bounding_rect.w, bounding_rect.h); - i.terrain_arguments.gametime = gametime; + i.terrain_arguments.gametime = egbase.get_gametime(); i.terrain_arguments.renderbuffer_width = surface_width; i.terrain_arguments.renderbuffer_height = surface_height; i.terrain_arguments.terrains = &egbase.world().terrains(); - i.terrain_arguments.fields_to_draw = &fields_to_draw_; + i.terrain_arguments.fields_to_draw = &fields_to_draw; i.terrain_arguments.scale = scale; RenderQueue::instance().enqueue(i); @@ -408,7 +122,4 @@ // Enqueue the drawing of the road layer. i.program_id = RenderQueue::Program::kTerrainRoad; RenderQueue::instance().enqueue(i); - - draw_objects(egbase, scale, fields_to_draw_, player, overlays.text_to_draw, draw_immovables, - draw_bobs, dst); } === modified file 'src/graphic/game_renderer.h' --- src/graphic/game_renderer.h 2017-08-28 11:52:39 +0000 +++ src/graphic/game_renderer.h 2017-08-28 12:55:36 +0000 @@ -26,47 +26,21 @@ #include "base/macros.h" #include "base/vector.h" #include "graphic/gl/fields_to_draw.h" +#include "logic/editor_game_base.h" #include "logic/map_objects/draw_text.h" - -namespace Widelands { -class Player; -class EditorGameBase; -} - -class RenderTarget; - -// Renders the MapView on screen. -class GameRenderer { -public: - struct Overlays { - TextToDraw text_to_draw; - std::map<Widelands::Coords, uint8_t> road_building_preview; - }; - - enum class DrawImmovables { kNo, kYes }; - enum class DrawBobs { kNo, kYes }; - - GameRenderer(); - ~GameRenderer(); - - // Renders the map from a 'player's point of view (or omniscient if nullptr) - // into the given drawing window. The 'viewpoint' is the top left screens - // pixel map pixel and 'scale' is the magnification of the view. - void render(const Widelands::EditorGameBase& egbase, - const Vector2f& viewpoint, - float zoom, - const Widelands::Player* player, - const Overlays& overlays, - const DrawImmovables& draw_immovables, - const DrawBobs& draw_bobs, - RenderTarget* dst); - -private: - // This is owned and handled by us, but handed to the RenderQueue, so we - // basically promise that this stays valid for one frame. - FieldsToDraw fields_to_draw_; - - DISALLOW_COPY_AND_ASSIGN(GameRenderer); -}; +#include "logic/player.h" + +// Draw the terrain only. +void draw_terrain(const Widelands::EditorGameBase& egbase, + const FieldsToDraw& fields_to_draw, + const float scale, + RenderTarget* dst); + +// Draw the border stones for 'field' if it is a border and 'visibility' is +// correct. +void draw_border_markers(const FieldsToDraw::Field& field, + const float scale, + const FieldsToDraw& fields_to_draw, + RenderTarget* dst); #endif // end of include guard: WL_GRAPHIC_GAME_RENDERER_H === added file 'src/graphic/gl/fields_to_draw.cc' --- src/graphic/gl/fields_to_draw.cc 1970-01-01 00:00:00 +0000 +++ src/graphic/gl/fields_to_draw.cc 2017-08-28 12:55:36 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2006-2017 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/fields_to_draw.h" + +#include "graphic/gl/coordinate_conversion.h" +#include "logic/map_objects/world/terrain_description.h" +#include "wui/mapviewpixelconstants.h" +#include "wui/mapviewpixelfunctions.h" + +namespace { + +// Returns the brightness value in [0, 1.] for 'fcoords'. +float field_brightness(const Widelands::FCoords& fcoords) { + uint32_t brightness = 144 + fcoords.field->get_brightness(); + brightness = std::min<uint32_t>(255, (brightness * 255) / 160); + return brightness / 255.; +} + +} // namespace + +void FieldsToDraw::reset(const Widelands::EditorGameBase& egbase, + const Vector2f& viewpoint, + const float zoom, + RenderTarget* dst) { + assert(viewpoint.x >= 0); // divisions involving negative numbers are bad + assert(viewpoint.y >= 0); + assert(dst->get_offset().x <= 0); + assert(dst->get_offset().y <= 0); + + int minfx = std::floor(viewpoint.x / kTriangleWidth); + int minfy = std::floor(viewpoint.y / kTriangleHeight); + + // If a view window is partially moved outside of the display, its 'rect' is + // adjusted to be fully contained on the screen - i.e. x = 0 and width is + // made smaller. Its offset is made negative to account for this change. + // To figure out which fields we need to draw, we have to add the absolute + // value of 'offset' to the actual dimension of the 'rect' to get to desired + // dimension of the 'rect' + const Vector2f br_map = MapviewPixelFunctions::panel_to_map( + viewpoint, zoom, Vector2f(dst->get_rect().w + std::abs(dst->get_offset().x), + dst->get_rect().h + std::abs(dst->get_offset().y))); + int maxfx = std::ceil(br_map.x / kTriangleWidth); + int maxfy = std::ceil(br_map.y / kTriangleHeight); + + // Adjust for triangle boundary effects and for height differences. + minfx -= 2; + maxfx += 2; + minfy -= 2; + maxfy += 10; + + const auto& surface = dst->get_surface(); + const int surface_width = surface.width(); + const int surface_height = surface.height(); + + const Widelands::Map& map = egbase.map(); + + min_fx_ = minfx; + max_fx_ = maxfx; + min_fy_ = minfy; + max_fy_ = maxfy; + w_ = max_fx_ - min_fx_ + 1; + h_ = max_fy_ - min_fy_ + 1; + const size_t dimension = w_ * h_; + if (fields_.size() != dimension) { + fields_.resize(dimension); + } + + for (int32_t fy = minfy; fy <= maxfy; ++fy) { + for (int32_t fx = minfx; fx <= maxfx; ++fx) { + FieldsToDraw::Field& f = fields_[calculate_index(fx, fy)]; + + f.geometric_coords = Widelands::Coords(fx, fy); + + f.ln_index = calculate_index(fx - 1, fy); + f.rn_index = calculate_index(fx + 1, fy); + f.trn_index = calculate_index(fx + (fy & 1), fy - 1); + f.bln_index = calculate_index(fx + (fy & 1) - 1, fy + 1); + f.brn_index = calculate_index(fx + (fy & 1), fy + 1); + + // Texture coordinates for pseudo random tiling of terrain and road + // graphics. Since screen space X increases top-to-bottom and OpenGL + // increases bottom-to-top we flip the y coordinate to not have + // terrains and road graphics vertically mirrorerd. + Vector2f map_pixel = + MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords); + f.texture_coords.x = map_pixel.x / Widelands::kTextureSideLength; + f.texture_coords.y = -map_pixel.y / Widelands::kTextureSideLength; + + Widelands::Coords normalized = f.geometric_coords; + map.normalize_coords(normalized); + f.fcoords = map.get_fcoords(normalized); + + map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor; + + f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel); + f.gl_position = f.surface_pixel = f.rendertarget_pixel + + dst->get_rect().origin().cast<float>() + + dst->get_offset().cast<float>(); + pixel_to_gl_renderbuffer( + surface_width, surface_height, &f.gl_position.x, &f.gl_position.y); + + f.brightness = field_brightness(f.fcoords); + + Widelands::PlayerNumber owned_by = f.fcoords.field->get_owned_by(); + f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr; + f.is_border = f.fcoords.field->is_border(); + f.vision = 2; + f.roads = f.fcoords.field->get_roads(); + } + } +} === modified file 'src/graphic/gl/fields_to_draw.h' --- src/graphic/gl/fields_to_draw.h 2017-08-09 17:53:24 +0000 +++ src/graphic/gl/fields_to_draw.h 2017-08-28 12:55:36 +0000 @@ -28,13 +28,12 @@ #include <stdint.h> #include "base/vector.h" -#include "logic/map_objects/tribes/road_textures.h" -#include "logic/player.h" +#include "graphic/rendertarget.h" +#include "logic/editor_game_base.h" #include "logic/widelands.h" #include "logic/widelands_geometry.h" -// Helper struct that contains the data needed for drawing all fields. All -// methods are inlined for performance reasons. +// Helper struct that contains the data needed for drawing all fields. class FieldsToDraw { public: static constexpr int kInvalidIndex = std::numeric_limits<int>::min(); @@ -77,39 +76,11 @@ } }; - FieldsToDraw() { - // Initialize everything to make cppcheck happy. - reset(0, 0, 0, 0); - } - - // Resize this fields to draw for reuse. - void reset(int minfx, int maxfx, int minfy, int maxfy) { - min_fx_ = minfx; - max_fx_ = maxfx; - min_fy_ = minfy; - max_fy_ = maxfy; - w_ = max_fx_ - min_fx_ + 1; - h_ = max_fy_ - min_fy_ + 1; - const size_t dimension = w_ * h_; - if (fields_.size() != dimension) { - fields_.resize(dimension); - } - } - - // Calculates the index of the given field with ('fx', 'fy') being geometric - // coordinates in the map. Returns kInvalidIndex if this field is not in the - // fields_to_draw. - inline int calculate_index(int fx, int fy) const { - uint16_t xidx = fx - min_fx_; - if (xidx >= w_) { - return kInvalidIndex; - } - uint16_t yidx = fy - min_fy_; - if (yidx >= h_) { - return kInvalidIndex; - } - return yidx * w_ + xidx; - } + // Reinitialize for the given view parameters. + void reset(const Widelands::EditorGameBase& egbase, + const Vector2f& viewpoint, + const float zoom, + RenderTarget* dst); // The number of fields to draw. inline size_t size() const { @@ -127,15 +98,30 @@ } private: + // Calculates the index of the given field with ('fx', 'fy') being geometric + // coordinates in the map. Returns kInvalidIndex if this field is not in the + // fields_to_draw. + inline int calculate_index(int fx, int fy) const { + uint16_t xidx = fx - min_fx_; + if (xidx >= w_) { + return kInvalidIndex; + } + uint16_t yidx = fy - min_fy_; + if (yidx >= h_) { + return kInvalidIndex; + } + return yidx * w_ + xidx; + } + // Minimum and maximum field coordinates (geometric) to render. Can be negative. - int min_fx_; - int max_fx_; - int min_fy_; - int max_fy_; + int min_fx_ = 0; + int max_fx_ = 0; + int min_fy_ = 0; + int max_fy_ = 0; // Width and height in number of fields. - int w_; - int h_; + int w_ = 0; + int h_ = 0; std::vector<Field> fields_; }; === modified file 'src/graphic/gl/road_program.cc' --- src/graphic/gl/road_program.cc 2017-05-19 06:40:44 +0000 +++ src/graphic/gl/road_program.cc 2017-08-28 12:55:36 +0000 @@ -29,6 +29,7 @@ #include "graphic/graphic.h" #include "graphic/image_io.h" #include "graphic/texture.h" +#include "logic/player.h" #include "logic/roadtype.h" // We target OpenGL 2.1 for the desktop here. === modified file 'src/graphic/render_queue.h' --- src/graphic/render_queue.h 2017-08-09 17:53:24 +0000 +++ src/graphic/render_queue.h 2017-08-28 12:55:36 +0000 @@ -118,7 +118,7 @@ int renderbuffer_width = 0; int renderbuffer_height = 0; const DescriptionMaintainer<Widelands::TerrainDescription>* terrains = nullptr; - FieldsToDraw* fields_to_draw = nullptr; + const FieldsToDraw* fields_to_draw = nullptr; float scale = 1.f; Rectf destination_rect = Rectf(0.f, 0.f, 0.f, 0.f); }; === modified file 'src/graphic/rendertarget.cc' --- src/graphic/rendertarget.cc 2017-05-21 19:07:27 +0000 +++ src/graphic/rendertarget.cc 2017-08-28 12:55:36 +0000 @@ -28,8 +28,7 @@ /** * Build a render target for the given surface. */ -RenderTarget::RenderTarget(Surface* surf) { - surface_ = surf; +RenderTarget::RenderTarget(Surface* surf) : surface_(surf) { reset(); } === modified file 'src/graphic/rendertarget.h' --- src/graphic/rendertarget.h 2017-06-24 08:47:46 +0000 +++ src/graphic/rendertarget.h 2017-08-28 12:55:36 +0000 @@ -120,8 +120,8 @@ void reset(); - Surface* get_surface() const { - return surface_; + const Surface& get_surface() const { + return *surface_; } const Recti& get_rect() const { return rect_; @@ -143,7 +143,7 @@ const int percent_from_bottom = 100); /// The target surface - Surface* surface_; + Surface* const surface_; /// The current clip rectangle Recti rect_; /// Drawing offset === modified file 'src/logic/widelands_geometry.h' --- src/logic/widelands_geometry.h 2017-08-12 06:51:08 +0000 +++ src/logic/widelands_geometry.h 2017-08-28 12:55:36 +0000 @@ -122,6 +122,7 @@ Field* field; }; +// TODO(sirver): This should not derive from CoordsType. Replace with NodeAndTriangle. template <typename CoordsType = Coords> struct TCoords : public CoordsType { enum TriangleIndex { D, R, None }; === modified file 'src/wui/CMakeLists.txt' --- src/wui/CMakeLists.txt 2017-08-20 16:12:28 +0000 +++ src/wui/CMakeLists.txt 2017-08-28 12:55:36 +0000 @@ -106,6 +106,7 @@ base_macros base_math graphic + graphic_fields_to_draw graphic_game_renderer logic logic_widelands_geometry === modified file 'src/wui/interactive_player.cc' --- src/wui/interactive_player.cc 2017-08-28 11:52:39 +0000 +++ src/wui/interactive_player.cc 2017-08-28 12:55:36 +0000 @@ -58,6 +58,146 @@ using Widelands::Building; using Widelands::Map; +namespace { + +// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for +// 'pf'. See 'field_brightness' in fields_to_draw.cc for scale of values. +float adjusted_field_brightness(const Widelands::FCoords& fcoords, + const uint32_t gametime, + const Widelands::Player::Field& pf) { + if (pf.vision == 0) { + return 0.; + }; + + uint32_t brightness = 144 + fcoords.field->get_brightness(); + brightness = std::min<uint32_t>(255, (brightness * 255) / 160); + + if (pf.vision == 1) { + static const uint32_t kDecayTimeInMs = 20000; + const Widelands::Duration time_ago = gametime - pf.time_node_last_unseen; + if (time_ago < kDecayTimeInMs) { + brightness = (brightness * (2 * kDecayTimeInMs - time_ago)) / (2 * kDecayTimeInMs); + } else { + brightness = brightness / 2; + } + } + return brightness / 255.; +} + +void draw_immovables_for_visible_field(const Widelands::EditorGameBase& egbase, + const FieldsToDraw::Field& field, + const float scale, + const TextToDraw text_to_draw, + const Widelands::Player& player, + RenderTarget* dst) { + Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable(); + if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) { + TextToDraw draw_text_for_this_immovable = text_to_draw; + const Widelands::Player* owner = imm->get_owner(); + if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) { + draw_text_for_this_immovable = + static_cast<TextToDraw>(draw_text_for_this_immovable & ~TextToDraw::kStatistics); + } + imm->draw( + egbase.get_gametime(), draw_text_for_this_immovable, field.rendertarget_pixel, scale, dst); + } +} + +void draw_bobs_for_visible_field(const Widelands::EditorGameBase& egbase, + const FieldsToDraw::Field& field, + const float scale, + const TextToDraw text_to_draw, + const Widelands::Player& player, + RenderTarget* dst) { + for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob; bob = bob->get_next_bob()) { + TextToDraw draw_text_for_this_bob = text_to_draw; + const Widelands::Player* owner = bob->get_owner(); + if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) { + draw_text_for_this_bob = + static_cast<TextToDraw>(draw_text_for_this_bob & ~TextToDraw::kStatistics); + } + bob->draw(egbase, draw_text_for_this_bob, field.rendertarget_pixel, scale, dst); + } +} + +void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field, + const Widelands::Player::Field& player_field, + const float scale, + RenderTarget* dst) { + if (const Widelands::MapObjectDescr* const map_object_descr = + player_field.map_object_descr[Widelands::TCoords<>::None]) { + if (player_field.constructionsite.becomes) { + assert(field.owner != nullptr); + const Widelands::ConstructionsiteInformation& csinf = player_field.constructionsite; + // draw the partly finished constructionsite + uint32_t anim_idx; + try { + anim_idx = csinf.becomes->get_animation("build"); + } catch (Widelands::MapObjectDescr::AnimationNonexistent&) { + try { + anim_idx = csinf.becomes->get_animation("unoccupied"); + } catch (Widelands::MapObjectDescr::AnimationNonexistent) { + anim_idx = csinf.becomes->get_animation("idle"); + } + } + const Animation& anim = g_gr->animations().get_animation(anim_idx); + const size_t nr_frames = anim.nr_frames(); + uint32_t cur_frame = + csinf.totaltime ? csinf.completedtime * nr_frames / csinf.totaltime : 0; + uint32_t tanim = cur_frame * FRAME_LENGTH; + + uint32_t percent = 100 * csinf.completedtime * nr_frames; + if (csinf.totaltime) { + percent /= csinf.totaltime; + } + percent -= 100 * cur_frame; + + if (cur_frame) { // not the first frame + // Draw the prev frame + dst->blit_animation(field.rendertarget_pixel, scale, anim_idx, tanim - FRAME_LENGTH, + field.owner->get_playercolor()); + } else if (csinf.was) { + // Is the first frame, but there was another building here before, + // get its last build picture and draw it instead. + uint32_t a; + try { + a = csinf.was->get_animation("unoccupied"); + } catch (Widelands::MapObjectDescr::AnimationNonexistent&) { + a = csinf.was->get_animation("idle"); + } + dst->blit_animation(field.rendertarget_pixel, scale, a, tanim - FRAME_LENGTH, + field.owner->get_playercolor()); + } + dst->blit_animation(field.rendertarget_pixel, scale, anim_idx, tanim, + field.owner->get_playercolor(), percent); + } else if (upcast(const Widelands::BuildingDescr, building, map_object_descr)) { + assert(field.owner != nullptr); + // this is a building therefore we either draw unoccupied or idle animation + uint32_t pic; + try { + pic = building->get_animation("unoccupied"); + } catch (Widelands::MapObjectDescr::AnimationNonexistent&) { + pic = building->get_animation("idle"); + } + dst->blit_animation( + field.rendertarget_pixel, scale, pic, 0, field.owner->get_playercolor()); + } else if (map_object_descr->type() == Widelands::MapObjectType::FLAG) { + assert(field.owner != nullptr); + dst->blit_animation(field.rendertarget_pixel, scale, field.owner->tribe().flag_animation(), + 0, field.owner->get_playercolor()); + } else if (const uint32_t pic = map_object_descr->main_animation()) { + if (field.owner != nullptr) { + dst->blit_animation( + field.rendertarget_pixel, scale, pic, 0, field.owner->get_playercolor()); + } else { + dst->blit_animation(field.rendertarget_pixel, scale, pic, 0); + } + } + } +} + +} // namespace + InteractivePlayer::InteractivePlayer(Widelands::Game& g, Section& global_s, Widelands::PlayerNumber const plyn, @@ -181,9 +321,63 @@ } void InteractivePlayer::draw_map_view(MapView* given_map_view, RenderTarget* dst) { - const GameRenderer::Overlays overlays{get_text_to_draw(), road_building_preview()}; - given_map_view->draw_map_view(egbase(), overlays, GameRenderer::DrawImmovables::kYes, - GameRenderer::DrawBobs::kYes, &player(), dst); + const Widelands::Player& plr = player(); + const auto& gbase = egbase(); + const Widelands::Map& map = gbase.map(); + const uint32_t gametime = gbase.get_gametime(); + + auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst); + const auto& roads_preview = road_building_preview(); + + for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) { + auto* f = fields_to_draw->mutable_field(idx); + + const Widelands::Player::Field& player_field = + plr.fields()[map.get_index(f->fcoords, map.get_width())]; + + // Adjust this field for visibility for this player. + if (!plr.see_all()) { + f->brightness = adjusted_field_brightness(f->fcoords, gametime, player_field); + f->roads = player_field.roads; + f->vision = player_field.vision; + if (player_field.vision == 0) { + // If the player cannot see the field, no need to do any more work. + continue; + } else if (player_field.vision == 1) { + f->owner = player_field.owner != 0 ? &gbase.player(player_field.owner) : nullptr; + f->is_border = player_field.border; + } + } + + // Add road building overlays if applicable. + const auto it = roads_preview.find(f->fcoords); + if (it != roads_preview.end()) { + f->roads |= it->second; + } + + const float scale = 1.f / given_map_view->view().zoom; + draw_border_markers(*f, scale, *fields_to_draw, dst); + + // Render stuff that belongs to the node. + if (f->vision > 1) { + const auto text_to_draw = get_text_to_draw(); + draw_immovables_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst); + draw_bobs_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst); + } else if (f->vision == 1) { + // We never show census or statistics for objects in the fog. + draw_immovables_for_formerly_visible_field(*f, player_field, scale, dst); + } + + // TODO(sirver): Do not use the field_overlay_manager, instead draw the + // overlays we are interested in here directly. + field_overlay_manager().foreach_overlay( + f->fcoords, [dst, f, scale](const Image* pic, const Vector2i& hotspot) { + dst->blitrect_scale(Rectf(f->rendertarget_pixel - hotspot.cast<float>() * scale, + pic->width() * scale, pic->height() * scale), + pic, Recti(0, 0, pic->width(), pic->height()), 1.f, + BlendMode::UseAlpha); + }); + } } void InteractivePlayer::popup_message(Widelands::MessageId const id, === modified file 'src/wui/interactive_spectator.cc' --- src/wui/interactive_spectator.cc 2017-08-28 07:39:59 +0000 +++ src/wui/interactive_spectator.cc 2017-08-28 12:55:36 +0000 @@ -109,9 +109,40 @@ } void InteractiveSpectator::draw_map_view(MapView* given_map_view, RenderTarget* dst) { - const GameRenderer::Overlays overlays{get_text_to_draw(), road_building_preview()}; - given_map_view->draw_map_view(egbase(), overlays, GameRenderer::DrawImmovables::kYes, - GameRenderer::DrawBobs::kYes, nullptr, dst); + // A spectator cannot build roads. + assert(road_building_preview().empty()); + + const auto& gbase = egbase(); + auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst); + const float scale = 1.f / given_map_view->view().zoom; + const uint32_t gametime = gbase.get_gametime(); + + const auto text_to_draw = get_text_to_draw(); + for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) { + const FieldsToDraw::Field& field = fields_to_draw->at(idx); + + draw_border_markers(field, scale, *fields_to_draw, dst); + + Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable(); + if (imm != nullptr && imm->get_positions(gbase).front() == field.fcoords) { + imm->draw(gametime, text_to_draw, field.rendertarget_pixel, scale, dst); + } + + for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob; + bob = bob->get_next_bob()) { + bob->draw(gbase, text_to_draw, field.rendertarget_pixel, scale, dst); + } + + // TODO(sirver): Do not use the field_overlay_manager, instead draw the + // overlays we are interested in here directly. + field_overlay_manager().foreach_overlay( + field.fcoords, [dst, &field, scale](const Image* pic, const Vector2i& hotspot) { + dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale, + pic->width() * scale, pic->height() * scale), + pic, Recti(0, 0, pic->width(), pic->height()), 1.f, + BlendMode::UseAlpha); + }); + } } /** === modified file 'src/wui/mapview.cc' --- src/wui/mapview.cc 2017-08-19 20:55:57 +0000 +++ src/wui/mapview.cc 2017-08-28 12:55:36 +0000 @@ -25,8 +25,6 @@ #include "base/math.h" #include "graphic/graphic.h" #include "graphic/rendertarget.h" -#include "logic/map_objects/draw_text.h" -#include "logic/player.h" #include "wlapplication.h" #include "wui/mapviewpixelfunctions.h" @@ -294,7 +292,6 @@ UI::Panel* parent, const Widelands::Map& map, int32_t x, int32_t y, uint32_t w, uint32_t h) : UI::Panel(parent, x, y, w, h), map_(map), - renderer_(new GameRenderer()), view_(), last_mouse_pos_(Vector2i::zero()), dragging_(false) { @@ -339,12 +336,7 @@ NEVER_HERE(); } -void MapView::draw_map_view(const Widelands::EditorGameBase& egbase, - const GameRenderer::Overlays& overlays, - const GameRenderer::DrawImmovables& draw_immovables, - const GameRenderer::DrawBobs& draw_bobs, - const Widelands::Player* player, - RenderTarget* dst) { +FieldsToDraw* MapView::draw_terrain(const Widelands::EditorGameBase& egbase, RenderTarget* dst) { uint32_t now = SDL_GetTicks(); while (!view_plans_.empty()) { auto& plan = view_plans_.front(); @@ -384,8 +376,10 @@ break; } - renderer_->render( - egbase, view_.viewpoint, view_.zoom, player, overlays, draw_immovables, draw_bobs, dst); + fields_to_draw_.reset(egbase, view_.viewpoint, view_.zoom, dst); + const float scale = 1.f / view_.zoom; + ::draw_terrain(egbase, fields_to_draw_, scale, dst); + return &fields_to_draw_; } void MapView::set_view(const View& target_view, const Transition& transition) { === modified file 'src/wui/mapview.h' --- src/wui/mapview.h 2017-08-19 20:55:57 +0000 +++ src/wui/mapview.h 2017-08-28 12:55:36 +0000 @@ -29,12 +29,11 @@ #include "base/rect.h" #include "base/vector.h" #include "graphic/game_renderer.h" +#include "graphic/gl/fields_to_draw.h" #include "logic/map.h" #include "logic/widelands_geometry.h" #include "ui_basic/panel.h" -class GameRenderer; - /** * Implements a view of a map. It is used to render a valid map on the screen. */ @@ -159,13 +158,12 @@ // True if a 'Transition::Smooth' animation is playing. bool is_animating() const; + // Schedules drawing of the terrain of this MapView. The returned value can + // be used to override contents of 'fields_to_draw' for player knowledge and + // visibility, and to correctly draw map objects, overlays and text. + FieldsToDraw* draw_terrain(const Widelands::EditorGameBase& egbase, RenderTarget* dst); + // Not overriden from UI::Panel, instead we expect to be passed the data through. - void draw_map_view(const Widelands::EditorGameBase& egbase, - const GameRenderer::Overlays& overlays, - const GameRenderer::DrawImmovables& draw_immovables, - const GameRenderer::DrawBobs& draw_bobs, - const Widelands::Player* player, - RenderTarget* dst); bool handle_mousepress(uint8_t btn, int32_t x, int32_t y); bool handle_mouserelease(uint8_t btn, int32_t x, int32_t y); bool handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff); @@ -190,7 +188,11 @@ Vector2f to_map(const Vector2i& panel_pixel) const; const Widelands::Map& map_; - std::unique_ptr<GameRenderer> renderer_; + + // This is owned and handled by us, but handed to the RenderQueue, so we + // basically promise that this stays valid for one frame. + FieldsToDraw fields_to_draw_; + View view_; Vector2i last_mouse_pos_; bool dragging_;
_______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp