From 326544ade5871312f2338bf8da285a39438fce98 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 17 Nov 2024 13:47:55 +0100 Subject: [PATCH 1/3] Pack LocalMapBase members --- apps/openmw/mwgui/mapwindow.cpp | 10 ---------- apps/openmw/mwgui/mapwindow.hpp | 26 ++++++++++++-------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 8cc63a08f7..c69e55fc4e 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -177,18 +177,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); } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 8066256437..0d5a881527 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -112,20 +112,20 @@ 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; + bool mNeedDoorMarkersUpdate = false; - int mNumCells; // for convenience, mCellDistance * 2 + 1 - int mCellDistance; + int mNumCells = 1; // for convenience, mCellDistance * 2 + 1 + int mCellDistance = 0; // Stores markers that were placed by a player. May be shared between multiple map views. CustomMarkerCollection& mCustomMarkers; @@ -185,12 +185,10 @@ namespace MWGui void redraw(); float getWidgetSize() const; - float mMarkerUpdateTimer; + float mMarkerUpdateTimer = 0.f; - float mLastDirectionX; - float mLastDirectionY; - - bool mNeedDoorMarkersUpdate; + float mLastDirectionX = 0.f; + float mLastDirectionY = 0.f; private: void updateDoorMarkers(); From fc3a1833ee4234b9a2631059a54461cd74cc2205 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 17 Nov 2024 21:14:14 +0100 Subject: [PATCH 2/3] Use a grid based on computed bounds for interiors Combine the cell radius (mCellDistance) and diameter (mNumCells) members into an offset IntRect (mGrid.) The grid is centered on the player's current cell in exteriors (with each grid square mapping to a cell.) In interiors, the grid is centered on the cell's computed bounds. The number of squares remains based on view distance in exteriors, but can now stretch to encompass arbitrarily large interiors, mostly preventing the player from walking off the map grid (interiors exceeding their computed bounds during gameplay still cause issues.) --- apps/openmw/mwgui/mapwindow.cpp | 166 +++++++++++++++++------------- apps/openmw/mwgui/mapwindow.hpp | 7 +- apps/openmw/mwrender/localmap.cpp | 6 ++ apps/openmw/mwrender/localmap.hpp | 3 + 4 files changed, 106 insertions(+), 76 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c69e55fc4e..1dbe4b2d86 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -188,41 +188,41 @@ 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); + mLocalMap->setCanvasSize(mapWidgetSize * (mGrid.width() + 1), mapWidgetSize * (mGrid.height() + 1)); 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() @@ -250,8 +250,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 @@ -334,35 +334,38 @@ namespace MWGui mCustomMarkerWidgets.clear(); if (!mActiveCell) return; - for (int dX = -mCellDistance; dX <= mCellDistance; ++dX) - { - for (int dY = -mCellDistance; dY <= mCellDistance; ++dY) + auto updateMarkers = [this](CustomMarkerCollection::RangeType markers) { + for (auto it = markers.first; it != markers.second; ++it) { - 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) + 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 x = mGrid.left; x <= mGrid.right; ++x) + { + 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(); } @@ -377,13 +380,13 @@ namespace MWGui 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()); @@ -400,27 +403,46 @@ 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) - { - for (int my = 0; my < mNumCells; ++my) + constexpr auto resetEntry = [](MapEntry& entry, bool visible, const MyGUI::IntPoint* position) { + entry.mMapWidget->setVisible(visible); + entry.mFogWidget->setVisible(visible); + if (position) { - 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); + 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 cy = mGrid.top; cy <= mGrid.bottom; ++cy) + { + 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); } // Delay the door markers update until scripts have been given a chance to run. @@ -709,7 +731,7 @@ namespace MWGui void LocalMapBase::updateLocalMap() { auto mapWidgetSize = getWidgetSize(); - mLocalMap->setCanvasSize(mapWidgetSize * mNumCells, mapWidgetSize * mNumCells); + mLocalMap->setCanvasSize(mapWidgetSize * (mGrid.width() + 1), mapWidgetSize * (mGrid.height() + 1)); const auto size = MyGUI::IntSize(std::ceil(mapWidgetSize), std::ceil(mapWidgetSize)); for (auto& entry : mMaps) @@ -854,12 +876,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()) @@ -889,11 +909,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 0d5a881527..ed070c5407 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -124,9 +124,6 @@ namespace MWGui bool mFogOfWarEnabled; bool mNeedDoorMarkersUpdate = false; - int mNumCells = 1; // for convenience, mCellDistance * 2 + 1 - int mCellDistance = 0; - // Stores markers that were placed by a player. May be shared between multiple map views. CustomMarkerCollection& mCustomMarkers; @@ -185,6 +182,10 @@ namespace MWGui void redraw(); float getWidgetSize() const; + MWGui::LocalMapBase::MapEntry& addMapEntry(); + + MyGUI::IntRect mGrid{ -1, -1, 1, 1 }; + int mExtCellDistance = 0; float mMarkerUpdateTimer = 0.f; float mLastDirectionX = 0.f; 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; From c31c43bed53803978005762b4670d741b01a13b8 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 18 Nov 2024 17:11:05 +0100 Subject: [PATCH 3/3] Adjust canvas size when switching cells --- apps/openmw/mwgui/mapwindow.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 1dbe4b2d86..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 @@ -214,8 +219,7 @@ namespace MWGui mExtCellDistance = cellDistance; const int mapWidgetSize = Settings::map().mLocalMapWidgetSize; - - mLocalMap->setCanvasSize(mapWidgetSize * (mGrid.width() + 1), mapWidgetSize * (mGrid.height() + 1)); + setCanvasSize(mLocalMap, mGrid, mapWidgetSize); mCompass->setDepth(Local_CompassLayer); mCompass->setNeedMouseFocus(false); @@ -378,6 +382,8 @@ 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); @@ -445,6 +451,9 @@ namespace MWGui 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. mNeedDoorMarkersUpdate = true; @@ -570,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() @@ -731,7 +732,7 @@ namespace MWGui void LocalMapBase::updateLocalMap() { auto mapWidgetSize = getWidgetSize(); - mLocalMap->setCanvasSize(mapWidgetSize * (mGrid.width() + 1), mapWidgetSize * (mGrid.height() + 1)); + setCanvasSize(mLocalMap, mGrid, getWidgetSize()); const auto size = MyGUI::IntSize(std::ceil(mapWidgetSize), std::ceil(mapWidgetSize)); for (auto& entry : mMaps)