1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-24 18:39:59 +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);
}
MWGui::LocalMapBase::MapEntry& LocalMapBase::addMapEntry()
{
const int mapWidgetSize = Settings::map().mLocalMapWidgetSize;
MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>(
"ImageBox", MyGUI::IntCoord(0, 0, mapWidgetSize, mapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left);
map->setDepth(Local_MapLayer);
MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>(
"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<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(mx * mapWidgetSize, my * mapWidgetSize, mapWidgetSize, mapWidgetSize),
MyGUI::Align::Top | MyGUI::Align::Left);
map->setDepth(Local_MapLayer);
MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>("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<MarkerWidget>("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<MarkerWidget>("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)
{

View File

@ -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;

View File

@ -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)

View File

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