Merge branch 'fix_local_map_update' into 'master'

Update cell local map on different neighbour cells (#7140)

See merge request OpenMW/openmw!3012
simplify_debugging
psi29a 2 years ago
commit 242ac21b38

@ -61,9 +61,6 @@ namespace MWRender
void setDefaults(osg::Camera* camera) override; void setDefaults(osg::Camera* camera) override;
bool isActive() { return mActive; }
void setIsActive(bool active) { mActive = active; }
osg::Node* mSceneRoot; osg::Node* mSceneRoot;
osg::Matrix mProjectionMatrix; osg::Matrix mProjectionMatrix;
osg::Matrix mViewMatrix; osg::Matrix mViewMatrix;
@ -182,29 +179,28 @@ namespace MWRender
void LocalMap::requestMap(const MWWorld::CellStore* cell) void LocalMap::requestMap(const MWWorld::CellStore* cell)
{ {
if (cell->isExterior()) if (!cell->isExterior())
{ {
int cellX = cell->getCell()->getGridX();
int cellY = cell->getCell()->getGridY();
MapSegment& segment = mExteriorSegments[std::make_pair(cellX, cellY)];
if (!segment.needUpdate)
return;
else
{
requestExteriorMap(cell);
segment.needUpdate = false;
}
}
else
requestInteriorMap(cell); requestInteriorMap(cell);
return;
}
int cellX = cell->getCell()->getGridX();
int cellY = cell->getCell()->getGridY();
MapSegment& segment = mExteriorSegments[std::make_pair(cellX, cellY)];
const std::uint8_t neighbourFlags = getExteriorNeighbourFlags(cellX, cellY);
if ((segment.mLastRenderNeighbourFlags & neighbourFlags) == neighbourFlags)
return;
requestExteriorMap(cell, segment);
segment.mLastRenderNeighbourFlags = neighbourFlags;
} }
void LocalMap::addCell(MWWorld::CellStore* cell) void LocalMap::addCell(MWWorld::CellStore* cell)
{ {
if (cell->isExterior()) if (cell->isExterior())
mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())].needUpdate mExteriorSegments.emplace(
= true; std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY()), MapSegment{});
} }
void LocalMap::removeExteriorCell(int x, int y) void LocalMap::removeExteriorCell(int x, int y)
@ -216,7 +212,9 @@ namespace MWRender
{ {
saveFogOfWar(cell); saveFogOfWar(cell);
if (!cell->isExterior()) if (cell->isExterior())
mExteriorSegments.erase({ cell->getCell()->getGridX(), cell->getCell()->getGridY() });
else
mInteriorSegments.clear(); mInteriorSegments.clear();
} }
@ -245,7 +243,7 @@ namespace MWRender
auto it = mLocalMapRTTs.begin(); auto it = mLocalMapRTTs.begin();
while (it != mLocalMapRTTs.end()) while (it != mLocalMapRTTs.end())
{ {
if (!(*it)->isActive()) if (!(*it)->mActive)
{ {
mRoot->removeChild(*it); mRoot->removeChild(*it);
it = mLocalMapRTTs.erase(it); it = mLocalMapRTTs.erase(it);
@ -255,30 +253,27 @@ namespace MWRender
} }
} }
void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell, MapSegment& segment)
{ {
mInterior = false; mInterior = false;
int x = cell->getCell()->getGridX(); const int x = cell->getCell()->getGridX();
int y = cell->getCell()->getGridY(); const int y = cell->getCell()->getGridY();
osg::BoundingSphere bound = mSceneRoot->getBound(); osg::BoundingSphere bound = mSceneRoot->getBound();
float zmin = bound.center().z() - bound.radius(); float zmin = bound.center().z() - bound.radius();
float zmax = bound.center().z() + bound.radius(); float zmax = bound.center().z() + bound.radius();
setupRenderToTexture(cell->getCell()->getGridX(), cell->getCell()->getGridY(), setupRenderToTexture(x, y, x * mMapWorldSize + mMapWorldSize / 2.f, y * mMapWorldSize + mMapWorldSize / 2.f,
x * mMapWorldSize + mMapWorldSize / 2.f, y * mMapWorldSize + mMapWorldSize / 2.f, osg::Vec3d(0, 1, 0), zmin, osg::Vec3d(0, 1, 0), zmin, zmax);
zmax);
MapSegment& segment if (segment.mFogOfWarImage != nullptr)
= mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; return;
if (!segment.mFogOfWarImage)
{ if (cell->getFog())
if (cell->getFog()) segment.loadFogOfWar(cell->getFog()->mFogTextures.back());
segment.loadFogOfWar(cell->getFog()->mFogTextures.back()); else
else segment.initFogOfWar();
segment.initFogOfWar();
}
} }
static osg::Vec2f getNorthVector(const MWWorld::CellStore* cell) static osg::Vec2f getNorthVector(const MWWorld::CellStore* cell)
@ -476,7 +471,8 @@ namespace MWRender
int texU = static_cast<int>((sFogOfWarResolution - 1) * nX); int texU = static_cast<int>((sFogOfWarResolution - 1) * nX);
int texV = static_cast<int>((sFogOfWarResolution - 1) * nY); int texV = static_cast<int>((sFogOfWarResolution - 1) * nY);
uint32_t clr = ((const uint32_t*)segment.mFogOfWarImage->data())[texV * sFogOfWarResolution + texU]; const std::uint32_t clr
= reinterpret_cast<const uint32_t*>(segment.mFogOfWarImage->data())[texV * sFogOfWarResolution + texU];
uint8_t alpha = (clr >> 24); uint8_t alpha = (clr >> 24);
return alpha < 200; return alpha < 200;
} }
@ -544,7 +540,7 @@ namespace MWRender
if (!segment.mFogOfWarImage || !segment.mMapTexture) if (!segment.mFogOfWarImage || !segment.mMapTexture)
continue; continue;
uint32_t* data = (uint32_t*)segment.mFogOfWarImage->data(); std::uint32_t* data = reinterpret_cast<std::uint32_t*>(segment.mFogOfWarImage->data());
bool changed = false; bool changed = false;
for (int texV = 0; texV < sFogOfWarResolution; ++texV) for (int texV = 0; texV < sFogOfWarResolution; ++texV)
{ {
@ -553,10 +549,9 @@ namespace MWRender
float sqrDist = square((texU + mx * (sFogOfWarResolution - 1)) - u * (sFogOfWarResolution - 1)) float sqrDist = square((texU + mx * (sFogOfWarResolution - 1)) - u * (sFogOfWarResolution - 1))
+ square((texV + my * (sFogOfWarResolution - 1)) - v * (sFogOfWarResolution - 1)); + square((texV + my * (sFogOfWarResolution - 1)) - v * (sFogOfWarResolution - 1));
uint32_t clr = *(uint32_t*)data; const std::uint8_t alpha = std::min<std::uint8_t>(
uint8_t alpha = (clr >> 24); *data >> 24, std::clamp(sqrDist / sqrExploreRadius, 0.f, 1.f) * 255);
alpha = std::min(alpha, (uint8_t)(std::clamp(sqrDist / sqrExploreRadius, 0.f, 1.f) * 255)); std::uint32_t val = static_cast<std::uint32_t>(alpha << 24);
uint32_t val = (uint32_t)(alpha << 24);
if (*data != val) if (*data != val)
{ {
*data = val; *data = val;
@ -576,9 +571,23 @@ namespace MWRender
} }
} }
LocalMap::MapSegment::MapSegment() std::uint8_t LocalMap::getExteriorNeighbourFlags(int cellX, int cellY) const
: mHasFogState(false) {
{ constexpr std::tuple<NeighbourCellFlag, int, int> flags[] = {
{ NeighbourCellTopLeft, -1, -1 },
{ NeighbourCellTopCenter, 0, -1 },
{ NeighbourCellTopRight, 1, -1 },
{ NeighbourCellMiddleLeft, -1, 0 },
{ NeighbourCellMiddleRight, 1, 0 },
{ NeighbourCellBottomLeft, -1, 1 },
{ NeighbourCellBottomCenter, 0, 1 },
{ NeighbourCellBottomRight, 1, 1 },
};
std::uint8_t result = 0;
for (const auto& [flag, dx, dy] : flags)
if (mExteriorSegments.contains(std::pair(cellX + dx, cellY + dy)))
result |= flag;
return result;
} }
void LocalMap::MapSegment::createFogOfWarTexture() void LocalMap::MapSegment::createFogOfWarTexture()
@ -768,13 +777,10 @@ namespace MWRender
void CameraLocalUpdateCallback::operator()(LocalMapRenderToTexture* node, osg::NodeVisitor* nv) void CameraLocalUpdateCallback::operator()(LocalMapRenderToTexture* node, osg::NodeVisitor* nv)
{ {
if (!node->isActive()) if (!node->mActive)
node->setNodeMask(0); node->setNodeMask(0);
if (node->isActive()) node->mActive = false;
{
node->setIsActive(false);
}
// Rtt-nodes do not forward update traversal to their cameras so we can traverse safely. // Rtt-nodes do not forward update traversal to their cameras so we can traverse safely.
// Traverse in case there are nested callbacks. // Traverse in case there are nested callbacks.

@ -1,6 +1,7 @@
#ifndef GAME_RENDER_LOCALMAP_H #ifndef GAME_RENDER_LOCALMAP_H
#define GAME_RENDER_LOCALMAP_H #define GAME_RENDER_LOCALMAP_H
#include <cstdint>
#include <map> #include <map>
#include <set> #include <set>
#include <vector> #include <vector>
@ -107,23 +108,30 @@ namespace MWRender
typedef std::set<std::pair<int, int>> Grid; typedef std::set<std::pair<int, int>> Grid;
Grid mCurrentGrid; Grid mCurrentGrid;
struct MapSegment enum NeighbourCellFlag : std::uint8_t
{ {
MapSegment(); NeighbourCellTopLeft = 1,
~MapSegment() = default; NeighbourCellTopCenter = 1 << 1,
NeighbourCellTopRight = 1 << 2,
NeighbourCellMiddleLeft = 1 << 3,
NeighbourCellMiddleRight = 1 << 4,
NeighbourCellBottomLeft = 1 << 5,
NeighbourCellBottomCenter = 1 << 6,
NeighbourCellBottomRight = 1 << 7,
};
struct MapSegment
{
void initFogOfWar(); void initFogOfWar();
void loadFogOfWar(const ESM::FogTexture& fog); void loadFogOfWar(const ESM::FogTexture& fog);
void saveFogOfWar(ESM::FogTexture& fog) const; void saveFogOfWar(ESM::FogTexture& fog) const;
void createFogOfWarTexture(); void createFogOfWarTexture();
std::uint8_t mLastRenderNeighbourFlags = 0;
bool mHasFogState = false;
osg::ref_ptr<osg::Texture2D> mMapTexture; osg::ref_ptr<osg::Texture2D> mMapTexture;
osg::ref_ptr<osg::Texture2D> mFogOfWarTexture; osg::ref_ptr<osg::Texture2D> mFogOfWarTexture;
osg::ref_ptr<osg::Image> mFogOfWarImage; osg::ref_ptr<osg::Image> mFogOfWarImage;
bool needUpdate = true;
bool mHasFogState;
}; };
typedef std::map<std::pair<int, int>, MapSegment> SegmentMap; typedef std::map<std::pair<int, int>, MapSegment> SegmentMap;
@ -143,7 +151,7 @@ namespace MWRender
float mAngle; float mAngle;
const osg::Vec2f rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle); const osg::Vec2f rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle);
void requestExteriorMap(const MWWorld::CellStore* cell); void requestExteriorMap(const MWWorld::CellStore* cell, MapSegment& segment);
void requestInteriorMap(const MWWorld::CellStore* cell); void requestInteriorMap(const MWWorld::CellStore* cell);
void setupRenderToTexture( void setupRenderToTexture(
@ -151,6 +159,8 @@ namespace MWRender
bool mInterior; bool mInterior;
osg::BoundingBox mBounds; osg::BoundingBox mBounds;
std::uint8_t getExteriorNeighbourFlags(int cellX, int cellY) const;
}; };
} }

Loading…
Cancel
Save