Rewrote the halo render engine...

...which solves a few minor glitches but most importantly speeds up the
drawing of the halos.
This commit is contained in:
Mark de Wever 2007-03-16 20:02:53 +00:00
parent 6438df7d23
commit 7d4cc5638b
7 changed files with 203 additions and 73 deletions

View file

@ -15,7 +15,9 @@ Version 1.3.1+svn:
* added new experimental new unit location in the hex
* tiles with overlays are also properly shown if compiled with
--enable-tinygui (bug #8720 patch #697)
* language and i18n:
* rewrote the halo render engine which solves a few minor glitches but
most importantly speeds up the drawing of the halos.
* language and i18n:
* updated translations: Bulgarian, Czech, Danish, Dutch, French, German,
Hungarian, Italian, Norwegian, Portuguese (Brazil)
* new translations: Indonesian

View file

@ -13,7 +13,8 @@ Version 1.3.1+svn:
* Improved the look of the main menu and tips of the day boxes in the title
screen.
* Added new experimental new unit location in the hex.
* Units changes and balancing:
* Added some more speed optimizations.
* Units changes and balancing:
* Converted the cold resistance of the Elvish Sorceress line to a holy
resistance.
* Decreased the holy resistance of the Orcish Assassin line to 0%.

View file

@ -710,7 +710,6 @@ void display::flip()
const surface frameBuffer = get_video_surface();
halo::render();
font::draw_floating_labels(frameBuffer);
events::raise_volatile_draw_event();
cursor::draw(frameBuffer);
@ -720,7 +719,6 @@ void display::flip()
cursor::undraw(frameBuffer);
events::raise_volatile_undraw_event();
font::undraw_floating_labels(frameBuffer);
halo::unrender();
}
namespace {
@ -851,6 +849,8 @@ void display::draw(bool update,bool force)
if(!map_.empty() && !invalidated_.empty()) {
changed = true;
halo::unrender(invalidated_);
// Units can overlap multiple hexes, so we need to (1) redraw
// them last, and (2) redraw them if they are adjacent existing hexes.
@ -879,7 +879,16 @@ void display::draw(bool update,bool force)
if (temp_unit_ && invalidated_.find(temp_unit_loc_) != invalidated_.end()) {
temp_unit_->redraw_unit(*this, temp_unit_loc_);
}
halo::render();
invalidated_.clear();
} else if (!map_.empty()) {
// if no hexes are invalidated we still need to update the haloes since
// there might be animated or expired haloes
wassert(invalidated_.empty());
halo::unrender(invalidated_);
halo::render();
}
if(redrawMinimap_) {
@ -909,6 +918,7 @@ void display::draw(bool update,bool force)
}
update_display();
}
// set the theortical next draw time
nextDraw_ += time_between_draws;

View file

@ -417,6 +417,8 @@ public:
bool in_game() const { return in_game_; }
void draw_bar(const std::string& image, int xpos, int ypos, size_t height, double filled, const SDL_Color& col, fixed_t alpha);
void get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const;
private:
display(const display&);
void operator=(const display&);
@ -434,8 +436,6 @@ private:
void bounds_check_position();
void get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const;
// std::vector<surface> getAdjacentTerrain(int x, int y, image::TYPE type, ADJACENT_TERRAIN_TYPE terrain_type);
std::vector<surface> get_terrain_images(int x, int y, image::TYPE type, ADJACENT_TERRAIN_TYPE terrain_type);
std::vector<std::string> get_fog_shroud_graphics(const gamemap::location& loc);

View file

