1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-04-16 17:42:31 +00:00

Merge branch 'Allow-Zoom-levels-on-the-World-Map' into 'master'

Allow Zoom levels on the World Map

See merge request OpenMW/openmw!275
This commit is contained in:
Petr Mikheev 2021-07-25 20:19:11 +00:00
commit 200ccfab69
11 changed files with 529 additions and 215 deletions

View File

@ -27,6 +27,7 @@
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
Feature #2780: A way to see current OpenMW version in the console Feature #2780: A way to see current OpenMW version in the console
Feature #3616: Allow Zoom levels on the World Map
Feature #5489: MCP: Telekinesis fix for activators Feature #5489: MCP: Telekinesis fix for activators
Feature #5996: Support Lua scripts in OpenMW Feature #5996: Support Lua scripts in OpenMW
Feature #6017: Separate persistent and temporary cell references when saving Feature #6017: Separate persistent and temporary cell references when saving

View File

@ -192,6 +192,7 @@ bool Launcher::AdvancedPage::loadSettings()
if (showOwnedIndex >= 0 && showOwnedIndex <= 3) if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex); showOwnedComboBox->setCurrentIndex(showOwnedIndex);
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI")); scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI"));
} }
@ -352,6 +353,7 @@ void Launcher::AdvancedPage::saveSettings()
if (showOwnedCurrentIndex != Settings::Manager::getInt("show owned", "Game")) if (showOwnedCurrentIndex != Settings::Manager::getInt("show owned", "Game"))
Settings::Manager::setInt("show owned", "Game", showOwnedCurrentIndex); Settings::Manager::setInt("show owned", "Game", showOwnedCurrentIndex);
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
float uiScalingFactor = scalingSpinBox->value(); float uiScalingFactor = scalingSpinBox->value();
if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI")) if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI"))

View File

