From 165f6a094465fc289d6b8a97909ae1a2265713ff Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 1 Mar 2023 19:25:51 +0200 Subject: [PATCH] Implemented terrain transition animation --- client/mapView/IMapRendererContext.h | 2 ++ client/mapView/MapRendererContext.cpp | 15 +++++++++++ client/mapView/MapRendererContext.h | 11 ++++++++ client/mapView/MapViewCache.cpp | 26 +++++++++--------- client/mapView/MapViewCache.h | 6 ++++- client/mapView/MapViewController.cpp | 38 +++++++++++++++------------ client/mapView/MapViewController.h | 3 ++- client/render/Canvas.cpp | 5 ++++ 8 files changed, 74 insertions(+), 32 deletions(-) diff --git a/client/mapView/IMapRendererContext.h b/client/mapView/IMapRendererContext.h index dfe2df654..848b93228 100644 --- a/client/mapView/IMapRendererContext.h +++ b/client/mapView/IMapRendererContext.h @@ -67,6 +67,8 @@ public: /// returns animation frame for terrain virtual size_t terrainImageIndex(size_t groupSize) const = 0; + virtual double viewTransitionProgress() const = 0; + /// if true, rendered images will be converted to grayscale virtual bool filterGrayscale() const = 0; diff --git a/client/mapView/MapRendererContext.cpp b/client/mapView/MapRendererContext.cpp index 9719ed1de..cf61e7572 100644 --- a/client/mapView/MapRendererContext.cpp +++ b/client/mapView/MapRendererContext.cpp @@ -139,6 +139,11 @@ size_t MapRendererBaseContext::overlayImageIndex(const int3 & coordinates) const return std::numeric_limits::max(); } +double MapRendererBaseContext::viewTransitionProgress() const +{ + return 0; +} + bool MapRendererBaseContext::filterGrayscale() const { return false; @@ -246,6 +251,16 @@ bool MapRendererAdventureContext::showBlockable() const return settingShowBlockable; } +MapRendererAdventureTransitionContext::MapRendererAdventureTransitionContext(const MapRendererContextState & viewState) + : MapRendererAdventureContext(viewState) +{ +} + +double MapRendererAdventureTransitionContext::viewTransitionProgress() const +{ + return progress; +} + MapRendererAdventureFadingContext::MapRendererAdventureFadingContext(const MapRendererContextState & viewState) : MapRendererAdventureContext(viewState) { diff --git a/client/mapView/MapRendererContext.h b/client/mapView/MapRendererContext.h index d9251cc90..a395093b1 100644 --- a/client/mapView/MapRendererContext.h +++ b/client/mapView/MapRendererContext.h @@ -47,6 +47,7 @@ public: size_t terrainImageIndex(size_t groupSize) const override; size_t overlayImageIndex(const int3 & coordinates) const override; + double viewTransitionProgress() const override; bool filterGrayscale() const override; bool showRoads() const override; bool showRivers() const override; @@ -79,6 +80,16 @@ public: bool showBlockable() const override; }; +class MapRendererAdventureTransitionContext : public MapRendererAdventureContext +{ +public: + double progress = 0; + + explicit MapRendererAdventureTransitionContext(const MapRendererContextState & viewState); + + double viewTransitionProgress() const override; +}; + class MapRendererAdventureFadingContext : public MapRendererAdventureContext { public: diff --git a/client/mapView/MapViewCache.cpp b/client/mapView/MapViewCache.cpp index 23b5ec8d2..8d6e801ff 100644 --- a/client/mapView/MapViewCache.cpp +++ b/client/mapView/MapViewCache.cpp @@ -30,6 +30,7 @@ MapViewCache::MapViewCache(const std::shared_ptr & model) , iconsStorage(new CAnimation("VwSymbol")) , intermediate(new Canvas(Point(32, 32))) , terrain(new Canvas(model->getCacheDimensionsPixels())) + , terrainTransition(new Canvas(model->getPixelsVisibleDimensions())) { iconsStorage->preload(); for(size_t i = 0; i < iconsStorage->size(); ++i) @@ -54,17 +55,6 @@ std::shared_ptr MapViewCache::getOverlayImageForTile(const std::shared_p return nullptr; } -void MapViewCache::invalidate(const std::shared_ptr & context, const int3 & tile) -{ - int cacheX = (terrainChecksum.shape()[0] + tile.x) % terrainChecksum.shape()[0]; - int cacheY = (terrainChecksum.shape()[1] + tile.y) % terrainChecksum.shape()[1]; - - auto & entry = terrainChecksum[cacheX][cacheY]; - - if(entry.tileX == tile.x && entry.tileY == tile.y) - entry = TileChecksum{}; -} - void MapViewCache::invalidate(const std::shared_ptr & context, const ObjectInstanceID & object) { for(size_t cacheY = 0; cacheY < terrainChecksum.shape()[1]; ++cacheY) @@ -145,7 +135,7 @@ void MapViewCache::update(const std::shared_ptr & context) void MapViewCache::render(const std::shared_ptr & context, Canvas & target, bool fullRedraw) { bool mapMoved = (cachedPosition != model->getMapViewCenter()); - bool lazyUpdate = !mapMoved && !fullRedraw; + bool lazyUpdate = !mapMoved && !fullRedraw && context->viewTransitionProgress() == 0; Rect dimensions = model->getTilesTotalRect(); @@ -164,7 +154,8 @@ void MapViewCache::render(const std::shared_ptr & context, Rect targetRect = model->getTargetTileArea(tile); target.draw(source, targetRect.topLeft()); - tilesUpToDate[cacheX][cacheY] = true; + if (!fullRedraw) + tilesUpToDate[cacheX][cacheY] = true; } } @@ -187,5 +178,14 @@ void MapViewCache::render(const std::shared_ptr & context, } } + if (context->viewTransitionProgress() != 0) + target.drawTransparent(*terrainTransition, Point(0,0), 1.0 - context->viewTransitionProgress()); + cachedPosition = model->getMapViewCenter(); } + +void MapViewCache::createTransitionSnapshot(const std::shared_ptr & context) +{ + update(context); + render(context, *terrainTransition, true); +} diff --git a/client/mapView/MapViewCache.h b/client/mapView/MapViewCache.h index 5075ec60f..95bab9827 100644 --- a/client/mapView/MapViewCache.h +++ b/client/mapView/MapViewCache.h @@ -62,7 +62,7 @@ public: explicit MapViewCache(const std::shared_ptr & model); ~MapViewCache(); - void invalidate(const std::shared_ptr & context, const int3 & tile); + /// invalidates cache of specified object void invalidate(const std::shared_ptr & context, const ObjectInstanceID & object); /// updates internal terrain cache according to provided time delta @@ -70,4 +70,8 @@ public: /// renders updated terrain cache onto provided canvas void render(const std::shared_ptr & context, Canvas & target, bool fullRedraw); + + /// creates snapshot of current view and stores it into internal canvas + /// used for view transition, e.g. Dimension Door spell or teleporters (Subterra gates / Monolith) + void createTransitionSnapshot(const std::shared_ptr & context); }; diff --git a/client/mapView/MapViewController.cpp b/client/mapView/MapViewController.cpp index a5c44bd5b..0f8c525c8 100644 --- a/client/mapView/MapViewController.cpp +++ b/client/mapView/MapViewController.cpp @@ -99,7 +99,7 @@ void MapViewController::update(uint32_t timeDelta) // - teleporting ( 250 ms) static const double fadeOutDuration = 500; static const double fadeInDuration = 500; - //static const double heroTeleportDuration = 250; + static const double heroTeleportDuration = 250; //FIXME: remove code duplication? @@ -140,13 +140,14 @@ void MapViewController::update(uint32_t timeDelta) } } - //if(teleportContext) - //{ - // teleportContext->progress += timeDelta / heroTeleportDuration; - // moveFocusToSelection(); - // if(teleportContext->progress >= 1.0) - // teleportContext.reset(); - //} + if(teleportContext) + { + teleportContext->progress += timeDelta / heroTeleportDuration; + if(teleportContext->progress >= 1.0) + { + activateAdventureContext(teleportContext->animationTime); + } + } if(fadingOutContext) { @@ -316,9 +317,8 @@ void MapViewController::onBeforeHeroTeleported(const CGHeroInstance * obj, const if(isEventVisible(obj, from, dest)) { - // TODO: generate view with old state setViewCenter(obj->getSightCenter()); - removeObject(obj); + view->createTransitionSnapshot(context); } } @@ -326,16 +326,16 @@ void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const { assert(!hasOngoingAnimations()); + removeObject(obj); + addObject(obj); + if(isEventVisible(obj, from, dest)) { - // TODO: animation + teleportContext = std::make_shared(*state); + teleportContext->animationTime = adventureContext->animationTime; + adventureContext = teleportContext; + context = teleportContext; setViewCenter(obj->getSightCenter()); - addObject(obj); - } - else - { - removeObject(obj); - addObject(obj); } } @@ -395,6 +395,9 @@ bool MapViewController::hasOngoingAnimations() if(fadingInContext) return true; + if (teleportContext) + return true; + return false; } @@ -458,6 +461,7 @@ void MapViewController::resetContext() movementContext.reset(); fadingOutContext.reset(); fadingInContext.reset(); + teleportContext.reset(); worldViewContext.reset(); spellViewContext.reset(); puzzleMapContext.reset(); diff --git a/client/mapView/MapViewController.h b/client/mapView/MapViewController.h index 2a13b158c..e705f5a4f 100644 --- a/client/mapView/MapViewController.h +++ b/client/mapView/MapViewController.h @@ -24,6 +24,7 @@ class IMapRendererContext; class MapRendererAdventureContext; class MapRendererAdventureFadingContext; class MapRendererAdventureMovingContext; +class MapRendererAdventureTransitionContext; class MapRendererWorldViewContext; class MapRendererSpellViewContext; class MapRendererPuzzleMapContext; @@ -41,7 +42,7 @@ class MapViewController : public IMapObjectObserver // only some are present at any given moment, rest are nullptr's std::shared_ptr adventureContext; std::shared_ptr movementContext; - //std::shared_ptr teleportContext; + std::shared_ptr teleportContext; std::shared_ptr fadingOutContext; std::shared_ptr fadingInContext; std::shared_ptr worldViewContext; diff --git a/client/render/Canvas.cpp b/client/render/Canvas.cpp index 5670a0a5f..6ad722df9 100644 --- a/client/render/Canvas.cpp +++ b/client/render/Canvas.cpp @@ -84,9 +84,14 @@ void Canvas::draw(const Canvas & image, const Point & pos) void Canvas::drawTransparent(const Canvas & image, const Point & pos, double transparency) { + SDL_BlendMode oldMode; + + SDL_GetSurfaceBlendMode(image.surface, &oldMode); + SDL_SetSurfaceBlendMode(image.surface, SDL_BLENDMODE_BLEND); SDL_SetSurfaceAlphaMod(image.surface, 255 * transparency); CSDL_Ext::blitSurface(image.surface, image.renderArea, surface, renderArea.topLeft() + pos); SDL_SetSurfaceAlphaMod(image.surface, 255); + SDL_SetSurfaceBlendMode(image.surface, oldMode); } void Canvas::drawScaled(const Canvas & image, const Point & pos, const Point & targetSize)