Skip to content

Commit

Permalink
Use a grid based on computed bounds for interiors
Browse files Browse the repository at this point in the history
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.)
  • Loading branch information
Assumeru committed Nov 17, 2024
1 parent 326544a commit fc3a183
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 74 deletions.
162 changes: 91 additions & 71 deletions apps/openmw/mwgui/mapwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<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 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<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();
}
Expand All @@ -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());
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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)
{
Expand Down
7 changes: 4 additions & 3 deletions apps/openmw/mwgui/mapwindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions apps/openmw/mwrender/localmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions apps/openmw/mwrender/localmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <set>
#include <vector>

#include <MyGUI_Types.h>
#include <osg/BoundingBox>
#include <osg/Quat>
#include <osg/ref_ptr>
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit fc3a183

Please sign in to comment.