1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Abstracted fading into separate class;

Smoother fade animations;
Added fading when recentering hero after switching levels;
This commit is contained in:
Fay 2015-01-30 23:37:28 +01:00
parent e64c08df27
commit 5e78a3147a
7 changed files with 314 additions and 82 deletions

View File

@ -276,6 +276,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
}
}
adventureInt->centerOn(hero, true); //actualizing screen pos
adventureInt->minimap.redraw();
adventureInt->heroList.update(hero);
return; //teleport - no fancy moving animation
//TODO: smooth disappear / appear effect

View File

@ -1222,3 +1222,88 @@ void CAnimation::getAnimInfo()
logGlobal->errorStream()<<", "<<anim->images.begin()->second.size()<<" image loaded in group "<< anim->images.begin()->first;
}
}
float CFadeAnimation::initialCounter() const
{
if (fadingMode == EMode::OUT)
return 1.0f;
return 0.0f;
}
void CFadeAnimation::update()
{
if (!fading)
return;
if (fadingMode == EMode::OUT)
fadingCounter -= delta;
else
fadingCounter += delta;
if (isFinished())
{
fading = false;
if (shouldFreeSurface)
{
SDL_FreeSurface(fadingSurface);
fadingSurface = nullptr;
}
}
}
bool CFadeAnimation::isFinished() const
{
if (fadingMode == EMode::OUT)
return fadingCounter <= 0.0f;
return fadingCounter >= 1.0f;
}
CFadeAnimation::CFadeAnimation()
: fadingSurface(nullptr),
fading(false),
fadingMode(EMode::NONE)
{
}
CFadeAnimation::~CFadeAnimation()
{
if (fadingSurface && shouldFreeSurface)
SDL_FreeSurface(fadingSurface);
}
void CFadeAnimation::init(EMode mode, float animDelta /* = DEFAULT_DELTA */, SDL_Surface * sourceSurface /* = nullptr */, bool freeSurfaceAtEnd /* = false */)
{
if (fading)
{
logGlobal->warnStream() << "Tried to init fading animation that is already running.";
return;
}
if (animDelta <= 0.0f)
{
logGlobal->warnStream() << "Fade anim: delta should be positive; " << animDelta << " given.";
animDelta = DEFAULT_DELTA;
}
if (sourceSurface)
fadingSurface = sourceSurface;
delta = animDelta;
fadingMode = mode;
fadingCounter = initialCounter();
fading = true;
shouldFreeSurface = freeSurfaceAtEnd;
}
void CFadeAnimation::draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect)
{
if (!fading || !fadingSurface || fadingMode == EMode::NONE)
{
fading = false;
return;
}
SDL_SetSurfaceAlphaMod(fadingSurface, fadingCounter * 255);
SDL_BlitSurface(fadingSurface, sourceRect, targetSurface, destRect);
SDL_SetSurfaceAlphaMod(fadingSurface, 255);
}

View File

@ -220,3 +220,31 @@ public:
//total count of frames in group (including not loaded)
size_t size(size_t group=0) const;
};
class CFadeAnimation
{
public:
enum class EMode
{
NONE, IN, OUT
};
private:
static constexpr float DEFAULT_DELTA = 0.05f;
float delta;
SDL_Surface * fadingSurface;
bool fading;
float fadingCounter;
bool shouldFreeSurface;
float initialCounter() const;
bool isFinished() const;
public:
EMode fadingMode;
CFadeAnimation();
~CFadeAnimation();
void init(EMode mode, float animDelta = DEFAULT_DELTA, SDL_Surface * sourceSurface = nullptr, bool freeSurfaceAtEnd = false);
void update();
void draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect);
bool isFading() const { return fading; }
};

View File

