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 8cc63a08f77..c69e55fc4e6 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 8066256437a..0d5a8815276 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 mLastDirectionX; - float mLastDirectionY; + float mMarkerUpdateTimer = 0.f; - 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 | 162 +++++++++++++++++------------- apps/openmw/mwgui/mapwindow.hpp | 7 +- apps/openmw/mwrender/localmap.cpp | 6 ++ apps/openmw/mwrender/localmap.hpp | 3 + 4 files changed, 104 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c69e55fc4e6..1dbe4b2d86d 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) + 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(); } @@ -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,28 +403,47 @@ 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); + } // 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. @@ -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 0d5a8815276..ed070c54070 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 9e934d6f207..dd0268c5301 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 9fd101c45f7..555170e1aa7 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 1dbe4b2d86d..bf4bd7644c4 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)