diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 8cc63a08f7..bf4bd7644c 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -102,6 +102,11 @@ namespace return ESM::Cell::generateIdForCell(true, {}, x, y); return cell.getId(); } + + void setCanvasSize(MyGUI::ScrollView* scrollView, const MyGUI::IntRect& grid, int widgetSize) + { + scrollView->setCanvasSize(widgetSize * (grid.width() + 1), widgetSize * (grid.height() + 1)); + } } namespace MWGui @@ -177,18 +182,8 @@ namespace MWGui LocalMapBase::LocalMapBase( CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled) : mLocalMapRender(localMapRender) - , mActiveCell(nullptr) - , mLocalMap(nullptr) - , mCompass(nullptr) - , mFogOfWarToggled(true) , mFogOfWarEnabled(fogOfWarEnabled) - , mNumCells(1) - , mCellDistance(0) , mCustomMarkers(markers) - , mMarkerUpdateTimer(0.0f) - , mLastDirectionX(0.0f) - , mLastDirectionY(0.0f) - , mNeedDoorMarkersUpdate(false) { mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } @@ -198,41 +193,40 @@ namespace MWGui mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } + MWGui::LocalMapBase::MapEntry& LocalMapBase::addMapEntry() + { + const int mapWidgetSize = Settings::map().mLocalMapWidgetSize; + MyGUI::ImageBox* map = mLocalMap->createWidget( + "ImageBox", MyGUI::IntCoord(0, 0, mapWidgetSize, mapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); + map->setDepth(Local_MapLayer); + + MyGUI::ImageBox* fog = mLocalMap->createWidget( + "ImageBox", MyGUI::IntCoord(0, 0, mapWidgetSize, mapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); + fog->setDepth(Local_FogLayer); + fog->setColour(MyGUI::Colour(0, 0, 0)); + + map->setNeedMouseFocus(false); + fog->setNeedMouseFocus(false); + + return mMaps.emplace_back(map, fog); + } + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance) { mLocalMap = widget; mCompass = compass; - mCellDistance = cellDistance; - mNumCells = mCellDistance * 2 + 1; + mGrid = createRect({ 0, 0 }, cellDistance); + mExtCellDistance = cellDistance; const int mapWidgetSize = Settings::map().mLocalMapWidgetSize; - - mLocalMap->setCanvasSize(mapWidgetSize * mNumCells, mapWidgetSize * mNumCells); + setCanvasSize(mLocalMap, mGrid, mapWidgetSize); mCompass->setDepth(Local_CompassLayer); mCompass->setNeedMouseFocus(false); - for (int mx = 0; mx < mNumCells; ++mx) - { - for (int my = 0; my < mNumCells; ++my) - { - MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx * mapWidgetSize, my * mapWidgetSize, mapWidgetSize, mapWidgetSize), - MyGUI::Align::Top | MyGUI::Align::Left); - map->setDepth(Local_MapLayer); - - MyGUI::ImageBox* fog = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx * mapWidgetSize, my * mapWidgetSize, mapWidgetSize, mapWidgetSize), - MyGUI::Align::Top | MyGUI::Align::Left); - fog->setDepth(Local_FogLayer); - fog->setColour(MyGUI::Colour(0, 0, 0)); - - map->setNeedMouseFocus(false); - fog->setNeedMouseFocus(false); - - mMaps.emplace_back(map, fog); - } - } + int numCells = (mGrid.width() + 1) * (mGrid.height() + 1); + for (int i = 0; i < numCells; ++i) + addMapEntry(); } bool LocalMapBase::toggleFogOfWar() @@ -260,8 +254,8 @@ namespace MWGui { // normalized cell coordinates auto mapWidgetSize = getWidgetSize(); - return MyGUI::IntPoint(std::round((nX + mCellDistance + cellX - mActiveCell->getGridX()) * mapWidgetSize), - std::round((nY + mCellDistance - cellY + mActiveCell->getGridY()) * mapWidgetSize)); + return MyGUI::IntPoint(std::round((nX + cellX - mGrid.left) * mapWidgetSize), + std::round((nY - cellY + mGrid.bottom) * mapWidgetSize)); } MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) const @@ -344,35 +338,38 @@ namespace MWGui mCustomMarkerWidgets.clear(); if (!mActiveCell) return; - for (int dX = -mCellDistance; dX <= mCellDistance; ++dX) + auto updateMarkers = [this](CustomMarkerCollection::RangeType markers) { + for (auto it = markers.first; it != markers.second; ++it) + { + const ESM::CustomMarker& marker = it->second; + MarkerUserData markerPos(mLocalMapRender); + MarkerWidget* markerWidget = mLocalMap->createWidget("CustomMarkerButton", + getMarkerCoordinates(marker.mWorldX, marker.mWorldY, markerPos, 16), MyGUI::Align::Default); + markerWidget->setDepth(Local_MarkerAboveFogLayer); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", MyGUI::TextIterator::toTagsString(marker.mNote)); + markerWidget->setNormalColour(MyGUI::Colour(0.6f, 0.6f, 0.6f)); + markerWidget->setHoverColour(MyGUI::Colour(1.0f, 1.0f, 1.0f)); + markerWidget->setUserData(marker); + markerWidget->setNeedMouseFocus(true); + customMarkerCreated(markerWidget); + mCustomMarkerWidgets.push_back(markerWidget); + } + }; + if (mActiveCell->isExterior()) { - for (int dY = -mCellDistance; dY <= mCellDistance; ++dY) + for (int x = mGrid.left; x <= mGrid.right; ++x) { - ESM::RefId cellRefId - = getCellIdInWorldSpace(*mActiveCell, mActiveCell->getGridX() + dX, mActiveCell->getGridY() + dY); - - CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellRefId); - for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; - ++it) + for (int y = mGrid.top; y <= mGrid.bottom; ++y) { - const ESM::CustomMarker& marker = it->second; - - MarkerUserData markerPos(mLocalMapRender); - MarkerWidget* markerWidget = mLocalMap->createWidget("CustomMarkerButton", - getMarkerCoordinates(marker.mWorldX, marker.mWorldY, markerPos, 16), MyGUI::Align::Default); - markerWidget->setDepth(Local_MarkerAboveFogLayer); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", MyGUI::TextIterator::toTagsString(marker.mNote)); - markerWidget->setNormalColour(MyGUI::Colour(0.6f, 0.6f, 0.6f)); - markerWidget->setHoverColour(MyGUI::Colour(1.0f, 1.0f, 1.0f)); - markerWidget->setUserData(marker); - markerWidget->setNeedMouseFocus(true); - customMarkerCreated(markerWidget); - mCustomMarkerWidgets.push_back(markerWidget); + ESM::RefId cellRefId = getCellIdInWorldSpace(*mActiveCell, x, y); + updateMarkers(mCustomMarkers.getMarkers(cellRefId)); } } } + else + updateMarkers(mCustomMarkers.getMarkers(mActiveCell->getId())); redraw(); } @@ -385,15 +382,17 @@ namespace MWGui const int x = cell.getGridX(); const int y = cell.getGridY(); + MyGUI::IntSize oldSize{ mGrid.width(), mGrid.height() }; + if (cell.isExterior()) { + mGrid = createRect({ x, y }, mExtCellDistance); const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius); - const MyGUI::IntRect currentView = createRect({ x, y }, mCellDistance); mExteriorDoorMarkerWidgets.clear(); for (auto& [coord, doors] : mExteriorDoorsByCell) { - if (!mHasALastActiveCell || !currentView.inside({ coord.first, coord.second }) + if (!mHasALastActiveCell || !mGrid.inside({ coord.first, coord.second }) || activeGrid.inside({ coord.first, coord.second })) { mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), doors.begin(), doors.end()); @@ -410,28 +409,50 @@ namespace MWGui { for (const auto& entry : mMaps) { - if (!currentView.inside({ entry.mCellX, entry.mCellY })) + if (!mGrid.inside({ entry.mCellX, entry.mCellY })) mLocalMapRender->removeExteriorCell(entry.mCellX, entry.mCellY); } } } + else + mGrid = mLocalMapRender->getInteriorGrid(); mActiveCell = &cell; - for (int mx = 0; mx < mNumCells; ++mx) + constexpr auto resetEntry = [](MapEntry& entry, bool visible, const MyGUI::IntPoint* position) { + entry.mMapWidget->setVisible(visible); + entry.mFogWidget->setVisible(visible); + if (position) + { + entry.mMapWidget->setPosition(*position); + entry.mFogWidget->setPosition(*position); + } + entry.mMapWidget->setRenderItemTexture(nullptr); + entry.mFogWidget->setRenderItemTexture(nullptr); + entry.mMapTexture.reset(); + entry.mFogTexture.reset(); + }; + + std::size_t usedEntries = 0; + for (int cx = mGrid.left; cx <= mGrid.right; ++cx) { - for (int my = 0; my < mNumCells; ++my) + for (int cy = mGrid.top; cy <= mGrid.bottom; ++cy) { - MapEntry& entry = mMaps[my + mNumCells * mx]; - entry.mMapWidget->setRenderItemTexture(nullptr); - entry.mFogWidget->setRenderItemTexture(nullptr); - entry.mMapTexture.reset(); - entry.mFogTexture.reset(); - - entry.mCellX = x + (mx - mCellDistance); - entry.mCellY = y - (my - mCellDistance); + MapEntry& entry = usedEntries < mMaps.size() ? mMaps[usedEntries] : addMapEntry(); + entry.mCellX = cx; + entry.mCellY = cy; + MyGUI::IntPoint position = getPosition(cx, cy, 0, 0); + resetEntry(entry, true, &position); + ++usedEntries; } } + for (std::size_t i = usedEntries; i < mMaps.size(); ++i) + { + resetEntry(mMaps[i], false, nullptr); + } + + if (oldSize != MyGUI::IntSize{ mGrid.width(), mGrid.height() }) + setCanvasSize(mLocalMap, mGrid, getWidgetSize()); // Delay the door markers update until scripts have been given a chance to run. // If we don't do this, door markers that should be disabled will still appear on the map. @@ -558,15 +579,7 @@ namespace MWGui { MyGUI::IntRect coord = widget->getAbsoluteRect(); MyGUI::IntRect croppedCoord = cropTo->getAbsoluteRect(); - if (coord.left < croppedCoord.left && coord.right < croppedCoord.left) - return true; - if (coord.left > croppedCoord.right && coord.right > croppedCoord.right) - return true; - if (coord.top < croppedCoord.top && coord.bottom < croppedCoord.top) - return true; - if (coord.top > croppedCoord.bottom && coord.bottom > croppedCoord.bottom) - return true; - return false; + return !coord.intersect(croppedCoord); } void LocalMapBase::updateRequiredMaps() @@ -719,7 +732,7 @@ namespace MWGui void LocalMapBase::updateLocalMap() { auto mapWidgetSize = getWidgetSize(); - mLocalMap->setCanvasSize(mapWidgetSize * mNumCells, mapWidgetSize * mNumCells); + setCanvasSize(mLocalMap, mGrid, getWidgetSize()); const auto size = MyGUI::IntSize(std::ceil(mapWidgetSize), std::ceil(mapWidgetSize)); for (auto& entry : mMaps) @@ -864,12 +877,10 @@ namespace MWGui MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition(); auto mapWidgetSize = getWidgetSize(); - int x = int(widgetPos.left / float(mapWidgetSize)) - mCellDistance; - int y = (int(widgetPos.top / float(mapWidgetSize)) - mCellDistance) * -1; + int x = int(widgetPos.left / float(mapWidgetSize)) + mGrid.left; + int y = mGrid.bottom - int(widgetPos.top / float(mapWidgetSize)); float nX = widgetPos.left / float(mapWidgetSize) - int(widgetPos.left / float(mapWidgetSize)); float nY = widgetPos.top / float(mapWidgetSize) - int(widgetPos.top / float(mapWidgetSize)); - x += mActiveCell->getGridX(); - y += mActiveCell->getGridY(); osg::Vec2f worldPos; if (!mActiveCell->isExterior()) @@ -899,11 +910,11 @@ namespace MWGui const bool zoomOut = rel < 0; const bool zoomIn = !zoomOut; const double speedDiff = zoomOut ? 1.0 / speed : speed; - const float localMapSizeInUnits = localWidgetSize * mNumCells; - const float currentMinLocalMapZoom = std::max({ (float(Settings::map().mGlobalMapCellSize) * 4.f) - / float(localWidgetSize), - float(mLocalMap->getWidth()) / localMapSizeInUnits, float(mLocalMap->getHeight()) / localMapSizeInUnits }); + const float currentMinLocalMapZoom + = std::max({ (float(Settings::map().mGlobalMapCellSize) * 4.f) / float(localWidgetSize), + float(mLocalMap->getWidth()) / (localWidgetSize * (mGrid.width() + 1)), + float(mLocalMap->getHeight()) / (localWidgetSize * (mGrid.height() + 1)) }); if (Settings::map().mGlobal) { diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 8066256437..ed070c5407 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -112,20 +112,17 @@ namespace MWGui protected: void updateLocalMap(); - float mLocalMapZoom = 1.f; MWRender::LocalMap* mLocalMapRender; - - const MWWorld::Cell* mActiveCell; - bool mHasALastActiveCell = false; + const MWWorld::Cell* mActiveCell = nullptr; osg::Vec2f mCurPos; // the position of the player in the world (in cell coords) - MyGUI::ScrollView* mLocalMap; - MyGUI::ImageBox* mCompass; - bool mFogOfWarToggled; + MyGUI::ScrollView* mLocalMap = nullptr; + MyGUI::ImageBox* mCompass = nullptr; + float mLocalMapZoom = 1.f; + bool mHasALastActiveCell = false; + bool mFogOfWarToggled = true; bool mFogOfWarEnabled; - - int mNumCells; // for convenience, mCellDistance * 2 + 1 - int mCellDistance; + bool mNeedDoorMarkersUpdate = false; // Stores markers that were placed by a player. May be shared between multiple map views. CustomMarkerCollection& mCustomMarkers; @@ -185,12 +182,14 @@ namespace MWGui void redraw(); float getWidgetSize() const; - float mMarkerUpdateTimer; + MWGui::LocalMapBase::MapEntry& addMapEntry(); - float mLastDirectionX; - float mLastDirectionY; + MyGUI::IntRect mGrid{ -1, -1, 1, 1 }; + int mExtCellDistance = 0; + float mMarkerUpdateTimer = 0.f; - bool mNeedDoorMarkersUpdate; + float mLastDirectionX = 0.f; + float mLastDirectionY = 0.f; private: void updateDoorMarkers(); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 9e934d6f20..dd0268c530 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -587,6 +587,12 @@ namespace MWRender return result; } + MyGUI::IntRect LocalMap::getInteriorGrid() const + { + auto segments = divideIntoSegments(mBounds, mMapWorldSize); + return { 0, 0, segments.first - 1, segments.second - 1 }; + } + void LocalMap::MapSegment::createFogOfWarTexture() { if (mFogOfWarTexture) diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 9fd101c45f..555170e1aa 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,8 @@ namespace MWRender osg::Group* getRoot(); + MyGUI::IntRect getInteriorGrid() const; + private: osg::ref_ptr mRoot; osg::ref_ptr mSceneRoot;