@ -30,10 +30,13 @@
#include "confirmationdialog.hpp" #include "confirmationdialog.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"
#include <numeric>
namespace namespace
{ {
const int cellSize = Constants::CellSizeInUnits; const int cellSize = Constants::CellSizeInUnits;
constexpr float speed = 1.08; //the zoom speed, it should be greater than 1
enum LocalMapWidgetDepth enum LocalMapWidgetDepth
{ {
@ -84,6 +87,23 @@ namespace
setColour(mHoverColour); setColour(mHoverColour);
} }
}; };
MyGUI::IntRect createRect(const MyGUI::IntPoint& center, int radius)
{
return { center.left - radius, center.top + radius, center.left + radius, center.top - radius };
}
int getLocalViewingDistance()
{
if (!Settings::Manager::getBool("allow zooming", "Map"))
return Constants::CellGridRadius;
if (!Settings::Manager::getBool("distant terrain", "Terrain"))
return Constants::CellGridRadius;
const float localViewingDistanceCoef = Settings::Manager::getFloat("local viewing distance coef", "Map");
const int viewingDistance = Settings::Manager::getInt("viewing distance", "Camera");
const int localViewingDistanceInCells = (viewingDistance * localViewingDistanceCoef) / double(Constants::CellSizeInUnits);
return std::max(Constants::CellGridRadius, localViewingDistanceInCells);
}
} }
namespace MWGui namespace MWGui
@ -167,13 +187,12 @@ namespace MWGui
, mFogOfWarToggled(true) , mFogOfWarToggled(true)
, mFogOfWarEnabled(fogOfWarEnabled) , mFogOfWarEnabled(fogOfWarEnabled)
, mMapWidgetSize(0) , mMapWidgetSize(0)
, mNumCells(0) , mNumCells(1)
, mCellDistance(0) , mCellDistance(0)
, mCustomMarkers(markers) , mCustomMarkers(markers)
, mMarkerUpdateTimer(0.0f) , mMarkerUpdateTimer(0.0f)
, mLastDirectionX(0.0f) , mLastDirectionX(0.0f)
, mLastDirectionY(0.0f) , mLastDirectionY(0.0f)
, mNeedDoorMarkersUpdate(false)
{ {
mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers);
} }
@ -183,12 +202,12 @@ 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) void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance)
{ {
mLocalMap = widget; mLocalMap = widget;
mCompass = compass; mCompass = compass;
mMapWidgetSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map")); mMapWidgetSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map"));
mCellDistance = Constants::CellGridRadius; mCellDistance = cellDistance;
mNumCells = mCellDistance * 2 + 1; mNumCells = mCellDistance * 2 + 1;
mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells); mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells);
@ -234,65 +253,94 @@ namespace MWGui
void LocalMapBase::applyFogOfWar() void LocalMapBase::applyFogOfWar()
{ {
for (int mx=0; mx<mNumCells; ++mx)
{
for (int my=0; my<mNumCells; ++my)
{
MapEntry& entry = mMaps[my + mNumCells*mx];
MyGUI::ImageBox* fog = entry.mFogWidget;
if (!mFogOfWarToggled || !mFogOfWarEnabled) if (!mFogOfWarToggled || !mFogOfWarEnabled)
{ {
fog->setImageTexture(""); for (auto& entry : mMaps)
{
entry.mFogWidget->setImageTexture("");
entry.mFogTexture.reset(); entry.mFogTexture.reset();
continue;
}
} }
} }
redraw(); redraw();
} }
MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) MyGUI::IntPoint LocalMapBase::getPosition(int cellX, int cellY, float nX, float nY) const
{ {
MyGUI::IntPoint widgetPos; // normalized cell coordinates
auto mapWidgetSize = getWidgetSize();
return MyGUI::IntPoint(
std::round(nX * mapWidgetSize + (mCellDistance + (cellX - mCurX)) * mapWidgetSize),
std::round(nY * mapWidgetSize + (mCellDistance - (cellY - mCurY)) * mapWidgetSize)
);
}
MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) const
{
int cellX, cellY;
// normalized cell coordinates // normalized cell coordinates
float nX,nY; float nX,nY;
if (!mInterior) if (!mInterior)
{ {
int cellX, cellY;
MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY); MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY);
nX = (worldX - cellSize * cellX) / cellSize; nX = (worldX - cellSize * cellX) / cellSize;
// Image space is -Y up, cells are Y up // Image space is -Y up, cells are Y up
nY = 1 - (worldY - cellSize * cellY) / cellSize; nY = 1 - (worldY - cellSize * cellY) / cellSize;
float cellDx = static_cast<float>(cellX - mCurX);
float cellDy = static_cast<float>(cellY - mCurY);
markerPos.cellX = cellX;
markerPos.cellY = cellY;
widgetPos = MyGUI::IntPoint(static_cast<int>(nX * mMapWidgetSize + (mCellDistance + cellDx) * mMapWidgetSize),
static_cast<int>(nY * mMapWidgetSize + (mCellDistance - cellDy) * mMapWidgetSize));
} }
else else
{ mLocalMapRender->worldToInteriorMapPosition({ worldX, worldY }, nX, nY, cellX, cellY);
int cellX, cellY;
osg::Vec2f worldPos (worldX, worldY);
mLocalMapRender->worldToInteriorMapPosition(worldPos, nX, nY, cellX, cellY);
markerPos.cellX = cellX; markerPos.cellX = cellX;
markerPos.cellY = cellY; markerPos.cellY = cellY;
// Image space is -Y up, cells are Y up
widgetPos = MyGUI::IntPoint(static_cast<int>(nX * mMapWidgetSize + (mCellDistance + (cellX - mCurX)) * mMapWidgetSize),
static_cast<int>(nY * mMapWidgetSize + (mCellDistance - (cellY - mCurY)) * mMapWidgetSize));
}
markerPos.nX = nX; markerPos.nX = nX;
markerPos.nY = nY; markerPos.nY = nY;
return widgetPos; return getPosition(markerPos.cellX, markerPos.cellY, markerPos.nX, markerPos.nY);
}
MyGUI::IntCoord LocalMapBase::getMarkerCoordinates(float worldX, float worldY, MarkerUserData& markerPos, size_t markerSize) const
{
int halfMarkerSize = markerSize / 2;
auto position = getMarkerPosition(worldX, worldY, markerPos);
return MyGUI::IntCoord(position.left - halfMarkerSize, position.top - halfMarkerSize, markerSize, markerSize);
}
MyGUI::Widget* LocalMapBase::createDoorMarker(const std::string& name, const MyGUI::VectorString& notes, float x, float y) const
{
MarkerUserData data(mLocalMapRender);
data.notes = notes;
data.caption = name;
MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>("MarkerButton",
getMarkerCoordinates(x, y, data, 8), MyGUI::Align::Default);
markerWidget->setNormalColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal}")));
markerWidget->setHoverColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal_over}")));
markerWidget->setDepth(Local_MarkerLayer);
markerWidget->setNeedMouseFocus(true);
// Used by tooltips to not show the tooltip if marker is hidden by fog of war
markerWidget->setUserString("ToolTipType", "MapMarker");
markerWidget->setUserData(data);
return markerWidget;
}
void LocalMapBase::centerView()
{
MyGUI::IntPoint pos = mCompass->getPosition() + MyGUI::IntPoint{ 16, 16 };
MyGUI::IntSize viewsize = mLocalMap->getSize();
MyGUI::IntPoint viewOffset((viewsize.width / 2) - pos.left, (viewsize.height / 2) - pos.top);
mLocalMap->setViewOffset(viewOffset);
}
MyGUI::IntCoord LocalMapBase::getMarkerCoordinates(MyGUI::Widget* widget, size_t markerSize) const
{
MarkerUserData& markerPos(*widget->getUserData<MarkerUserData>());
auto position = getPosition(markerPos.cellX, markerPos.cellY, markerPos.nX, markerPos.nY);
return MyGUI::IntCoord(position.left - markerSize / 2, position.top - markerSize / 2, markerSize, markerSize);
}
std::vector<MyGUI::Widget*>& LocalMapBase::currentDoorMarkersWidgets()
{
return mInterior ? mInteriorDoorMarkerWidgets : mExteriorDoorMarkerWidgets;
} }
void LocalMapBase::updateCustomMarkers() void LocalMapBase::updateCustomMarkers()
@ -317,13 +365,8 @@ namespace MWGui
const ESM::CustomMarker& marker = it->second; const ESM::CustomMarker& marker = it->second;
MarkerUserData markerPos (mLocalMapRender); MarkerUserData markerPos (mLocalMapRender);
MyGUI::IntPoint widgetPos = getMarkerPosition(marker.mWorldX, marker.mWorldY, markerPos);
MyGUI::IntCoord widgetCoord(widgetPos.left - 8,
widgetPos.top - 8,
16, 16);
MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>("CustomMarkerButton", MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>("CustomMarkerButton",
widgetCoord, MyGUI::Align::Default); getMarkerCoordinates(marker.mWorldX, marker.mWorldY, markerPos, 16), MyGUI::Align::Default);
markerWidget->setDepth(Local_MarkerAboveFogLayer); markerWidget->setDepth(Local_MarkerAboveFogLayer);
markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipType", "Layout");
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
@ -346,6 +389,38 @@ namespace MWGui
if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) if (x==mCurX && y==mCurY && mInterior==interior && !mChanged)
return; // don't do anything if we're still in the same cell return; // don't do anything if we're still in the same cell
if (!interior && !(x == mCurX && y == mCurY))
{
const MyGUI::IntRect intersection = {
std::max(x, mCurX) - mCellDistance, std::max(y, mCurY) - mCellDistance,
std::min(x, mCurX) + mCellDistance, std::min(y, mCurY) + mCellDistance
};
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 }) || activeGrid.inside({ coord.first, coord.second }))
{
mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), doors.begin(), doors.end());
doors.clear();
}
else
mExteriorDoorMarkerWidgets.insert(mExteriorDoorMarkerWidgets.end(), doors.begin(), doors.end());
}
for (auto& widget : mDoorMarkersToRecycle)
widget->setVisible(false);
for (auto const& cell : mMaps)
{
if (mHasALastActiveCell && !intersection.inside({ cell.mCellX, cell.mCellY }))
mLocalMapRender->removeExteriorCell(cell.mCellX, cell.mCellY);
}
}
mCurX = x; mCurX = x;
mCurY = y; mCurY = y;
mInterior = interior; mInterior = interior;
@ -366,9 +441,11 @@ namespace MWGui
} }
} }
// Delay the door markers update until scripts have been given a chance to run. for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
// If we don't do this, door markers that should be disabled will still appear on the map. widget->setCoord(getMarkerCoordinates(widget, 8));
mNeedDoorMarkersUpdate = true;
if (!mInterior)
mHasALastActiveCell = true;
updateMagicMarkers(); updateMagicMarkers();
updateCustomMarkers(); updateCustomMarkers();
@ -385,21 +462,26 @@ namespace MWGui
mLocalMap->getParent()->_updateChilds(); mLocalMap->getParent()->_updateChilds();
} }
float LocalMapBase::getWidgetSize() const
{
return mLocalMapZoom * mMapWidgetSize;
}
void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny) void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny)
{ {
MyGUI::IntPoint pos(static_cast<int>(mMapWidgetSize * mCellDistance + nx*mMapWidgetSize - 16), static_cast<int>(mMapWidgetSize * mCellDistance + ny*mMapWidgetSize - 16)); MyGUI::IntPoint pos = getPosition(cellX, cellY, nx, ny) - MyGUI::IntPoint{ 16, 16 };
pos.left += (cellX - mCurX) * mMapWidgetSize;
pos.top -= (cellY - mCurY) * mMapWidgetSize;
if (pos != mCompass->getPosition()) if (pos != mCompass->getPosition())
{ {
notifyPlayerUpdate (); notifyPlayerUpdate ();
mCompass->setPosition(pos); mCompass->setPosition(pos);
MyGUI::IntPoint middle (pos.left+16, pos.top+16); }
MyGUI::IntCoord viewsize = mLocalMap->getCoord(); osg::Vec2f curPos((cellX + nx) * cellSize, (cellY + 1 - ny) * cellSize);
MyGUI::IntPoint viewOffset((viewsize.width / 2) - middle.left, (viewsize.height / 2) - middle.top); if ((curPos - mCurPos).length2() > 0.001)
mLocalMap->setViewOffset(viewOffset); {
mCurPos = curPos;
centerView();
} }
} }
@ -449,28 +531,21 @@ namespace MWGui
{ {
const ESM::Position& worldPos = ptr.getRefData().getPosition(); const ESM::Position& worldPos = ptr.getRefData().getPosition();
MarkerUserData markerPos (mLocalMapRender); MarkerUserData markerPos (mLocalMapRender);
MyGUI::IntPoint widgetPos = getMarkerPosition(worldPos.pos[0], worldPos.pos[1], markerPos);
MyGUI::IntCoord widgetCoord(widgetPos.left - 4,
widgetPos.top - 4,
8, 8);
++counter; ++counter;
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
widgetCoord, MyGUI::Align::Default); getMarkerCoordinates(worldPos.pos[0], worldPos.pos[1], markerPos, 8), MyGUI::Align::Default);
markerWidget->setDepth(Local_MarkerAboveFogLayer); markerWidget->setDepth(Local_MarkerAboveFogLayer);
markerWidget->setImageTexture(markerTexture); markerWidget->setImageTexture(markerTexture);
markerWidget->setImageCoord(MyGUI::IntCoord(0,0,8,8)); markerWidget->setImageCoord(MyGUI::IntCoord(0,0,8,8));
markerWidget->setNeedMouseFocus(false); markerWidget->setNeedMouseFocus(false);
markerWidget->setUserData(markerPos);
mMagicMarkerWidgets.push_back(markerWidget); mMagicMarkerWidgets.push_back(markerWidget);
} }
} }
void LocalMapBase::onFrame(float dt) void LocalMapBase::onFrame(float dt)
{
if (mNeedDoorMarkersUpdate)
{ {
updateDoorMarkers(); updateDoorMarkers();
mNeedDoorMarkersUpdate = false;
}
mMarkerUpdateTimer += dt; mMarkerUpdateTimer += dt;
@ -545,34 +620,32 @@ namespace MWGui
void LocalMapBase::updateDoorMarkers() void LocalMapBase::updateDoorMarkers()
{ {
// clear all previous door markers std::vector<MWBase::World::DoorMarker> doors;
for (MyGUI::Widget* widget : mDoorMarkerWidgets)
MyGUI::Gui::getInstance().destroyWidget(widget);
mDoorMarkerWidgets.clear();
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
// Retrieve the door markers we want to show mDoorMarkersToRecycle.insert(mDoorMarkersToRecycle.end(), mInteriorDoorMarkerWidgets.begin(), mInteriorDoorMarkerWidgets.end());
std::vector<MWBase::World::DoorMarker> doors; mInteriorDoorMarkerWidgets.clear();
if (mInterior) if (mInterior)
{ {
for (MyGUI::Widget* widget : mExteriorDoorMarkerWidgets)
widget->setVisible(false);
MWWorld::CellStore* cell = world->getInterior (mPrefix); MWWorld::CellStore* cell = world->getInterior (mPrefix);
world->getDoorMarkers(cell, doors); world->getDoorMarkers(cell, doors);
} }
else else
{ {
for (int dX=-mCellDistance; dX<=mCellDistance; ++dX) for (MapEntry& entry : mMaps)
{ {
for (int dY=-mCellDistance; dY<=mCellDistance; ++dY) if (!entry.mMapTexture && !widgetCropped(entry.mMapWidget, mLocalMap))
{ world->getDoorMarkers(world->getExterior(entry.mCellX, entry.mCellY), doors);
MWWorld::CellStore* cell = world->getExterior (mCurX+dX, mCurY+dY);
world->getDoorMarkers(cell, doors);
}
} }
if (doors.empty())
return;
} }
// Create a widget for each marker // Create a widget for each marker
int counter = 0;
for (MWBase::World::DoorMarker& marker : doors) for (MWBase::World::DoorMarker& marker : doors)
{ {
std::vector<std::string> destNotes; std::vector<std::string> destNotes;
@ -580,28 +653,33 @@ namespace MWGui
for (CustomMarkerCollection::ContainerType::const_iterator iter = markers.first; iter != markers.second; ++iter) for (CustomMarkerCollection::ContainerType::const_iterator iter = markers.first; iter != markers.second; ++iter)
destNotes.push_back(iter->second.mNote); destNotes.push_back(iter->second.mNote);
MarkerUserData data (mLocalMapRender); MyGUI::Widget* markerWidget = nullptr;
data.notes = destNotes; MarkerUserData* data;
data.caption = marker.name; if (mDoorMarkersToRecycle.empty())
MyGUI::IntPoint widgetPos = getMarkerPosition(marker.x, marker.y, data); {
MyGUI::IntCoord widgetCoord(widgetPos.left - 4, markerWidget = createDoorMarker(marker.name, destNotes, marker.x, marker.y);
widgetPos.top - 4, data = markerWidget->getUserData<MarkerUserData>();
8, 8);
++counter;
MarkerWidget* markerWidget = mLocalMap->createWidget<MarkerWidget>("MarkerButton",
widgetCoord, MyGUI::Align::Default);
markerWidget->setNormalColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal}")));
markerWidget->setHoverColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal_over}")));
markerWidget->setDepth(Local_MarkerLayer);
markerWidget->setNeedMouseFocus(true);
// Used by tooltips to not show the tooltip if marker is hidden by fog of war
markerWidget->setUserString("ToolTipType", "MapMarker");
markerWidget->setUserData(data);
doorMarkerCreated(markerWidget); doorMarkerCreated(markerWidget);
mDoorMarkerWidgets.push_back(markerWidget);
} }
else
{
markerWidget = (MarkerWidget*)mDoorMarkersToRecycle.back();
mDoorMarkersToRecycle.pop_back();
data = markerWidget->getUserData<MarkerUserData>();
data->notes = destNotes;
data->caption = marker.name;
markerWidget->setCoord(getMarkerCoordinates(marker.x, marker.y, *data, 8));
markerWidget->setVisible(true);
}
currentDoorMarkersWidgets().push_back(markerWidget);
if (!mInterior)
mExteriorDoorsByCell[{data->cellX, data->cellY}].push_back(markerWidget);
}
for (auto& widget : mDoorMarkersToRecycle)
widget->setVisible(false);
} }
void LocalMapBase::updateMagicMarkers() void LocalMapBase::updateMagicMarkers()
@ -623,23 +701,46 @@ namespace MWGui
&& (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->mName, mPrefix))) && (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->mName, mPrefix)))
{ {
MarkerUserData markerPos (mLocalMapRender); MarkerUserData markerPos (mLocalMapRender);
MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos);
MyGUI::IntCoord widgetCoord(widgetPos.left - 4,
widgetPos.top - 4,
8, 8);
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
widgetCoord, MyGUI::Align::Default); getMarkerCoordinates(markedPosition.pos[0], markedPosition.pos[1], markerPos, 8), MyGUI::Align::Default);
markerWidget->setDepth(Local_MarkerAboveFogLayer); markerWidget->setDepth(Local_MarkerAboveFogLayer);
markerWidget->setImageTexture("textures\\menu_map_smark.dds"); markerWidget->setImageTexture("textures\\menu_map_smark.dds");
markerWidget->setNeedMouseFocus(false); markerWidget->setNeedMouseFocus(false);
markerWidget->setUserData(markerPos);
mMagicMarkerWidgets.push_back(markerWidget); mMagicMarkerWidgets.push_back(markerWidget);
} }
redraw(); redraw();
} }
// ------------------------------------------------------------------------------------------ void LocalMapBase::updateLocalMap()
{
auto mapWidgetSize = getWidgetSize();
mLocalMap->setCanvasSize(mapWidgetSize * mNumCells, mapWidgetSize * mNumCells);
const auto size = MyGUI::IntSize(std::ceil(mapWidgetSize), std::ceil(mapWidgetSize));
for (auto& entry : mMaps)
{
const auto position = getPosition(entry.mCellX, entry.mCellY, 0, 0);
entry.mMapWidget->setCoord({ position, size });
entry.mFogWidget->setCoord({ position, size });
}
MarkerUserData markerPos(mLocalMapRender);
for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
widget->setCoord(getMarkerCoordinates(widget, 8));
for (MyGUI::Widget* widget : mCustomMarkerWidgets)
{
const auto& marker = *widget->getUserData<ESM::CustomMarker>();
widget->setCoord(getMarkerCoordinates(marker.mWorldX, marker.mWorldY, markerPos, 16));
}
for (MyGUI::Widget* widget : mMagicMarkerWidgets)
widget->setCoord(getMarkerCoordinates(widget, 8));
}
// ------------------------------------------------------------------------------------------
MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue) MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue)
: WindowPinnableBase("openmw_map_window.layout") : WindowPinnableBase("openmw_map_window.layout")
, LocalMapBase(customMarkers, localMapRender) , LocalMapBase(customMarkers, localMapRender)
@ -690,14 +791,16 @@ namespace MWGui
getWidget(mEventBoxGlobal, "EventBoxGlobal"); getWidget(mEventBoxGlobal, "EventBoxGlobal");
mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
mEventBoxGlobal->eventMouseWheel += MyGUI::newDelegate(this, &MapWindow::onMapZoomed);
mEventBoxGlobal->setDepth(Global_ExploreOverlayLayer); mEventBoxGlobal->setDepth(Global_ExploreOverlayLayer);
getWidget(mEventBoxLocal, "EventBoxLocal"); getWidget(mEventBoxLocal, "EventBoxLocal");
mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
mEventBoxLocal->eventMouseWheel += MyGUI::newDelegate(this, &MapWindow::onMapZoomed);
LocalMapBase::init(mLocalMap, mPlayerArrowLocal); LocalMapBase::init(mLocalMap, mPlayerArrowLocal, getLocalViewingDistance());
mGlobalMap->setVisible(mGlobal); mGlobalMap->setVisible(mGlobal);
mLocalMap->setVisible(!mGlobal); mLocalMap->setVisible(!mGlobal);
@ -745,10 +848,11 @@ namespace MWGui
MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition(); MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition();
MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition(); MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition();
int x = int(widgetPos.left/float(mMapWidgetSize))-mCellDistance; auto mapWidgetSize = getWidgetSize();
int y = (int(widgetPos.top/float(mMapWidgetSize))-mCellDistance)*-1; int x = int(widgetPos.left/float(mapWidgetSize))-mCellDistance;
float nX = widgetPos.left/float(mMapWidgetSize) - int(widgetPos.left/float(mMapWidgetSize)); int y = (int(widgetPos.top/float(mapWidgetSize))-mCellDistance)*-1;
float nY = widgetPos.top/float(mMapWidgetSize) - int(widgetPos.top/float(mMapWidgetSize)); float nX = widgetPos.left/float(mapWidgetSize) - int(widgetPos.left/float(mapWidgetSize));
float nY = widgetPos.top/float(mapWidgetSize) - int(widgetPos.top/float(mapWidgetSize));
x += mCurX; x += mCurX;
y += mCurY; y += mCurY;
@ -781,6 +885,106 @@ namespace MWGui
mEditNoteDialog.setText(""); mEditNoteDialog.setText("");
} }
void MapWindow::onMapZoomed(MyGUI::Widget* sender, int rel)
{
const static int localWidgetSize = Settings::Manager::getInt("local map widget size", "Map");
const static int globalCellSize = Settings::Manager::getInt("global map cell size", "Map");
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(globalCellSize) * 4.f) / float(localWidgetSize),
float(mLocalMap->getWidth()) / localMapSizeInUnits,
float(mLocalMap->getHeight()) / localMapSizeInUnits
});
if (mGlobal)
{
const float currentGlobalZoom = mGlobalMapZoom;
const float currentMinGlobalMapZoom = std::min(
float(mGlobalMap->getWidth()) / float(mGlobalMapRender->getWidth()),
float(mGlobalMap->getHeight()) / float(mGlobalMapRender->getHeight())
);
mGlobalMapZoom *= speedDiff;
if (zoomIn && mGlobalMapZoom > 4.f)
{
mGlobalMapZoom = currentGlobalZoom;
mLocalMapZoom = currentMinLocalMapZoom;
onWorldButtonClicked(nullptr);
updateLocalMap();
return; //the zoom in is too big
}
if (zoomOut && mGlobalMapZoom < currentMinGlobalMapZoom)
{
mGlobalMapZoom = currentGlobalZoom;
return; //the zoom out is too big, we have reach the borders of the widget
}
}
else
{
auto const currentLocalZoom = mLocalMapZoom;
mLocalMapZoom *= speedDiff;
if (zoomIn && mLocalMapZoom > 4.0f)
{
mLocalMapZoom = currentLocalZoom;
return; //the zoom in is too big
}
if (zoomOut && mLocalMapZoom < currentMinLocalMapZoom)
{
mLocalMapZoom = currentLocalZoom;
float zoomRatio = 4.f/ mGlobalMapZoom;
mGlobalMapZoom = 4.f;
onWorldButtonClicked(nullptr);
zoomOnCursor(zoomRatio);
return; //the zoom out is too big, we switch to the global map
}
}
zoomOnCursor(speedDiff);
}
void MapWindow::zoomOnCursor(float speedDiff)
{
auto map = mGlobal ? mGlobalMap : mLocalMap;
auto cursor = MyGUI::InputManager::getInstance().getMousePosition() - map->getAbsolutePosition();
auto centerView = map->getViewOffset() - cursor;
mGlobal? updateGlobalMap() : updateLocalMap();
map->setViewOffset(MyGUI::IntPoint(
std::round(centerView.left * speedDiff) + cursor.left,
std::round(centerView.top * speedDiff) + cursor.top
));
}
void MapWindow::updateGlobalMap()
{
resizeGlobalMap();
float x = mCurPos.x(), y = mCurPos.y();
if (mInterior)
{
auto pos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
x = pos.x(), y = pos.y();
}
setGlobalMapPlayerPosition(x, y);
for (auto& [marker, col] : mGlobalMapMarkers)
{
marker.widget->setCoord(createMarkerCoords(marker.position.x(), marker.position.y(), col.size()));
marker.widget->setVisible(marker.widget->getHeight() >= 6);
}
}
void MapWindow::onChangeScrollWindowCoord(MyGUI::Widget* sender) void MapWindow::onChangeScrollWindowCoord(MyGUI::Widget* sender)
{ {
MyGUI::IntCoord currentCoordinates = sender->getCoord(); MyGUI::IntCoord currentCoordinates = sender->getCoord();
@ -804,8 +1008,7 @@ namespace MWGui
void MapWindow::renderGlobalMap() void MapWindow::renderGlobalMap()
{ {
mGlobalMapRender->render(); mGlobalMapRender->render();
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); resizeGlobalMap();
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
} }
MapWindow::~MapWindow() MapWindow::~MapWindow()
@ -818,28 +1021,25 @@ namespace MWGui
setTitle("#{sCell=" + cellName + "}"); setTitle("#{sCell=" + cellName + "}");
} }
void MapWindow::addVisitedLocation(const std::string& name, int x, int y) MyGUI::IntCoord MapWindow::createMarkerCoords(float x, float y, float agregatedWeight) const
{
CellId cell;
cell.first = x;
cell.second = y;
if (mMarkers.insert(cell).second)
{ {
float worldX, worldY; float worldX, worldY;
mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); worldPosToGlobalMapImageSpace((x + 0.5f) * Constants::CellSizeInUnits, (y + 0.5f)* Constants::CellSizeInUnits, worldX, worldY);
int markerSize = 12; const float markerSize = getMarkerSize(agregatedWeight);
int offset = mGlobalMapRender->getCellSize()/2 - markerSize/2; const float halfMarkerSize = markerSize / 2.0f;
MyGUI::IntCoord widgetCoord( return MyGUI::IntCoord(
static_cast<int>(worldX * mGlobalMapRender->getWidth()+offset), static_cast<int>(worldX - halfMarkerSize),
static_cast<int>(worldY * mGlobalMapRender->getHeight() + offset), static_cast<int>(worldY - halfMarkerSize),
markerSize, markerSize); markerSize, markerSize);
}
MyGUI::Widget* MapWindow::createMarker(const std::string& name, float x, float y, float agregatedWeight)
{
MyGUI::Widget* markerWidget = mGlobalMap->createWidget<MyGUI::Widget>("MarkerButton", MyGUI::Widget* markerWidget = mGlobalMap->createWidget<MyGUI::Widget>("MarkerButton",
widgetCoord, MyGUI::Align::Default); createMarkerCoords(x, y, agregatedWeight), MyGUI::Align::Default);
markerWidget->setVisible(markerWidget->getHeight() >= 6.0);
markerWidget->setUserString("Caption_TextOneLine", "#{sCell=" + name + "}"); markerWidget->setUserString("Caption_TextOneLine", "#{sCell=" + name + "}");
setGlobalMapMarkerTooltip(markerWidget, x, y); setGlobalMapMarkerTooltip(markerWidget, x, y);
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
@ -848,8 +1048,46 @@ namespace MWGui
markerWidget->setColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal}"))); markerWidget->setColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal}")));
markerWidget->setDepth(Global_MarkerLayer); markerWidget->setDepth(Global_MarkerLayer);
markerWidget->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); markerWidget->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
markerWidget->eventMouseWheel += MyGUI::newDelegate(this, &MapWindow::onMapZoomed);
markerWidget->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); markerWidget->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
mGlobalMapMarkers[std::make_pair(x,y)] = markerWidget;
return markerWidget;
}
void MapWindow::addVisitedLocation(const std::string& name, int x, int y)
{
CellId cell;
cell.first = x;
cell.second = y;
if (mMarkers.insert(cell).second)
{
MapMarkerType mapMarkerWidget = { osg::Vec2f(x, y), createMarker(name, x, y, 0) };
mGlobalMapMarkers.emplace(mapMarkerWidget, std::vector<MapMarkerType>());
std::string name_ = name.substr(0, name.find(','));
auto& entry = mGlobalMapMarkersByName[name_];
if (!entry.widget)
{
entry = { osg::Vec2f(x, y), entry.widget }; //update the coords
entry.widget = createMarker(name_, entry.position.x(), entry.position.y(), 1);
mGlobalMapMarkers.emplace(entry, std::vector<MapMarkerType>{ entry });
}
else
{
auto it = mGlobalMapMarkers.find(entry);
auto& marker = const_cast<MapMarkerType&>(it->first);
auto& elements = it->second;
elements.emplace_back(mapMarkerWidget);
//we compute the barycenter of the entry elements => it will be the place on the world map for the agregated widget
marker.position = std::accumulate(elements.begin(), elements.end(), osg::Vec2f(0.f, 0.f), [](const auto& left, const auto& right) {
return left + right.position;
}) / float(elements.size());
marker.widget->setCoord(createMarkerCoords(marker.position.x(), marker.position.y(), elements.size()));
marker.widget->setVisible(marker.widget->getHeight() >= 6);
}
} }
} }
@ -891,17 +1129,32 @@ namespace MWGui
} }
} }
float MapWindow::getMarkerSize(size_t agregatedWeight) const
{
float markerSize = 12.f * mGlobalMapZoom;
if (mGlobalMapZoom < 1)
return markerSize * std::sqrt(agregatedWeight); //we want to see agregated object
return agregatedWeight ? 0 : markerSize; //we want to see only original markers (i.e. non agregated)
}
void MapWindow::resizeGlobalMap()
{
mGlobalMap->setCanvasSize(mGlobalMapRender->getWidth() * mGlobalMapZoom, mGlobalMapRender->getHeight() * mGlobalMapZoom);
mGlobalMapImage->setSize(mGlobalMapRender->getWidth() * mGlobalMapZoom, mGlobalMapRender->getHeight() * mGlobalMapZoom);
}
void MapWindow::worldPosToGlobalMapImageSpace(float x, float y, float& imageX, float& imageY) const
{
mGlobalMapRender->worldPosToImageSpace(x, y, imageX, imageY);
imageX *= mGlobalMapZoom, imageY *= mGlobalMapZoom;
}
void MapWindow::updateCustomMarkers() void MapWindow::updateCustomMarkers()
{ {
LocalMapBase::updateCustomMarkers(); LocalMapBase::updateCustomMarkers();
for (auto& widgetPair : mGlobalMapMarkers) for (auto& [widgetPair, ignore]: mGlobalMapMarkers)
{ setGlobalMapMarkerTooltip(widgetPair.widget, widgetPair.position.x(), widgetPair.position.y());
int x = widgetPair.first.first;
int y = widgetPair.first.second;
MyGUI::Widget* markerWidget = widgetPair.second;
setGlobalMapMarkerTooltip(markerWidget, x, y);
}
} }
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
@ -934,9 +1187,6 @@ namespace MWGui
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
"#{sWorld}"); "#{sWorld}");
if (mGlobal)
globalMapUpdatePlayer ();
} }
void MapWindow::onPinToggled() void MapWindow::onPinToggled()
@ -978,19 +1228,21 @@ namespace MWGui
setGlobalMapPlayerDir(mLastDirectionX, mLastDirectionY); setGlobalMapPlayerDir(mLastDirectionX, mLastDirectionY);
} }
void MapWindow::centerView()
{
LocalMapBase::centerView();
// set the view offset so that player is in the center
MyGUI::IntSize viewsize = mGlobalMap->getSize();
MyGUI::IntPoint pos = mPlayerArrowGlobal->getPosition() + MyGUI::IntPoint{ 16,16 };
MyGUI::IntPoint viewoffs(static_cast<int>(viewsize.width * 0.5f - pos.left), static_cast<int>(viewsize.height * 0.5f - pos.top));
mGlobalMap->setViewOffset(viewoffs);
}
void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY) void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY)
{ {
float x, y; float x, y;
mGlobalMapRender->worldPosToImageSpace (worldX, worldY, x, y); worldPosToGlobalMapImageSpace(worldX, worldY, x, y);
x *= mGlobalMapRender->getWidth();
y *= mGlobalMapRender->getHeight();
mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(static_cast<int>(x - 16), static_cast<int>(y - 16))); mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(static_cast<int>(x - 16), static_cast<int>(y - 16)));
// set the view offset so that player is in the center
MyGUI::IntSize viewsize = mGlobalMap->getSize();
MyGUI::IntPoint viewoffs(static_cast<int>(viewsize.width * 0.5f - x), static_cast<int>(viewsize.height *0.5 - y));
mGlobalMap->setViewOffset(viewoffs);
} }
void MapWindow::setGlobalMapPlayerDir(const float x, const float y) void MapWindow::setGlobalMapPlayerDir(const float x, const float y)
@ -1027,8 +1279,9 @@ namespace MWGui
mChanged = true; mChanged = true;
for (auto& widgetPair : mGlobalMapMarkers) for (auto& widgetPair : mGlobalMapMarkers)
MyGUI::Gui::getInstance().destroyWidget(widgetPair.second); MyGUI::Gui::getInstance().destroyWidget(widgetPair.first.widget);
mGlobalMapMarkers.clear(); mGlobalMapMarkers.clear();
mGlobalMapMarkersByName.clear();
} }
void MapWindow::write(ESM::ESMWriter &writer, Loading::Listener& progress) void MapWindow::write(ESM::ESMWriter &writer, Loading::Listener& progress)
@ -1075,12 +1328,14 @@ namespace MWGui
marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
marker->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onCustomMarkerDoubleClicked); marker->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onCustomMarkerDoubleClicked);
marker->eventMouseWheel += MyGUI::newDelegate(this, &MapWindow::onMapZoomed);
} }
void MapWindow::doorMarkerCreated(MyGUI::Widget *marker) void MapWindow::doorMarkerCreated(MyGUI::Widget *marker)
{ {
marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
marker->eventMouseWheel += MyGUI::newDelegate(this, &MapWindow::onMapZoomed);
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------

View File

@ -9,6 +9,7 @@
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/esm/custommarkerstate.hpp> #include <components/esm/custommarkerstate.hpp>
#include <components/misc/constants.hpp>
namespace MWRender namespace MWRender
{ {
@ -72,7 +73,7 @@ namespace MWGui
public: public:
LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true); LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true);
virtual ~LocalMapBase(); virtual ~LocalMapBase();
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass); void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance = Constants::CellGridRadius);
void setCellPrefix(const std::string& prefix); void setCellPrefix(const std::string& prefix);
void setActiveCell(const int x, const int y, bool interior=false); void setActiveCell(const int x, const int y, bool interior=false);
@ -107,9 +108,15 @@ namespace MWGui
}; };
protected: protected:
void updateLocalMap();
float mLocalMapZoom = 1.f;
MWRender::LocalMap* mLocalMapRender; MWRender::LocalMap* mLocalMapRender;
int mCurX, mCurY; int mCurX, mCurY; //the position of the active cell on the global map (in cell coords)
bool mHasALastActiveCell = false;
osg::Vec2f mCurPos; //the position of the player in the world (in cell coords)
bool mInterior; bool mInterior;
MyGUI::ScrollView* mLocalMap; MyGUI::ScrollView* mLocalMap;
MyGUI::ImageBox* mCompass; MyGUI::ImageBox* mCompass;
@ -141,17 +148,27 @@ namespace MWGui
std::vector<MapEntry> mMaps; std::vector<MapEntry> mMaps;
// Keep track of created marker widgets, just to easily remove them later. // Keep track of created marker widgets, just to easily remove them later.
std::vector<MyGUI::Widget*> mDoorMarkerWidgets; std::vector<MyGUI::Widget*> mExteriorDoorMarkerWidgets;
std::map<std::pair<int, int>, std::vector<MyGUI::Widget*>> mExteriorDoorsByCell;
std::vector<MyGUI::Widget*> mInteriorDoorMarkerWidgets;
std::vector<MyGUI::Widget*> mMagicMarkerWidgets; std::vector<MyGUI::Widget*> mMagicMarkerWidgets;
std::vector<MyGUI::Widget*> mCustomMarkerWidgets; std::vector<MyGUI::Widget*> mCustomMarkerWidgets;
std::vector<MyGUI::Widget*> mDoorMarkersToRecycle;
std::vector<MyGUI::Widget*>& currentDoorMarkersWidgets();
virtual void updateCustomMarkers(); virtual void updateCustomMarkers();
void applyFogOfWar(); void applyFogOfWar();
MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerUserData& markerPos); MyGUI::IntPoint getPosition(int cellX, int cellY, float nx, float ny) const;
MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerUserData& markerPos) const;
MyGUI::IntCoord getMarkerCoordinates(float worldX, float worldY, MarkerUserData& markerPos, size_t markerSize) const;
MyGUI::Widget* createDoorMarker(const std::string& name, const MyGUI::VectorString& notes, float x, float y) const;
MyGUI::IntCoord getMarkerCoordinates(MyGUI::Widget* widget, size_t markerSize) const;
virtual void notifyPlayerUpdate() {} virtual void notifyPlayerUpdate() {}
virtual void centerView();
virtual void notifyMapChanged() {} virtual void notifyMapChanged() {}
virtual void customMarkerCreated(MyGUI::Widget* marker) {} virtual void customMarkerCreated(MyGUI::Widget* marker) {}
@ -163,6 +180,7 @@ namespace MWGui
void addDetectionMarkers(int type); void addDetectionMarkers(int type);
void redraw(); void redraw();
float getWidgetSize() const;
float mMarkerUpdateTimer; float mMarkerUpdateTimer;
@ -171,7 +189,6 @@ namespace MWGui
private: private:
void updateDoorMarkers(); void updateDoorMarkers();
bool mNeedDoorMarkersUpdate;
}; };
class EditNoteDialog : public MWGui::WindowModal class EditNoteDialog : public MWGui::WindowModal
@ -244,6 +261,9 @@ namespace MWGui
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onWorldButtonClicked(MyGUI::Widget* _sender); void onWorldButtonClicked(MyGUI::Widget* _sender);
void onMapDoubleClicked(MyGUI::Widget* sender); void onMapDoubleClicked(MyGUI::Widget* sender);
void onMapZoomed(MyGUI::Widget* sender, int rel);
void zoomOnCursor(float speedDiff);
void updateGlobalMap();
void onCustomMarkerDoubleClicked(MyGUI::Widget* sender); void onCustomMarkerDoubleClicked(MyGUI::Widget* sender);
void onNoteEditOk(); void onNoteEditOk();
void onNoteEditDelete(); void onNoteEditDelete();
@ -252,6 +272,12 @@ namespace MWGui
void onChangeScrollWindowCoord(MyGUI::Widget* sender); void onChangeScrollWindowCoord(MyGUI::Widget* sender);
void globalMapUpdatePlayer(); void globalMapUpdatePlayer();
void setGlobalMapMarkerTooltip(MyGUI::Widget* widget, int x, int y); void setGlobalMapMarkerTooltip(MyGUI::Widget* widget, int x, int y);
float getMarkerSize(size_t agregatedWeight) const;
void resizeGlobalMap();
void worldPosToGlobalMapImageSpace(float x, float z, float& imageX, float& imageY) const;
MyGUI::IntCoord createMarkerCoords(float x, float y, float agregatedWeight) const;
MyGUI::Widget* createMarker(const std::string& name, float x, float y, float agregatedWeight);
MyGUI::ScrollView* mGlobalMap; MyGUI::ScrollView* mGlobalMap;
std::unique_ptr<MyGUI::ITexture> mGlobalMapTexture; std::unique_ptr<MyGUI::ITexture> mGlobalMapTexture;
@ -273,9 +299,21 @@ namespace MWGui
MyGUI::Button* mEventBoxGlobal; MyGUI::Button* mEventBoxGlobal;
MyGUI::Button* mEventBoxLocal; MyGUI::Button* mEventBoxLocal;
float mGlobalMapZoom = 1.0f;
MWRender::GlobalMap* mGlobalMapRender; MWRender::GlobalMap* mGlobalMapRender;
std::map<std::pair<int, int>, MyGUI::Widget*> mGlobalMapMarkers; struct MapMarkerType
{
osg::Vec2f position;
MyGUI::Widget* widget = nullptr;
bool operator<(const MapMarkerType& right) const {
return widget < right.widget;
}
};
std::map<std::string, MapMarkerType> mGlobalMapMarkersByName;
std::map<MapMarkerType, std::vector<MapMarkerType>> mGlobalMapMarkers;
EditNoteDialog mEditNoteDialog; EditNoteDialog mEditNoteDialog;
ESM::CustomMarker mEditingMarker; ESM::CustomMarker mEditingMarker;
@ -288,6 +326,7 @@ namespace MWGui
void notifyPlayerUpdate() override; void notifyPlayerUpdate() override;
void centerView() override;
}; };
} }
#endif #endif

