From b9a660f6c34c2e3b935d0cb7d9c3c03f74b80d8c Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 20 Oct 2023 01:25:06 +0200 Subject: [PATCH] Redesign map editor rendering --- lib/mapping/ObstacleProxy.cpp | 3 +- lib/mapping/ObstacleProxy.h | 4 +- mapeditor/mainwindow.cpp | 2 - mapeditor/mapcontroller.cpp | 16 +- mapeditor/maphandler.cpp | 308 ++++++++++++---------------------- mapeditor/maphandler.h | 50 +++--- mapeditor/scenelayer.cpp | 8 +- 7 files changed, 151 insertions(+), 240 deletions(-) diff --git a/lib/mapping/ObstacleProxy.cpp b/lib/mapping/ObstacleProxy.cpp index d6a30de33..fd295572b 100644 --- a/lib/mapping/ObstacleProxy.cpp +++ b/lib/mapping/ObstacleProxy.cpp @@ -208,10 +208,11 @@ bool EditorObstaclePlacer::isInTheMap(const int3& tile) return map->isInTheMap(tile); } -void EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand) +std::set EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand) { auto obstacles = createObstacles(rand); finalInsertion(map->getEditManager(), obstacles); + return obstacles; } VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/ObstacleProxy.h b/lib/mapping/ObstacleProxy.h index ef30e4bd0..70f1f46df 100644 --- a/lib/mapping/ObstacleProxy.h +++ b/lib/mapping/ObstacleProxy.h @@ -67,10 +67,10 @@ public: bool isInTheMap(const int3& tile) override; - void placeObstacles(CRandomGenerator& rand); + std::set placeObstacles(CRandomGenerator& rand); private: CMap* map; }; -VCMI_LIB_NAMESPACE_END \ No newline at end of file +VCMI_LIB_NAMESPACE_END diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 206dd6c8e..680e4337a 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -1095,9 +1095,7 @@ void MainWindow::on_actionUpdate_appearance_triggered() } app = templates.front(); } - auto tiles = controller.mapHandler()->getTilesUnderObject(obj); obj->appearance = app; - controller.mapHandler()->invalidate(tiles); controller.mapHandler()->invalidate(obj); controller.scene(mapLevel)->selectionObjectsView.deselectObject(obj); } diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index ee0ba16fc..4912541de 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -350,10 +350,10 @@ void MapController::commitObjectErase(int level) return; } - for (auto obj : selectedObjects) + for (auto & obj : selectedObjects) { //invalidate tiles under objects - _mapHandler->invalidate(_mapHandler->getTilesUnderObject(obj)); + _mapHandler->removeObject(obj); _scenes[level]->objectsView.setDirty(obj); } @@ -454,14 +454,16 @@ void MapController::commitObstacleFill(int level) for(auto & sel : _obstaclePainters) { - sel.second->placeObstacles(CRandomGenerator::getDefault()); + for(auto * o : sel.second->placeObstacles(CRandomGenerator::getDefault())) + { + _mapHandler->invalidate(o); + _scenes[level]->objectsView.setDirty(o); + } } - - _mapHandler->invalidateObjects(); _scenes[level]->selectionTerrainView.clear(); _scenes[level]->selectionTerrainView.draw(); - _scenes[level]->objectsView.draw(false); //TODO: enable smart invalidation (setDirty) + _scenes[level]->objectsView.draw(); _scenes[level]->passabilityView.update(); _miniscenes[level]->updateViews(); @@ -500,10 +502,8 @@ void MapController::commitObjectShift(int level) pos.z = level; pos.x += shift.x(); pos.y += shift.y(); - auto prevPositions = _mapHandler->getTilesUnderObject(obj); _scenes[level]->objectsView.setDirty(obj); //set dirty before movement _map->getEditManager()->moveObject(obj, pos); - _mapHandler->invalidate(prevPositions); _mapHandler->invalidate(obj); } } diff --git a/mapeditor/maphandler.cpp b/mapeditor/maphandler.cpp index 922e38345..8d4ff8b30 100644 --- a/mapeditor/maphandler.cpp +++ b/mapeditor/maphandler.cpp @@ -25,14 +25,14 @@ const int tileSize = 32; -static bool objectBlitOrderSorter(const TileObject & a, const TileObject & b) +static bool objectBlitOrderSorter(const ObjectRect & a, const ObjectRect & b) { return MapHandler::compareObjectBlitOrder(a.obj, b.obj); } int MapHandler::index(int x, int y, int z) const { - return z * (sizes.x * sizes.y) + y * sizes.x + x; + return z * (map->width * map->height) + y * map->width + x; } int MapHandler::index(const int3 & p) const @@ -48,14 +48,7 @@ MapHandler::MapHandler() void MapHandler::reset(const CMap * Map) { - ttiles.clear(); map = Map; - - //sizes of terrain - sizes.x = map->width; - sizes.y = map->height; - sizes.z = map->twoLevel ? 2 : 1; - initObjectRects(); logGlobal->info("\tMaking object rects"); } @@ -176,67 +169,101 @@ void setPlayerColor(QImage * sur, PlayerColor player) logGlobal->warn("Warning, setPlayerColor called on not 8bpp surface!"); } -void MapHandler::initObjectRects() +std::shared_ptr MapHandler::getObjectImage(const CGObjectInstance * obj) { - ttiles.resize(sizes.x * sizes.y * sizes.z); - - //initializing objects / rects - for(const CGObjectInstance * elem : map->objects) + if( !obj + || (obj->ID==Obj::HERO && static_cast(obj)->inTownGarrison) //garrisoned hero + || (obj->ID==Obj::BOAT && static_cast(obj)->hero)) //boat with hero (hero graphics is used) { - CGObjectInstance *obj = const_cast(elem); - if( !obj - || (obj->ID==Obj::HERO && static_cast(obj)->inTownGarrison) //garrisoned hero - || (obj->ID==Obj::BOAT && static_cast(obj)->hero)) //boat with hero (hero graphics is used) + return nullptr; + } + + std::shared_ptr animation = graphics->getAnimation(obj); + + //no animation at all + if(!animation) + return nullptr; + + //empty animation + if(animation->size(0) == 0) + return nullptr; + + auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0); + if(!image) + { + //workaround for prisons + image = animation->getImage(0, 0); + } + + return image; +} + +std::set MapHandler::removeObject(const CGObjectInstance *object) +{ + std::set result = tilesCache[object]; + for(auto & t : result) + { + auto & objects = getObjects(t); + for(auto iter = objects.begin(); iter != objects.end(); ++iter) { - continue; - } - - std::shared_ptr animation = graphics->getAnimation(obj); - - //no animation at all - if(!animation) - continue; - - //empty animation - if(animation->size(0) == 0) - continue; - - auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0); - if(!image) - { - //workaround for prisons - image = animation->getImage(0, 0); - if(!image) - continue; - } - - - for(int fx=0; fx < obj->getWidth(); ++fx) - { - for(int fy=0; fy < obj->getHeight(); ++fy) + if(iter->obj == object) { - int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); - QRect cr(image->width() - fx * tileSize - tileSize, - image->height() - fy * tileSize - tileSize, - image->width(), - image->height()); - - TileObject toAdd(obj, cr); - - if( map->isInTheMap(currTile) && // within map - cr.x() + cr.width() > 0 && // image has data on this tile - cr.y() + cr.height() > 0) - { - ttiles[index(currTile)].push_back(toAdd); - } + objects.erase(iter); + break; } } } - for(auto & tt : ttiles) + tilesCache.erase(object); + return result; +} + +std::set MapHandler::addObject(const CGObjectInstance * object) +{ + auto image = getObjectImage(object); + if(!image) + return std::set{}; + + for(int fx = 0; fx < object->getWidth(); ++fx) { - stable_sort(tt.begin(), tt.end(), objectBlitOrderSorter); + for(int fy = 0; fy < object->getHeight(); ++fy) + { + int3 currTile(object->pos.x - fx, object->pos.y - fy, object->pos.z); + QRect cr(image->width() - fx * tileSize - tileSize, + image->height() - fy * tileSize - tileSize, + tileSize, + tileSize); + + if( map->isInTheMap(currTile) && // within map + cr.x() + cr.width() > 0 && // image has data on this tile + cr.y() + cr.height() > 0) + { + getObjects(currTile).emplace_back(object, cr); + tilesCache[object].insert(currTile); + } + } } + + return tilesCache[object]; +} + +void MapHandler::initObjectRects() +{ + tileObjects.clear(); + tilesCache.clear(); + if(!map) + return; + + tileObjects.resize(map->width * map->height * (map->twoLevel ? 2 : 1)); + + //initializing objects / rects + for(const CGObjectInstance * elem : map->objects) + { + addObject(elem); + } + + for(auto & tt : tileObjects) + stable_sort(tt.begin(), tt.end(), objectBlitOrderSorter); } bool MapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b) @@ -265,13 +292,13 @@ bool MapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObje return false; } -TileObject::TileObject(CGObjectInstance * obj_, QRect rect_) +ObjectRect::ObjectRect(const CGObjectInstance * obj_, QRect rect_) : obj(obj_), rect(rect_) { } -TileObject::~TileObject() +ObjectRect::~ObjectRect() { } @@ -295,31 +322,36 @@ std::shared_ptr MapHandler::findFlagBitmapInternal(std::shared_ptrgetImage((anim / 4) % groupSize, group); } -MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance * obj, int anim, int group) const +MapHandler::BitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance * obj, int anim, int group) const { if(!obj) - return MapHandler::AnimBitmapHolder(); + return MapHandler::BitmapHolder(); // normal object std::shared_ptr animation = graphics->getAnimation(obj); size_t groupSize = animation->size(group); if(groupSize == 0) - return MapHandler::AnimBitmapHolder(); + return MapHandler::BitmapHolder(); animation->playerColored(obj->tempOwner); auto bitmap = animation->getImage(anim % groupSize, group); if(!bitmap) - return MapHandler::AnimBitmapHolder(); + return MapHandler::BitmapHolder(); setPlayerColor(bitmap.get(), obj->tempOwner); - return MapHandler::AnimBitmapHolder(bitmap); + return MapHandler::BitmapHolder(bitmap); } -std::vector & MapHandler::getObjects(int x, int y, int z) +std::vector & MapHandler::getObjects(const int3 & tile) { - return ttiles[index(x, y, z)]; + return tileObjects[index(tile)]; +} + +std::vector & MapHandler::getObjects(int x, int y, int z) +{ + return tileObjects[index(x, y, z)]; } void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std::set & locked) @@ -364,36 +396,6 @@ void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std: } } -void MapHandler::drawObject(QPainter & painter, const TileObject & object) -{ - const CGObjectInstance * obj = object.obj; - if (!obj) - { - logGlobal->error("Stray map object that isn't fading"); - return; - } - - uint8_t animationFrame = 0; - - auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0); - if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer()) - objData.flagBitmap = findFlagBitmap(dynamic_cast(obj), 0, obj->tempOwner, 0); - - if (objData.objBitmap) - { - auto pos = obj->getPosition(); - - painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.objBitmap); - - if (objData.flagBitmap) - { - if(object.rect.x() == pos.x && object.rect.y() == pos.y) - painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.flagBitmap); - } - } -} - - void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, int x, int y) { if (!obj) @@ -410,10 +412,10 @@ void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, if (objData.objBitmap) { - painter.drawImage(QPoint((x + 1) * 32 - objData.objBitmap->width(), (y + 1) * 32 - objData.objBitmap->height()), *objData.objBitmap); + painter.drawImage(QPoint((x + 1) * tileSize - objData.objBitmap->width(), (y + 1) * tileSize - objData.objBitmap->height()), *objData.objBitmap); if (objData.flagBitmap) - painter.drawImage(QPoint((x + 1) * 32 - objData.objBitmap->width(), (y + 1) * 32 - objData.objBitmap->height()), *objData.flagBitmap); + painter.drawImage(QPoint((x + 1) * tileSize - objData.objBitmap->width(), (y + 1) * tileSize - objData.objBitmap->height()), *objData.flagBitmap); } } @@ -450,107 +452,19 @@ void MapHandler::drawMinimapTile(QPainter & painter, int x, int y, int z) painter.drawPoint(x, y); } -void MapHandler::invalidate(int x, int y, int z) +std::set MapHandler::invalidate(const CGObjectInstance * obj) { - auto & objects = getObjects(x, y, z); + auto t1 = removeObject(obj); + auto t2 = addObject(obj); + t1.insert(t2.begin(), t2.end()); - for(auto obj = objects.begin(); obj != objects.end();) - { - //object was removed - if(std::find(map->objects.begin(), map->objects.end(), obj->obj) == map->objects.end()) - { - obj = objects.erase(obj); - continue; - } - - //object was moved - auto & pos = obj->obj->pos; - if(pos.z != z || pos.x < x || pos.y < y || pos.x - obj->obj->getWidth() >= x || pos.y - obj->obj->getHeight() >= y) - { - obj = objects.erase(obj); - continue; - } - - ++obj; - } + for(auto & tt : t2) + stable_sort(tileObjects[index(tt)].begin(), tileObjects[index(tt)].end(), objectBlitOrderSorter); - stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter); -} - -void MapHandler::invalidate(CGObjectInstance * obj) -{ - std::shared_ptr animation = graphics->getAnimation(obj); - - //no animation at all or empty animation - if(!animation || animation->size(0) == 0) - return; - - auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0); - if(!image) - return; - - for(int fx=0; fx < obj->getWidth(); ++fx) - { - for(int fy=0; fy < obj->getHeight(); ++fy) - { - //object presented on the tile - int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); - QRect cr(image->width() - fx * tileSize - tileSize, image->height() - fy * tileSize - tileSize, image->width(), image->height()); - - if( map->isInTheMap(currTile) && // within map - cr.x() + cr.width() > 0 && // image has data on this tile - cr.y() + cr.height() > 0) - { - auto & objects = ttiles[index(currTile)]; - bool found = false; - for(auto & o : objects) - { - if(o.obj == obj) - { - o.rect = cr; - found = true; - break; - } - } - if(!found) - objects.emplace_back(obj, cr); - - stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter); - } - } - } -} - -std::vector MapHandler::getTilesUnderObject(CGObjectInstance * obj) const -{ - std::vector result; - for(int fx=0; fx < obj->getWidth(); ++fx) - { - for(int fy=0; fy < obj->getHeight(); ++fy) - { - //object presented on the tile - int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); - if(map->isInTheMap(currTile)) // within map - { - result.push_back(currTile); - } - } - } - return result; + return t1; } void MapHandler::invalidateObjects() { - for(auto obj : map->objects) - { - invalidate(obj); - } -} - -void MapHandler::invalidate(const std::vector & tiles) -{ - for(auto & currTile : tiles) - { - invalidate(currTile.x, currTile.y, currTile.z); - } + initObjectRects(); } diff --git a/mapeditor/maphandler.h b/mapeditor/maphandler.h index 27b0518e8..76c7cfe41 100644 --- a/mapeditor/maphandler.h +++ b/mapeditor/maphandler.h @@ -29,26 +29,26 @@ class PlayerColor; VCMI_LIB_NAMESPACE_END -struct TileObject +struct ObjectRect { - CGObjectInstance *obj; + const CGObjectInstance * obj; QRect rect; - TileObject(CGObjectInstance *obj_, QRect rect_); - ~TileObject(); + ObjectRect(const CGObjectInstance * obj_, QRect rect_); + ~ObjectRect(); }; -using TileObjects = std::vector; //pointers to objects being on this tile with rects to be easier to blit this tile on screen +using TileObjects = std::vector; //pointers to objects being on this tile with rects to be easier to blit this tile on screen class MapHandler { public: - struct AnimBitmapHolder + struct BitmapHolder { std::shared_ptr objBitmap; // main object bitmap std::shared_ptr flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes) - AnimBitmapHolder(std::shared_ptr objBitmap_ = nullptr, std::shared_ptr flagBitmap_ = nullptr) + BitmapHolder(std::shared_ptr objBitmap_ = nullptr, std::shared_ptr flagBitmap_ = nullptr) : objBitmap(objBitmap_), flagBitmap(flagBitmap_) {} @@ -61,7 +61,7 @@ private: std::shared_ptr findFlagBitmapInternal(std::shared_ptr animation, int anim, int group, ui8 dir, bool moving) const; std::shared_ptr findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor color, int group) const; - AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim, int group = 0) const; + BitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim, int group = 0) const; //FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013 typedef std::map> TFlippedAnimations; //[type, rotation] @@ -76,28 +76,22 @@ private: TFlippedAnimations riverAnimations;//[river type, rotation] TFlippedCache riverImages;//[river type, view type, rotation] - std::vector ttiles; //informations about map tiles - int3 sizes; //map size (x = width, y = height, z = number of levels) - const CMap * map; - - enum class EMapCacheType : char - { - TERRAIN, OBJECTS, ROADS, RIVERS, FOW, HEROES, HERO_FLAGS, FRAME, AFTER_LAST - }; + std::vector tileObjects; //informations about map tiles + std::map> tilesCache; //set of tiles beloging to object + + const CMap * map = nullptr; void initObjectRects(); void initTerrainGraphics(); QRgb getTileColor(int x, int y, int z); - - QPolygon lockBitMask; + + std::shared_ptr getObjectImage(const CGObjectInstance * obj); public: MapHandler(); ~MapHandler() = default; void reset(const CMap * Map); - - void updateWater(); void drawTerrainTile(QPainter & painter, int x, int y, int z); /// draws a river segment on current tile @@ -105,17 +99,21 @@ public: /// draws a road segment on current tile void drawRoad(QPainter & painter, int x, int y, int z); - void invalidate(int x, int y, int z); //invalidates all objects in particular tile - void invalidate(CGObjectInstance *); //invalidates object rects - void invalidate(const std::vector &); //invalidates all tiles + std::set invalidate(const CGObjectInstance *); //invalidates object rects void invalidateObjects(); //invalidates all objects on the map - std::vector getTilesUnderObject(CGObjectInstance *) const; + const std::set & getTilesUnderObject(const CGObjectInstance *) const; + + //get objects at position + std::vector & getObjects(const int3 & tile); + std::vector & getObjects(int x, int y, int z); + + //returns set of tiles to draw + std::set removeObject(const CGObjectInstance * object); + std::set addObject(const CGObjectInstance * object); /// draws all objects on current tile (higher-level logic, unlike other draw*** methods) void drawObjects(QPainter & painter, int x, int y, int z, const std::set & locked); - void drawObject(QPainter & painter, const TileObject & object); void drawObjectAt(QPainter & painter, const CGObjectInstance * object, int x, int y); - std::vector & getObjects(int x, int y, int z); void drawMinimapTile(QPainter & painter, int x, int y, int z); diff --git a/mapeditor/scenelayer.cpp b/mapeditor/scenelayer.cpp index 6d49c3f25..33e8997e8 100644 --- a/mapeditor/scenelayer.cpp +++ b/mapeditor/scenelayer.cpp @@ -518,7 +518,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO if(object.obj->visitableAt(x, y)) { - return object.obj; + return const_cast(object.obj); } } @@ -530,7 +530,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO if(object.obj->blockingAt(x, y)) { - return object.obj; + return const_cast(object.obj); } } @@ -542,7 +542,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO if(object.obj->coveringAt(x, y)) { - return object.obj; + return const_cast(object.obj); } } @@ -568,7 +568,7 @@ void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2) { for(auto & o : handler->getObjects(i, j, scene->level)) if(!lockedObjects.count(o.obj)) - selectObject(o.obj, false); //do not inform about each object added + selectObject(const_cast(o.obj), false); //do not inform about each object added } } }