@ -40,13 +40,18 @@ public:
void set_location(int x, int y);
void render();
bool render();
void unrender();
bool expired() const;
bool expired() const { return images_.animation_finished(); }
bool need_update() const { return images_.need_update(); }
bool does_change() const { return !images_.does_not_change(); }
bool on_location(const std::set<gamemap::location>& locations) const;
void add_overlay_location(std::set<gamemap::location>& locations);
private:
const std::string& current_image();
const std::string& current_image() { return images_.get_current_frame(); }
void rezoom();
animated<std::string> images_;
@ -59,15 +64,36 @@ private:
double origzoom_, zoom_;
surface surf_, buffer_;
SDL_Rect rect_;
// the location of the center of the halo
gamemap::location loc_;
// all location over which the halo lies
std::vector<gamemap::location> overlayed_hexes_;
};
std::map<int,effect> haloes;
std::map<int, effect> haloes;
int halo_id = 1;
bool hide_halo = false;
// Upon unrendering an invalidation list is send. All haloes in that area and the
// other invalidated haloes are stored in this set. Then there'll be tested which
// haloes overlap and they're also stored in this set.
std::set<int> invalidated_haloes;
// A newly added halo will be added to this list, these haloes don't need to
// be unrendered but do not to be rendered regardless which tiles are invalidated.
// These haloes will stay in this set until there're really rendered.
// (rendering won't happen if for example the halo is offscreen)
std::set<int> new_haloes;
// Upon deleting a halo isn't deleted but added to this set, upon unrendering the
// image is unrendered and deleted.
std::set<int> deleted_haloes;
// Haloes that have an animation or expiration time need to be checked every frame
// and are stored in this set.
std::set<int> changing_haloes;
effect::effect(int xpos, int ypos, const animated<std::string>::anim_description& img,
const gamemap::location& loc, ORIENTATION orientation, bool infinite) :
images_(img), orientation_(orientation), origx_(xpos), origy_(ypos),
@ -75,7 +101,6 @@ effect::effect(int xpos, int ypos, const animated<std::string>::anim_description
surf_(NULL), buffer_(NULL), rect_(empty_rect), loc_(loc)
{
wassert(disp != NULL);
// std::cerr << "Constructing halo sequence from image " << img << "\n";
set_location(xpos,ypos);
@ -101,15 +126,6 @@ void effect::set_location(int x, int y)
}
}
const std::string& effect::current_image()
{
static const std::string r = "";
const std::string& res = images_.get_current_frame();
return res;
}
void effect::rezoom()
{
zoom_ = disp->zoom();
@ -129,14 +145,14 @@ void effect::rezoom()
}
}
void effect::render()
bool effect::render()
{
if(disp == NULL) {
return;
return false;
}
if(loc_.x != -1 && loc_.y != -1 && disp->shrouded(loc_.x, loc_.y)) {
return;
return false;
}
images_.update_last_draw_time();
@ -147,7 +163,7 @@ void effect::render()
}
if(surf_ == NULL) {
return;
return false;
}
const gamemap::location zero_loc(0,0);
@ -162,7 +178,19 @@ void effect::render()
SDL_Rect clip_rect = disp->map_area();
if(rects_overlap(rect,clip_rect) == false) {
buffer_.assign(NULL);
return;
return false;
}
// if rendered the first time need to detemine the area affected, if a halo
// changes size it's not updated.
if(overlayed_hexes_.empty()) {
gamemap::location topleft, bottomright;
disp->get_rect_hex_bounds(rect, topleft, bottomright);
for (int x = topleft.x; x <= bottomright.x; ++x) {
for (int y = topleft.y; y <= bottomright.y; ++y) {
overlayed_hexes_.push_back(gamemap::location(x, y));
}
}
}
surface const screen = disp->video().getSurface();
@ -179,6 +207,8 @@ void effect::render()
SDL_BlitSurface(surf_,NULL,screen,&rect);
update_rect(rect_);
return true;
}
void effect::unrender()
@ -191,18 +221,42 @@ void effect::unrender()
SDL_Rect clip_rect = disp->map_area();
const clip_rect_setter clip_setter(screen,clip_rect);
SDL_Rect rect = rect_;
// due to scrolling the location of the rendered halo might have changed; recalculate
const gamemap::location zero_loc(0,0);
const int screenx = disp->get_location_x(zero_loc);
const int screeny = disp->get_location_y(zero_loc);
const int xpos = x_ + screenx - surf_->w/2;
const int ypos = y_ + screeny - surf_->h/2;
SDL_Rect rect = {xpos,ypos,surf_->w,surf_->h};
SDL_BlitSurface(buffer_,NULL,screen,&rect);
update_rect(rect_);
}
bool effect::expired() const
bool effect::on_location(const std::set<gamemap::location>& locations) const
{
return images_.animation_finished();
for(std::vector<gamemap::location>::const_iterator itor = overlayed_hexes_.begin();
itor != overlayed_hexes_.end(); ++itor) {
if(locations.find(*itor) != locations.end()) {
return true;
}
}
return false;
}
void effect::add_overlay_location(std::set<gamemap::location>& locations)
{
for(std::vector<gamemap::location>::const_iterator itor = overlayed_hexes_.begin();
itor != overlayed_hexes_.end(); ++itor) {
locations.insert(*itor);
}
}
} // namespace
manager::manager(display& screen) : old(disp)
{
disp = &screen;
@ -214,20 +268,8 @@ manager::~manager()
disp = old;
}
halo_hider::halo_hider() : old(hide_halo)
{
render();
hide_halo = true;
}
halo_hider::~halo_hider()
{
hide_halo = old;
unrender();
}
int add(int x, int y, const std::string& image, const gamemap::location& loc,
ORIENTATION orientation, bool infinite)
ORIENTATION orientation, bool infinite)
{
const int id = halo_id++;
animated<std::string>::anim_description image_vector;
@ -249,6 +291,10 @@ int add(int x, int y, const std::string& image, const gamemap::location& loc,
}
haloes.insert(std::pair<int,effect>(id,effect(x,y,image_vector,loc,orientation,infinite)));
new_haloes.insert(id);
if(haloes.find(id)->second.does_change() || !infinite) {
changing_haloes.insert(id);
}
return id;
}
@ -262,34 +308,111 @@ void set_location(int handle, int x, int y)
void remove(int handle)
{
haloes.erase(handle);
deleted_haloes.insert(handle);
}
void unrender(std::set<gamemap::location> invalidated_locations)
{
wassert(invalidated_haloes.size() == 0);
if(preferences::show_haloes() == false || haloes.size() == 0) {
return;
}
// remove expired haloes
std::map<int, effect>::iterator itor = haloes.begin();
for(; itor != haloes.end(); ++itor ) {
if(itor->second.expired()) {
deleted_haloes.insert(itor->first);
}
}
// add the haloes marked for deletion to the invalidation set
std::set<int>::const_iterator set_itor = deleted_haloes.begin();
for(;set_itor != deleted_haloes.end(); ++set_itor) {
invalidated_haloes.insert(*set_itor);
haloes.find(*set_itor)->second.add_overlay_location(invalidated_locations);
}
// test the multi-frame haloes whether they need an update
for(set_itor = changing_haloes.begin();
set_itor != changing_haloes.end(); ++set_itor) {
if(haloes.find(*set_itor)->second.need_update()) {
invalidated_haloes.insert(*set_itor);
haloes.find(*set_itor)->second.add_overlay_location(invalidated_locations);
}
}
// find all halo's in a the invalidated area
size_t halo_count;
// repeat until of haloes in the invalidated area didn't change (including none found)
// or all exisiting haloes are found
do {
halo_count = invalidated_haloes.size();
for(itor = haloes.begin(); itor != haloes.end(); ++itor) {
// test all haloes not yet in the set which match one of the locations
if(invalidated_haloes.find(itor->first) == invalidated_haloes.end() &&
itor->second.on_location(invalidated_locations)) {
// if found add all locations which the halo invalidates
// and add it to the set
itor->second.add_overlay_location(invalidated_locations);
invalidated_haloes.insert(itor->first);
}
}
} while (halo_count != invalidated_haloes.size() && halo_count != haloes.size());
if(halo_count == 0) {
return;
}
// render the haloes iterate through all the haloes and invalidate if in set
for(std::map<int, effect>::reverse_iterator ritor = haloes.rbegin(); ritor != haloes.rend(); ++ritor) {
if(invalidated_haloes.find(ritor->first) != invalidated_haloes.end()) {
ritor->second.unrender();
}
}
// realy delete the haloes marked for deletion
for(set_itor = deleted_haloes.begin(); set_itor != deleted_haloes.end(); ++set_itor) {
// it can happen a delete halo hasn't been rendered yet, invalidate them as well
new_haloes.erase(*set_itor);
changing_haloes.erase(*set_itor);
invalidated_haloes.erase(*set_itor);
haloes.erase(*set_itor);
}
deleted_haloes.clear();
}
void render()
{
if(hide_halo || preferences::show_haloes() == false) {
if(preferences::show_haloes() == false || haloes.size() == 0 ||
(new_haloes.size() == 0 && invalidated_haloes.size() == 0)) {
return;
}
for(std::map<int,effect>::iterator i = haloes.begin(); i != haloes.end(); ) {
if(i->second.expired()) {
haloes.erase(i++);
} else {
i->second.render();
++i;
// keep track of not rendered new images they have to be kept scheduled for rendering
// otherwise the invalidation area is never properly set
std::set<int> unrendered_new_haloes;
// render the haloes iterate through all the haloes and draw if in either set
for(std::map<int, effect>::iterator itor = haloes.begin();
itor != haloes.end(); ++itor) {
if(new_haloes.find(itor->first) != new_haloes.end() &&
! itor->second.render()) {
unrendered_new_haloes.insert(itor->first);
} else if(invalidated_haloes.find(itor->first) != invalidated_haloes.end()) {
itor->second.render();
}
}
}
void unrender()
{
if(hide_halo || preferences::show_haloes() == false) {
return;
}
for(std::map<int,effect>::reverse_iterator i = haloes.rbegin(); i != haloes.rend(); ++i) {
i->second.unrender();
}
invalidated_haloes.clear();
new_haloes = unrendered_new_haloes;
}
}

View file

@ -17,6 +17,7 @@
class display;
#include "map.hpp"
#include <set>
#include <string>
namespace halo
@ -31,14 +32,6 @@ private:
display* const old;
};
struct halo_hider
{
halo_hider();
~halo_hider();
private:
bool old;
};
enum ORIENTATION { NORMAL, HREVERSE, VREVERSE, HVREVERSE };
///function to add a haloing effect using 'image'
@ -64,10 +57,11 @@ struct remover
void operator()(int handle) const { remove(handle); }
};
///functions to render and unrender all haloes. Should
///be called immediately before/after flipping the screen
///functions to render and unrender haloes. Which haloes are rendered
// is determined by invalidated_locations and the internal state in
// the control sets (in halo.cpp)
void unrender(std::set<gamemap::location> invalidated_locations);
void render();
void unrender();
}

View file

@ -16,10 +16,10 @@
class config;
class CVideo;
class display;
#include "cursor.hpp"
#include "font.hpp"
#include "halo.hpp"
#include "network.hpp"
#include "tooltips.hpp"
@ -46,7 +46,7 @@ enum DIALOG_RESULT {
bool in_dialog();
struct dialog_manager : private cursor::setter, private font::floating_label_context, private halo::halo_hider {
struct dialog_manager : private cursor::setter, private font::floating_label_context {
dialog_manager();
~dialog_manager();