View File

@ -270,17 +270,9 @@ namespace MWRender
void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY)
{ {
imageX = float(x / float(Constants::CellSizeInUnits) - mMinX) / (mMaxX - mMinX + 1); imageX = (float(x / float(Constants::CellSizeInUnits) - mMinX) / (mMaxX - mMinX + 1)) * getWidth();
imageY = 1.f-float(z / float(Constants::CellSizeInUnits) - mMinY) / (mMaxY - mMinY + 1); imageY = (1.f-float(z / float(Constants::CellSizeInUnits) - mMinY) / (mMaxY - mMinY + 1)) * getHeight();
}
void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY)
{
imageX = float(x - mMinX) / (mMaxX - mMinX + 1);
// NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is
imageY = 1.f-float(y - mMinY + 1) / (mMaxY - mMinY + 1);
} }
void GlobalMap::requestOverlayTextureUpdate(int x, int y, int width, int height, osg::ref_ptr<osg::Texture2D> texture, bool clear, bool cpuCopy, void GlobalMap::requestOverlayTextureUpdate(int x, int y, int width, int height, osg::ref_ptr<osg::Texture2D> texture, bool clear, bool cpuCopy,

View File

@ -41,12 +41,8 @@ namespace MWRender
int getWidth() const { return mWidth; } int getWidth() const { return mWidth; }
int getHeight() const { return mHeight; } int getHeight() const { return mHeight; }
int getCellSize() const { return mCellSize; }
void worldPosToImageSpace(float x, float z, float& imageX, float& imageY); void worldPosToImageSpace(float x, float z, float& imageX, float& imageY);
void cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY);
void exploreCell (int cellX, int cellY, osg::ref_ptr<osg::Texture2D> localMapTexture); void exploreCell (int cellX, int cellY, osg::ref_ptr<osg::Texture2D> localMapTexture);
/// Clears the overlay /// Clears the overlay

View File

@ -118,14 +118,15 @@ const osg::Vec2f LocalMap::rotatePoint(const osg::Vec2f& point, const osg::Vec2f
void LocalMap::clear() void LocalMap::clear()
{ {
mSegments.clear(); mExteriorSegments.clear();
mInteriorSegments.clear();
} }
void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
{ {
if (!mInterior) if (!mInterior)
{ {
const MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; const MapSegment& segment = mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];
if (segment.mFogOfWarImage && segment.mHasFogState) if (segment.mFogOfWarImage && segment.mHasFogState)
{ {
@ -155,7 +156,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
{ {
for (int y = 0; y < segments.second; ++y) for (int y = 0; y < segments.second; ++y)
{ {
const MapSegment& segment = mSegments[std::make_pair(x,y)]; const MapSegment& segment = mInteriorSegments[std::make_pair(x,y)];
fog->mFogTextures.emplace_back(); fog->mFogTextures.emplace_back();
@ -249,26 +250,10 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int
mRoot->addChild(camera); mRoot->addChild(camera);
mActiveCameras.push_back(camera); mActiveCameras.push_back(camera);
MapSegment& segment = mSegments[std::make_pair(x, y)]; MapSegment& segment = mInterior? mInteriorSegments[std::make_pair(x, y)] : mExteriorSegments[std::make_pair(x, y)];
segment.mMapTexture = texture; segment.mMapTexture = texture;
} }
bool needUpdate(std::set<std::pair<int, int> >& renderedGrid, std::set<std::pair<int, int> >& currentGrid, int cellX, int cellY)
{
// if all the cells of the current grid are contained in the rendered grid then we can keep the old render
for (int dx=-1;dx<2;dx+=1)
{
for (int dy=-1;dy<2;dy+=1)
{
bool haveInRenderedGrid = renderedGrid.find(std::make_pair(cellX+dx,cellY+dy)) != renderedGrid.end();
bool haveInCurrentGrid = currentGrid.find(std::make_pair(cellX+dx,cellY+dy)) != currentGrid.end();
if (haveInCurrentGrid && !haveInRenderedGrid)
return true;
}
}
return false;
}
void LocalMap::requestMap(const MWWorld::CellStore* cell) void LocalMap::requestMap(const MWWorld::CellStore* cell)
{ {
if (cell->isExterior()) if (cell->isExterior())
@ -276,13 +261,13 @@ void LocalMap::requestMap(const MWWorld::CellStore* cell)
int cellX = cell->getCell()->getGridX(); int cellX = cell->getCell()->getGridX();
int cellY = cell->getCell()->getGridY(); int cellY = cell->getCell()->getGridY();
MapSegment& segment = mSegments[std::make_pair(cellX, cellY)]; MapSegment& segment = mExteriorSegments[std::make_pair(cellX, cellY)];
if (!needUpdate(segment.mGrid, mCurrentGrid, cellX, cellY)) if (!segment.needUpdate)
return; return;
else else
{ {
segment.mGrid = mCurrentGrid;
requestExteriorMap(cell); requestExteriorMap(cell);
segment.needUpdate = false;
} }
} }
else else
@ -292,27 +277,27 @@ void LocalMap::requestMap(const MWWorld::CellStore* cell)
void LocalMap::addCell(MWWorld::CellStore *cell) void LocalMap::addCell(MWWorld::CellStore *cell)
{ {
if (cell->isExterior()) if (cell->isExterior())
mCurrentGrid.emplace(cell->getCell()->getGridX(), cell->getCell()->getGridY()); mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())].needUpdate = true;
}
void LocalMap::removeExteriorCell(int x, int y)
{
mExteriorSegments.erase({ x, y });
} }
void LocalMap::removeCell(MWWorld::CellStore *cell) void LocalMap::removeCell(MWWorld::CellStore *cell)
{ {
saveFogOfWar(cell); saveFogOfWar(cell);
if (cell->isExterior()) if (!cell->isExterior())
{ mInteriorSegments.clear();
std::pair<int, int> coords = std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY());
mSegments.erase(coords);
mCurrentGrid.erase(coords);
}
else
mSegments.clear();
} }
osg::ref_ptr<osg::Texture2D> LocalMap::getMapTexture(int x, int y) osg::ref_ptr<osg::Texture2D> LocalMap::getMapTexture(int x, int y)
{ {
SegmentMap::iterator found = mSegments.find(std::make_pair(x, y)); auto& segments(mInterior ? mInteriorSegments : mExteriorSegments);
if (found == mSegments.end()) SegmentMap::iterator found = segments.find(std::make_pair(x, y));
if (found == segments.end())
return osg::ref_ptr<osg::Texture2D>(); return osg::ref_ptr<osg::Texture2D>();
else else
return found->second.mMapTexture; return found->second.mMapTexture;
@ -320,8 +305,9 @@ osg::ref_ptr<osg::Texture2D> LocalMap::getMapTexture(int x, int y)
osg::ref_ptr<osg::Texture2D> LocalMap::getFogOfWarTexture(int x, int y) osg::ref_ptr<osg::Texture2D> LocalMap::getFogOfWarTexture(int x, int y)
{ {
SegmentMap::iterator found = mSegments.find(std::make_pair(x, y)); auto& segments(mInterior ? mInteriorSegments : mExteriorSegments);
if (found == mSegments.end()) SegmentMap::iterator found = segments.find(std::make_pair(x, y));
if (found == segments.end())
return osg::ref_ptr<osg::Texture2D>(); return osg::ref_ptr<osg::Texture2D>();
else else
return found->second.mFogOfWarTexture; return found->second.mFogOfWarTexture;
@ -371,7 +357,7 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)
osg::Vec3d(0,1,0), zmin, zmax); osg::Vec3d(0,1,0), zmin, zmax);
setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY());
MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; MapSegment& segment = mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];
if (!segment.mFogOfWarImage) if (!segment.mFogOfWarImage)
{ {
if (cell->getFog()) if (cell->getFog())
@ -512,7 +498,7 @@ void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell)
setupRenderToTexture(camera, x, y); setupRenderToTexture(camera, x, y);
auto coords = std::make_pair(x,y); auto coords = std::make_pair(x,y);
MapSegment& segment = mSegments[coords]; MapSegment& segment = mInteriorSegments[coords];
if (!segment.mFogOfWarImage) if (!segment.mFogOfWarImage)
{ {
bool loaded = false; bool loaded = false;
@ -558,7 +544,8 @@ osg::Vec2f LocalMap::interiorMapToWorldPosition (float nX, float nY, int x, int
bool LocalMap::isPositionExplored (float nX, float nY, int x, int y) bool LocalMap::isPositionExplored (float nX, float nY, int x, int y)
{ {
const MapSegment& segment = mSegments[std::make_pair(x, y)]; auto& segments(mInterior ? mInteriorSegments : mExteriorSegments);
const MapSegment& segment = segments[std::make_pair(x, y)];
if (!segment.mFogOfWarImage) if (!segment.mFogOfWarImage)
return false; return false;
@ -630,7 +617,8 @@ void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orient
int texX = x + mx; int texX = x + mx;
int texY = y + my*-1; int texY = y + my*-1;
MapSegment& segment = mSegments[std::make_pair(texX, texY)]; auto& segments(mInterior ? mInteriorSegments : mExteriorSegments);
MapSegment& segment = segments[std::make_pair(texX, texY)];
if (!segment.mFogOfWarImage || !segment.mMapTexture) if (!segment.mFogOfWarImage || !segment.mMapTexture)
continue; continue;

View File

@ -50,6 +50,7 @@ namespace MWRender
void requestMap (const MWWorld::CellStore* cell); void requestMap (const MWWorld::CellStore* cell);
void addCell(MWWorld::CellStore* cell); void addCell(MWWorld::CellStore* cell);
void removeExteriorCell(int x, int y);
void removeCell (MWWorld::CellStore* cell); void removeCell (MWWorld::CellStore* cell);
@ -126,13 +127,14 @@ namespace MWRender
osg::ref_ptr<osg::Texture2D> mFogOfWarTexture; osg::ref_ptr<osg::Texture2D> mFogOfWarTexture;
osg::ref_ptr<osg::Image> mFogOfWarImage; osg::ref_ptr<osg::Image> mFogOfWarImage;
Grid mGrid; // the grid that was active at the time of rendering this segment bool needUpdate = true;
bool mHasFogState; bool mHasFogState;
}; };
typedef std::map<std::pair<int, int>, MapSegment> SegmentMap; typedef std::map<std::pair<int, int>, MapSegment> SegmentMap;
SegmentMap mSegments; SegmentMap mExteriorSegments;
SegmentMap mInteriorSegments;
int mMapResolution; int mMapResolution;

View File

@ -103,3 +103,26 @@ and typically require more panning to see all available portions of the map.
This larger size also enables an overall greater level of detail if the local map resolution setting is also increased. This larger size also enables an overall greater level of detail if the local map resolution setting is also increased.
This setting can not be configured except by editing the settings configuration file. This setting can not be configured except by editing the settings configuration file.
allow zooming
-------------
:Type: boolean
:Range: True/False
:Default: False
If this setting is true the user can zoom in/out on local and global map with the mouse wheel.
This setting can be controlled in Advanced tab of the launcher.
local viewing distance coef
---------------------------
:Type: float
:Range: > 0 and <= 1
:Default: 0.5
This setting controls viewing distance on local map. It is the coefficient of the viewing distance viewable on the local map if 'distant terrain' is enabled otherwise you will see the default value (a 3x3 square centered on the player).
If view distance is changed in settings menu during the game, then viewable distance on the local map is not updated.
This setting can not be configured except by editing the settings configuration file.

View File

@ -199,6 +199,12 @@ local map widget size = 512
# If true, map in world mode, otherwise in local mode # If true, map in world mode, otherwise in local mode
global = false global = false
# If true, allow zoom on local and global maps
allow zooming = false
# The local view distance coefficient (1.0 is full view distance if distant terrain is enabled)
local viewing distance coef = 0.5
[GUI] [GUI]
# Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger). # Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger).

View File

@ -1006,6 +1006,16 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="useZoomOnMapCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable zooming on local and global maps.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Can zoom on maps</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="graphicHerbalismCheckBox"> <widget class="QCheckBox" name="graphicHerbalismCheckBox">
<property name="toolTip"> <property name="toolTip">