@ -12,6 +12,7 @@
#include "mapHandler.h"
#include "CBitmapHandler.h"
#include "gui/CAnimation.h"
#include "gui/SDL_Extensions.h"
#include "CGameInfo.h"
#include "../lib/mapObjects/CGHeroInstance.h"
@ -185,11 +186,12 @@ void CMapHandler::prepareFOWDefs()
}
}
void CMapHandler::drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info)
EMapAnimRedrawStatus CMapHandler::drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info, bool redrawOnlyAnim /* = false */)
{
assert(info);
updateObjectsFade();
bool hasActiveFade = updateObjectsFade();
resolveBlitter(info)->blit(targetSurface, info);
return hasActiveFade ? EMapAnimRedrawStatus::REDRAW_REQUESTED : EMapAnimRedrawStatus::OK;
}
void CMapHandler::roadsRiverTerrainInit()
@ -810,20 +812,21 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
auto & objects = tile.objects;
for(auto & object : objects)
{
if (object.fading == EMapObjectFadingType::OUT)
if (object.fadeAnimKey >= 0)
{
auto &bitmap = graphics->advmapobjGraphics[object.image]->ourImages[0].bitmap;
if (!parent->fadingOffscreenBitmapSurface)
parent->fadingOffscreenBitmapSurface = CSDL_Ext::newSurface(tileSize, tileSize, targetSurf);
Rect r1(object.rect);
r1.w = tileSize;
r1.h = tileSize;
Rect r2(realTileRect);
SDL_SetSurfaceAlphaMod(parent->fadingOffscreenBitmapSurface, object.fadingCounter * 255);
SDL_BlitSurface(bitmap, &r1, parent->fadingOffscreenBitmapSurface, nullptr);
SDL_BlitSurface(parent->fadingOffscreenBitmapSurface, nullptr, targetSurf, &r2);
SDL_SetSurfaceAlphaMod(parent->fadingOffscreenBitmapSurface, 255);
SDL_FillRect(parent->fadingOffscreenBitmapSurface, nullptr, 0);
// this object is currently fading, so skip normal drawing
// TODO fading heroes/boats will not be drawn correctly this way
auto fadeIter = parent->fadeAnims.find(object.fadeAnimKey);
if (fadeIter != parent->fadeAnims.end())
{
Rect r1(object.rect);
r1.w = tileSize;
r1.h = tileSize;
Rect r2(realTileRect);
CFadeAnimation * fade = (*fadeIter).second.second;
fade->draw(targetSurf, &r1, &r2);
}
continue;
}
@ -1164,50 +1167,67 @@ std::pair<SDL_Surface *, bool> CMapHandler::CMapBlitter::getVisBitmap() const
}
}
void CMapHandler::updateObjectsFade()
bool CMapHandler::updateObjectsFade()
{
// TODO caching fading objects indices for optimization?
for (size_t i=0; i<map->width; i++)
for (auto iter = fadeAnims.begin(); iter != fadeAnims.end(); )
{
for (size_t j=0; j<map->height; j++)
int3 pos = (*iter).second.first;
CFadeAnimation * anim = (*iter).second.second;
anim->update();
if (anim->isFading())
++iter;
else
{
for (size_t k=0; k<(map->twoLevel ? 2 : 1); k++)
auto &objs = ttiles[pos.x][pos.y][pos.z].objects;
for (auto objIter = objs.begin(); objIter != objs.end(); ++objIter)
{
for(size_t x=0; x < ttiles[i][j][k].objects.size(); )
if ((*objIter).fadeAnimKey == (*iter).first)
{
auto &obj = ttiles[i][j][k].objects[x];
switch (obj.fading)
{
case EMapObjectFadingType::IN:
obj.fadingCounter += 0.2f;
if (obj.fadingCounter >= 1.0f)
{
obj.fadingCounter = 1.0f;
obj.fading = EMapObjectFadingType::NONE;
}
++x;
break;
case EMapObjectFadingType::OUT:
obj.fadingCounter -= 0.2f;
if (obj.fadingCounter <= 0.0f)
{
obj.fadingCounter = 0.0f;
obj.fading = EMapObjectFadingType::NONE;
ttiles[i][j][k].objects.erase(ttiles[i][j][k].objects.begin() + x);
}
else
++x;
break;
default:
// not fading
++x;
break;
}
objs.erase(objIter);
break;
}
}
iter = fadeAnims.erase(iter);
}
}
// TODO caching fading objects indices for optimization?
// for (size_t i=0; i<map->width; i++)
// {
// for (size_t j=0; j<map->height; j++)
// {
// for (size_t k=0; k<(map->twoLevel ? 2 : 1); k++)
// {
// for(size_t x=0; x < ttiles[i][j][k].objects.size(); )
// {
// auto &obj = ttiles[i][j][k].objects[x];
// if (obj.fadeAnimKey >= 0)
// {
// auto fadeAnimIter = fadeAnims.find(obj.fadeAnimKey);
// if (fadeAnimIter == fadeAnims.end())
// {
// obj.fadeAnimKey = -1;
// ++x;
// continue;
// }
// obj.fadeAnim->update();
// if (obj.fadeAnim->isFading())
// {
// anyObjectsStillFading = true;
// ++x;
// }
// else if (obj.fadeAnim->fadingMode == CFadeAnimation::EMode::OUT)
// ttiles[i][j][k].objects.erase(ttiles[i][j][k].objects.begin() + x);
// }
// else
// ++x;
// }
// }
// }
// }
return !fadeAnims.empty();
}
bool CMapHandler::printObject(const CGObjectInstance *obj, bool fadein /* = false */)
@ -1215,7 +1235,7 @@ bool CMapHandler::printObject(const CGObjectInstance *obj, bool fadein /* = fals
if (!graphics->getDef(obj))
processDef(obj->appearance);
const SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
const int tilesW = bitmap->w/32;
const int tilesH = bitmap->h/32;
@ -1231,8 +1251,12 @@ bool CMapHandler::printObject(const CGObjectInstance *obj, bool fadein /* = fals
TerrainTileObject toAdd(obj, cr);
if (fadein)
{
toAdd.fading = EMapObjectFadingType::IN;
toAdd.fadingCounter = 0.0f;
auto tmp = CSDL_Ext::newSurface(bitmap->w, bitmap->h);
SDL_BlitSurface(bitmap, nullptr, tmp, nullptr); // can't be 8bpp for fading
auto anim = new CFadeAnimation();
anim->init(CFadeAnimation::EMode::IN, 0.05f, tmp, true);
fadeAnims[++fadeAnimCounter] = std::pair<int3, CFadeAnimation*>(int3(fx, fy, obj->pos.z), anim);
toAdd.fadeAnimKey = fadeAnimCounter;
}
if((obj->pos.x + fx - tilesW+1)>=0 && (obj->pos.x + fx - tilesW+1)<ttiles.size()-frameW && (obj->pos.y + fy - tilesH+1)>=0 && (obj->pos.y + fy - tilesH+1)<ttiles[0].size()-frameH)
@ -1272,11 +1296,15 @@ bool CMapHandler::hideObject(const CGObjectInstance *obj, bool fadeout /* = fals
if (ttiles[i][j][k].objects[x].obj->id == obj->id)
{
if (fadeout) // erase delayed until end of fadeout
{
ttiles[i][j][k].objects[x].fading = EMapObjectFadingType::OUT;
ttiles[i][j][k].objects[x].fadingCounter = 1.0f;
ttiles[i][j][k].objects[x].image = ttiles[i][j][k].objects[x].obj->appearance.animationFile;
// fadingObjectsCache.push_back(std::make_pair(int3(i, j, k), ttiles[i][j][k].objects[x].obj->ID));
{
auto bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
auto tmp = CSDL_Ext::newSurface(bitmap->w, bitmap->h); // TODO cache these bitmaps instead of creating new ones?
SDL_BlitSurface(bitmap, nullptr, tmp, nullptr); // can't be 8bpp for fading
auto anim = new CFadeAnimation();
anim->init(CFadeAnimation::EMode::OUT, 0.05f, tmp, true);
fadeAnims[++fadeAnimCounter] = std::pair<int3, CFadeAnimation*>(int3(i, j, k), anim);
ttiles[i][j][k].objects[x].fadeAnimKey = fadeAnimCounter;
}
else
ttiles[i][j][k].objects.erase(ttiles[i][j][k].objects.begin() + x);
@ -1410,8 +1438,8 @@ CMapHandler::~CMapHandler()
{
delete graphics->FoWfullHide;
delete graphics->FoWpartialHide;
if (fadingOffscreenBitmapSurface)
delete fadingOffscreenBitmapSurface;
// if (fadingOffscreenBitmapSurface)
// delete fadingOffscreenBitmapSurface;
delete normalBlitter;
delete worldViewBlitter;
@ -1428,6 +1456,11 @@ CMapHandler::~CMapHandler()
for(int j=0; j < elem.size(); ++j)
SDL_FreeSurface(elem[j]);
}
for (auto & elem : fadeAnims)
{
delete elem.second.second;
}
terrainGraphics.clear();
}
@ -1436,10 +1469,10 @@ CMapHandler::CMapHandler()
frameW = frameH = 0;
graphics->FoWfullHide = nullptr;
graphics->FoWpartialHide = nullptr;
fadingOffscreenBitmapSurface = nullptr;
normalBlitter = new CMapNormalBlitter(this);
worldViewBlitter = new CMapWorldViewBlitter(this);
puzzleViewBlitter = new CMapPuzzleViewBlitter(this);
fadeAnimCounter = 0;
}
void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terName )
@ -1560,3 +1593,14 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
return false;
}
TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, SDL_Rect rect_)
: obj(obj_),
rect(rect_),
fadeAnimKey(-1)
{
}
TerrainTileObject::~TerrainTileObject()
{
}

View File

@ -25,6 +25,7 @@ struct TerrainTile;
struct SDL_Surface;
struct SDL_Rect;
class CDefEssential;
class CFadeAnimation;
enum class EWorldViewIcon
{
@ -57,20 +58,20 @@ enum class EMapObjectFadingType
OUT
};
enum class EMapAnimRedrawStatus
{
OK,
REDRAW_REQUESTED // map blitter requests quick redraw due to current animation
};
struct TerrainTileObject
{
const CGObjectInstance *obj;
SDL_Rect rect;
EMapObjectFadingType fading;
float fadingCounter;
std::string image;
int fadeAnimKey;
TerrainTileObject(const CGObjectInstance *obj_, SDL_Rect rect_)
: obj(obj_),
rect(rect_),
fading(EMapObjectFadingType::NONE),
fadingCounter(0.0f)
{}
TerrainTileObject(const CGObjectInstance *obj_, SDL_Rect rect_);
~TerrainTileObject();
};
struct TerrainTile2
@ -308,11 +309,11 @@ class CMapHandler
CMapBlitter * worldViewBlitter;
CMapBlitter * puzzleViewBlitter;
// std::vector<std::pair<int3, int>> fadingObjectsCache;
SDL_Surface * fadingOffscreenBitmapSurface;
std::map<int, std::pair<int3, CFadeAnimation*>> fadeAnims;
int fadeAnimCounter;
CMapBlitter * resolveBlitter(const MapDrawingInfo * info) const;
void updateObjectsFade();
bool updateObjectsFade();
public:
PseudoV< PseudoV< PseudoV<TerrainTile2> > > ttiles; //informations about map tiles
int3 sizes; //map size (x = width, y = height, z = number of levels)
@ -358,7 +359,7 @@ public:
void roadsRiverTerrainInit();
void prepareFOWDefs();
void drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info);
EMapAnimRedrawStatus drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info, bool redrawOnlyAnim = false);
void updateWater();
void validateRectTerr(SDL_Rect * val, const SDL_Rect * ext); //terrainRect helper
static ui8 getDir(const int3 & a, const int3 & b); //returns direction number in range 0 - 7 (0 is left top, clockwise) [direction: form a to b]

View File

@ -18,6 +18,7 @@
#include "../Graphics.h"
#include "../mapHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/SDL_Extensions.h"
@ -61,7 +62,10 @@ CAdvMapInt *adventureInt;
CTerrainRect::CTerrainRect()
: curHoveredTile(-1,-1,-1), currentPath(nullptr)
: fadeSurface(nullptr),
fadeAnim(new CFadeAnimation()),
curHoveredTile(-1,-1,-1),
currentPath(nullptr)
{
tilesw=(ADVOPT.advmapW+31)/32;
tilesh=(ADVOPT.advmapH+31)/32;
@ -73,6 +77,13 @@ CTerrainRect::CTerrainRect()
addUsedEvents(LCLICK | RCLICK | HOVER | MOVE);
}
CTerrainRect::~CTerrainRect()
{
if (fadeSurface)
SDL_FreeSurface(fadeSurface);
delete fadeAnim;
}
void CTerrainRect::deactivate()
{
CIntObject::deactivate();
@ -272,8 +283,14 @@ void CTerrainRect::show(SDL_Surface * to)
info.heroAnim = adventureInt->heroAnim;
if (ADVOPT.smoothMove)
info.movement = int3(moveX, moveY, 0);
CGI->mh->drawTerrainRectNew(to, &info);
lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info);
if (fadeAnim->isFading())
{
Rect r(pos);
fadeAnim->update();
fadeAnim->draw(to, nullptr, &r);
}
if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
{
@ -298,6 +315,22 @@ void CTerrainRect::showAll(SDL_Surface * to)
}
}
void CTerrainRect::showAnim(SDL_Surface * to)
{
if (fadeAnim->isFading())
show(to);
else if (lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
{
MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos);
info.otherheroAnim = true;
info.anim = adventureInt->anim;
info.heroAnim = adventureInt->heroAnim;
if (ADVOPT.smoothMove)
info.movement = int3(moveX, moveY, 0);
lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info, true);
}
}
int3 CTerrainRect::whichTileIsIt(const int & x, const int & y)
{
int3 ret;
@ -326,6 +359,22 @@ int3 CTerrainRect::tileCountOnScreen()
}
}
void CTerrainRect::fadeFromCurrentView()
{
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if (!fadeSurface)
fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
SDL_BlitSurface(screen, &pos, fadeSurface, nullptr);
fadeAnim->init(CFadeAnimation::EMode::OUT, 0.05f, fadeSurface);
}
bool CTerrainRect::needsAnimUpdate()
{
return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
}
void CResDataBar::clickRight(tribool down, bool previousState)
{
}
@ -917,6 +966,13 @@ void CAdvMapInt::show(SDL_Surface * to)
updateScreen=false;
LOCPLINT->cingconsole->showAll(to);
}
else if (terrain.needsAnimUpdate())
{
terrain.showAnim(to);
for(int i=0;i<4;i++)
blitAt(gems[i]->ourImages[LOCPLINT->playerID.getNum()].bitmap,ADVOPT.gemX[i],ADVOPT.gemY[i],to);
}
infoBar.show(to);
statusbar.showAll(to);
}
@ -928,9 +984,15 @@ void CAdvMapInt::selectionChanged()
select(to);
}
void CAdvMapInt::centerOn(int3 on)
void CAdvMapInt::centerOn(int3 on, bool fadeIfZChanged /* = false */)
{
bool switchedLevels = on.z != position.z;
if (switchedLevels && fadeIfZChanged)
{
logGlobal->warnStream() << "START FADING";
terrain.fadeFromCurrentView();
}
switch (mode)
{
@ -962,9 +1024,9 @@ void CAdvMapInt::centerOn(int3 on)
terrain.redraw();
}
void CAdvMapInt::centerOn(const CGObjectInstance *obj)
void CAdvMapInt::centerOn(const CGObjectInstance *obj, bool fadeIfZChanged /* = false */)
{
centerOn(obj->getSightCenter());
centerOn(obj->getSightCenter(), fadeIfZChanged);
}
void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)

