1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-25 03:40:40 +00:00

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.)
This commit is contained in:
Evil Eye 2024-11-17 21:14:14 +01:00
parent 326544ade5
commit fc3a1833ee
4 changed files with 106 additions and 76 deletions

View File

@ -188,41 +188,41 @@ namespace MWGui
mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers);
} }
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance) MWGui::LocalMapBase::MapEntry& LocalMapBase::addMapEntry()
{ {
mLocalMap = widget;
mCompass = compass;
mCellDistance = cellDistance;
mNumCells = mCellDistance * 2 + 1;
const int mapWidgetSize = Settings::map().mLocalMapWidgetSize; const int mapWidgetSize = Settings::map().mLocalMapWidgetSize;
MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>(
mLocalMap->setCanvasSize(mapWidgetSize * mNumCells, mapWidgetSize * mNumCells); "ImageBox", MyGUI::IntCoord(0, 0, mapWidgetSize, mapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left);
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<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(mx * mapWidgetSize, my * mapWidgetSize, mapWidgetSize, mapWidgetSize),
MyGUI::Align::Top | MyGUI::Align::Left);
map->setDepth(Local_MapLayer); map->setDepth(Local_MapLayer);
MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>(
MyGUI::IntCoord(mx * mapWidgetSize, my * mapWidgetSize, mapWidgetSize, mapWidgetSize), "ImageBox", MyGUI::IntCoord(0, 0, mapWidgetSize, mapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left);
MyGUI::Align::Top | MyGUI::Align::Left);
fog->setDepth(Local_FogLayer); fog->setDepth(Local_FogLayer);
fog->setColour(MyGUI::Colour(0, 0, 0)); fog->setColour(MyGUI::Colour(0, 0, 0));
map->setNeedMouseFocus(false); map->setNeedMouseFocus(false);
fog->setNeedMouseFocus(false); fog->setNeedMouseFocus(false);
mMaps.emplace_back(map, fog); return mMaps.emplace_back(map, fog);
}
} }
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance)
{
mLocalMap = widget;
mCompass = compass;
mGrid = createRect({ 0, 0 }, cellDistance);
mExtCellDistance = cellDistance;
const int mapWidgetSize = Settings::map().mLocalMapWidgetSize;
mLocalMap->setCanvasSize(mapWidgetSize * (mGrid.width() + 1), mapWidgetSize * (mGrid.height() + 1));
mCompass->setDepth(Local_CompassLayer);
mCompass->setNeedMouseFocus(false);
int numCells = (mGrid.width() + 1) * (mGrid.height() + 1);
for (int i = 0; i < numCells; ++i)
addMapEntry();
} }
bool LocalMapBase::toggleFogOfWar() bool LocalMapBase::toggleFogOfWar()
@ -250,8 +250,8 @@ namespace MWGui
{ {
// normalized cell coordinates // normalized cell coordinates
auto mapWidgetSize = getWidgetSize(); auto mapWidgetSize = getWidgetSize();
return MyGUI::IntPoint(std::round((nX + mCellDistance + cellX - mActiveCell->getGridX()) * mapWidgetSize), return MyGUI::IntPoint(std::round((nX + cellX - mGrid.left) * mapWidgetSize),
std::round((nY + mCellDistance - cellY + mActiveCell->getGridY()) * mapWidgetSize)); std::round((nY - cellY + mGrid.bottom) * mapWidgetSize));
} }
MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) const MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) const
@ -334,19 +334,10 @@ namespace MWGui
mCustomMarkerWidgets.clear(); mCustomMarkerWidgets.clear();
if (!mActiveCell) if (!mActiveCell)
return; return;
for (int dX = -mCellDistance; dX <= mCellDistance; ++dX) auto updateMarkers = [this](CustomMarkerCollection::RangeType markers) {
{ for (auto it = markers.first; it != markers.second; ++it)
for (int dY = -mCellDistance; dY <= mCellDistance; ++dY)
{
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; const ESM::CustomMarker& marker = it->second;
MarkerUserData markerPos(mLocalMapRender); MarkerUserData markerPos(mLocalMapRender);
MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>("CustomMarkerButton", MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>("CustomMarkerButton",
getMarkerCoordinates(marker.mWorldX, marker.mWorldY, markerPos, 16), MyGUI::Align::Default); getMarkerCoordinates(marker.mWorldX, marker.mWorldY, markerPos, 16), MyGUI::Align::Default);
@ -361,8 +352,20 @@ namespace MWGui
customMarkerCreated(markerWidget); customMarkerCreated(markerWidget);
mCustomMarkerWidgets.push_back(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)
{
ESM::RefId cellRefId = getCellIdInWorldSpace(*mActiveCell, x, y);
updateMarkers(mCustomMarkers.getMarkers(cellRefId));
} }
} }
}
else
updateMarkers(mCustomMarkers.getMarkers(mActiveCell->getId()));
redraw(); redraw();
} }
@ -377,13 +380,13 @@ namespace MWGui
if (cell.isExterior()) if (cell.isExterior())
{ {
mGrid = createRect({ x, y }, mExtCellDistance);
const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius); const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius);
const MyGUI::IntRect currentView = createRect({ x, y }, mCellDistance);
mExteriorDoorMarkerWidgets.clear(); mExteriorDoorMarkerWidgets.clear();
for (auto& [coord, doors] : mExteriorDoorsByCell) 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 })) || activeGrid.inside({ coord.first, coord.second }))
{ {
mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), doors.begin(), doors.end()); mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), doors.begin(), doors.end());
@ -400,28 +403,47 @@ namespace MWGui
{ {
for (const auto& entry : mMaps) 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); mLocalMapRender->removeExteriorCell(entry.mCellX, entry.mCellY);
} }
} }
} }
else
mGrid = mLocalMapRender->getInteriorGrid();
mActiveCell = &cell; 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)
{ {
for (int my = 0; my < mNumCells; ++my) entry.mMapWidget->setPosition(*position);
{ entry.mFogWidget->setPosition(*position);
MapEntry& entry = mMaps[my + mNumCells * mx]; }
entry.mMapWidget->setRenderItemTexture(nullptr); entry.mMapWidget->setRenderItemTexture(nullptr);
entry.mFogWidget->setRenderItemTexture(nullptr); entry.mFogWidget->setRenderItemTexture(nullptr);
entry.mMapTexture.reset(); entry.mMapTexture.reset();
entry.mFogTexture.reset(); entry.mFogTexture.reset();
};
entry.mCellX = x + (mx - mCellDistance); std::size_t usedEntries = 0;
entry.mCellY = y - (my - mCellDistance); 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. // 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. // 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() void LocalMapBase::updateLocalMap()
{ {
auto mapWidgetSize = getWidgetSize(); 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)); const auto size = MyGUI::IntSize(std::ceil(mapWidgetSize), std::ceil(mapWidgetSize));
for (auto& entry : mMaps) for (auto& entry : mMaps)
@ -854,12 +876,10 @@ namespace MWGui
MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition(); MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition();
auto mapWidgetSize = getWidgetSize(); auto mapWidgetSize = getWidgetSize();
int x = int(widgetPos.left / float(mapWidgetSize)) - mCellDistance; int x = int(widgetPos.left / float(mapWidgetSize)) + mGrid.left;
int y = (int(widgetPos.top / float(mapWidgetSize)) - mCellDistance) * -1; int y = mGrid.bottom - int(widgetPos.top / float(mapWidgetSize));
float nX = widgetPos.left / float(mapWidgetSize) - int(widgetPos.left / float(mapWidgetSize)); float nX = widgetPos.left / float(mapWidgetSize) - int(widgetPos.left / float(mapWidgetSize));
float nY = widgetPos.top / float(mapWidgetSize) - int(widgetPos.top / float(mapWidgetSize)); float nY = widgetPos.top / float(mapWidgetSize) - int(widgetPos.top / float(mapWidgetSize));
x += mActiveCell->getGridX();
y += mActiveCell->getGridY();
osg::Vec2f worldPos; osg::Vec2f worldPos;
if (!mActiveCell->isExterior()) if (!mActiveCell->isExterior())
@ -889,11 +909,11 @@ namespace MWGui
const bool zoomOut = rel < 0; const bool zoomOut = rel < 0;
const bool zoomIn = !zoomOut; const bool zoomIn = !zoomOut;
const double speedDiff = zoomOut ? 1.0 / speed : speed; const double speedDiff = zoomOut ? 1.0 / speed : speed;
const float localMapSizeInUnits = localWidgetSize * mNumCells;
const float currentMinLocalMapZoom = std::max({ (float(Settings::map().mGlobalMapCellSize) * 4.f) const float currentMinLocalMapZoom
/ float(localWidgetSize), = std::max({ (float(Settings::map().mGlobalMapCellSize) * 4.f) / float(localWidgetSize),
float(mLocalMap->getWidth()) / localMapSizeInUnits, float(mLocalMap->getHeight()) / localMapSizeInUnits }); float(mLocalMap->getWidth()) / (localWidgetSize * (mGrid.width() + 1)),
float(mLocalMap->getHeight()) / (localWidgetSize * (mGrid.height() + 1)) });
if (Settings::map().mGlobal) if (Settings::map().mGlobal)
{ {

View File

@ -124,9 +124,6 @@ namespace MWGui
bool mFogOfWarEnabled; bool mFogOfWarEnabled;
bool mNeedDoorMarkersUpdate = false; 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. // Stores markers that were placed by a player. May be shared between multiple map views.
CustomMarkerCollection& mCustomMarkers; CustomMarkerCollection& mCustomMarkers;
@ -185,6 +182,10 @@ namespace MWGui
void redraw(); void redraw();
float getWidgetSize() const; float getWidgetSize() const;
MWGui::LocalMapBase::MapEntry& addMapEntry();
MyGUI::IntRect mGrid{ -1, -1, 1, 1 };
int mExtCellDistance = 0;
float mMarkerUpdateTimer = 0.f; float mMarkerUpdateTimer = 0.f;
float mLastDirectionX = 0.f; float mLastDirectionX = 0.f;

View File

@ -587,6 +587,12 @@ namespace MWRender
return result; 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() void LocalMap::MapSegment::createFogOfWarTexture()
{ {
if (mFogOfWarTexture) if (mFogOfWarTexture)

View File

@ -6,6 +6,7 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include <MyGUI_Types.h>
#include <osg/BoundingBox> #include <osg/BoundingBox>
#include <osg/Quat> #include <osg/Quat>
#include <osg/ref_ptr> #include <osg/ref_ptr>
@ -98,6 +99,8 @@ namespace MWRender
osg::Group* getRoot(); osg::Group* getRoot();
MyGUI::IntRect getInteriorGrid() const;
private: private:
osg::ref_ptr<osg::Group> mRoot; osg::ref_ptr<osg::Group> mRoot;
osg::ref_ptr<osg::Node> mSceneRoot; osg::ref_ptr<osg::Node> mSceneRoot;