View File

@ -15,6 +15,8 @@ class CGTownInstance;
class CHeroWindow;
class CSpell;
class IShipyard;
enum class EMapAnimRedrawStatus;
class CFadeAnimation;
/*****************************/
@ -48,12 +50,16 @@ public:
class CTerrainRect
: public CIntObject
{
SDL_Surface * fadeSurface;
EMapAnimRedrawStatus lastRedrawStatus;
CFadeAnimation * fadeAnim;
public:
int tilesw, tilesh; //width and height of terrain to blit in tiles
int3 curHoveredTile;
int moveX, moveY; //shift between actual position of screen and the one we wil blit; ranges from -31 to 31 (in pixels)
CTerrainRect();
virtual ~CTerrainRect();
CGPath * currentPath;
void deactivate();
void clickLeft(tribool down, bool previousState);
@ -62,11 +68,15 @@ public:
void mouseMoved (const SDL_MouseMotionEvent & sEvent);
void show(SDL_Surface * to);
void showAll(SDL_Surface * to);
void showAnim(SDL_Surface * to);
void showPath(const SDL_Rect * extRect, SDL_Surface * to);
int3 whichTileIsIt(const int & x, const int & y); //x,y are cursor position
int3 whichTileIsIt(); //uses current cursor pos
/// @returns number of visible tiles on screen respecting current map scaling
int3 tileCountOnScreen();
/// animates view by caching current surface and crossfading it with normal screen
void fadeFromCurrentView();
bool needsAnimUpdate();
};
/// Resources bar which shows information about how many gold, crystals,... you have
@ -176,8 +186,8 @@ public:
void select(const CArmedInstance *sel, bool centerView = true);
void selectionChanged();
void centerOn(int3 on);
void centerOn(const CGObjectInstance *obj);
void centerOn(int3 on, bool fadeIfZChanged = false);
void centerOn(const CGObjectInstance *obj, bool fadeIfZChanged = false);
int3 verifyPos(int3 ver);
void handleRightClick(std::string text, tribool down);
void keyPressed(const SDL_KeyboardEvent & key);