|
|
|
@ -65,6 +65,15 @@ namespace
|
|
|
|
|
return val*val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::pair<int, int> divideIntoSegments(const osg::BoundingBox& bounds, float mapSize)
|
|
|
|
|
{
|
|
|
|
|
osg::Vec2f min(bounds.xMin(), bounds.yMin());
|
|
|
|
|
osg::Vec2f max(bounds.xMax(), bounds.yMax());
|
|
|
|
|
osg::Vec2f length = max - min;
|
|
|
|
|
const int segsX = static_cast<int>(std::ceil(length.x() / mapSize));
|
|
|
|
|
const int segsY = static_cast<int>(std::ceil(length.y() / mapSize));
|
|
|
|
|
return {segsX, segsY};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace MWRender
|
|
|
|
@ -127,12 +136,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// FIXME: segmenting code duplicated from requestMap
|
|
|
|
|
osg::Vec2f min(mBounds.xMin(), mBounds.yMin());
|
|
|
|
|
osg::Vec2f max(mBounds.xMax(), mBounds.yMax());
|
|
|
|
|
osg::Vec2f length = max-min;
|
|
|
|
|
const int segsX = static_cast<int>(std::ceil(length.x() / mMapWorldSize));
|
|
|
|
|
const int segsY = static_cast<int>(std::ceil(length.y() / mMapWorldSize));
|
|
|
|
|
auto segments = divideIntoSegments(mBounds, mMapWorldSize);
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<ESM::FogState> fog (new ESM::FogState());
|
|
|
|
|
|
|
|
|
@ -142,11 +146,11 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
|
|
|
|
|
fog->mBounds.mMaxY = mBounds.yMax();
|
|
|
|
|
fog->mNorthMarkerAngle = mAngle;
|
|
|
|
|
|
|
|
|
|
fog->mFogTextures.reserve(segsX*segsY);
|
|
|
|
|
fog->mFogTextures.reserve(segments.first * segments.second);
|
|
|
|
|
|
|
|
|
|
for (int x=0; x<segsX; ++x)
|
|
|
|
|
for (int x = 0; x < segments.first; ++x)
|
|
|
|
|
{
|
|
|
|
|
for (int y=0; y<segsY; ++y)
|
|
|
|
|
for (int y = 0; y < segments.second; ++y)
|
|
|
|
|
{
|
|
|
|
|
const MapSegment& segment = mSegments[std::make_pair(x,y)];
|
|
|
|
|
|
|
|
|
@ -422,56 +426,74 @@ void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell)
|
|
|
|
|
// If there is fog state in the CellStore (e.g. when it came from a savegame) we need to do some checks
|
|
|
|
|
// to see if this state is still valid.
|
|
|
|
|
// Both the cell bounds and the NorthMarker rotation could be changed by the content files or exchanged models.
|
|
|
|
|
// If they changed by too much (for bounds, < padding is considered acceptable) then parts of the interior might not
|
|
|
|
|
// be covered by the map anymore.
|
|
|
|
|
// If they changed by too much then parts of the interior might not be covered by the map anymore.
|
|
|
|
|
// The following code detects this, and discards the CellStore's fog state if it needs to.
|
|
|
|
|
bool cellHasValidFog = false;
|
|
|
|
|
std::vector<std::pair<int, int>> segmentMappings;
|
|
|
|
|
if (cell->getFog())
|
|
|
|
|
{
|
|
|
|
|
ESM::FogState* fog = cell->getFog();
|
|
|
|
|
|
|
|
|
|
osg::Vec3f newMin (fog->mBounds.mMinX, fog->mBounds.mMinY, zMin);
|
|
|
|
|
osg::Vec3f newMax (fog->mBounds.mMaxX, fog->mBounds.mMaxY, zMax);
|
|
|
|
|
|
|
|
|
|
osg::Vec3f minDiff = newMin - mBounds._min;
|
|
|
|
|
osg::Vec3f maxDiff = newMax - mBounds._max;
|
|
|
|
|
|
|
|
|
|
if (std::abs(minDiff.x()) > padding || std::abs(minDiff.y()) > padding
|
|
|
|
|
|| std::abs(maxDiff.x()) > padding || std::abs(maxDiff.y()) > padding
|
|
|
|
|
|| std::abs(mAngle - fog->mNorthMarkerAngle) > osg::DegreesToRadians(5.f))
|
|
|
|
|
if (std::abs(mAngle - fog->mNorthMarkerAngle) < osg::DegreesToRadians(5.f))
|
|
|
|
|
{
|
|
|
|
|
// Nuke it
|
|
|
|
|
cellHasValidFog = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Looks sane, use it
|
|
|
|
|
mBounds = osg::BoundingBox(newMin, newMax);
|
|
|
|
|
// Expand mBounds so the saved textures fit the same grid
|
|
|
|
|
int xOffset = 0;
|
|
|
|
|
int yOffset = 0;
|
|
|
|
|
if(fog->mBounds.mMinX < mBounds.xMin())
|
|
|
|
|
{
|
|
|
|
|
mBounds.xMin() = fog->mBounds.mMinX;
|
|
|
|
|
}
|
|
|
|
|
else if(fog->mBounds.mMinX > mBounds.xMin())
|
|
|
|
|
{
|
|
|
|
|
float diff = fog->mBounds.mMinX - mBounds.xMin();
|
|
|
|
|
xOffset += diff / mMapWorldSize;
|
|
|
|
|
xOffset++;
|
|
|
|
|
mBounds.xMin() = fog->mBounds.mMinX - xOffset * mMapWorldSize;
|
|
|
|
|
}
|
|
|
|
|
if(fog->mBounds.mMinY < mBounds.yMin())
|
|
|
|
|
{
|
|
|
|
|
mBounds.yMin() = fog->mBounds.mMinY;
|
|
|
|
|
}
|
|
|
|
|
else if(fog->mBounds.mMinY > mBounds.yMin())
|
|
|
|
|
{
|
|
|
|
|
float diff = fog->mBounds.mMinY - mBounds.yMin();
|
|
|
|
|
yOffset += diff / mMapWorldSize;
|
|
|
|
|
yOffset++;
|
|
|
|
|
mBounds.yMin() = fog->mBounds.mMinY - yOffset * mMapWorldSize;
|
|
|
|
|
}
|
|
|
|
|
mBounds.xMax() = std::max(mBounds.xMax(), fog->mBounds.mMaxX);
|
|
|
|
|
mBounds.yMax() = std::max(mBounds.yMax(), fog->mBounds.mMaxY);
|
|
|
|
|
|
|
|
|
|
if(xOffset != 0 || yOffset != 0)
|
|
|
|
|
Log(Debug::Warning) << "Warning: expanding fog by " << xOffset << ", " << yOffset;
|
|
|
|
|
|
|
|
|
|
const auto& textures = fog->mFogTextures;
|
|
|
|
|
segmentMappings.reserve(textures.size());
|
|
|
|
|
osg::BoundingBox savedBounds{
|
|
|
|
|
fog->mBounds.mMinX, fog->mBounds.mMinY, 0,
|
|
|
|
|
fog->mBounds.mMaxX, fog->mBounds.mMaxY, 0
|
|
|
|
|
};
|
|
|
|
|
auto segments = divideIntoSegments(savedBounds, mMapWorldSize);
|
|
|
|
|
for (int x = 0; x < segments.first; ++x)
|
|
|
|
|
for (int y = 0; y < segments.second; ++y)
|
|
|
|
|
segmentMappings.emplace_back(std::make_pair(x + xOffset, y + yOffset));
|
|
|
|
|
|
|
|
|
|
mAngle = fog->mNorthMarkerAngle;
|
|
|
|
|
cellHasValidFog = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::Vec2f min(mBounds.xMin(), mBounds.yMin());
|
|
|
|
|
osg::Vec2f max(mBounds.xMax(), mBounds.yMax());
|
|
|
|
|
|
|
|
|
|
osg::Vec2f length = max-min;
|
|
|
|
|
|
|
|
|
|
osg::Vec2f center(bounds.center().x(), bounds.center().y());
|
|
|
|
|
osg::Vec2f center(mBounds.center().x(), mBounds.center().y());
|
|
|
|
|
osg::Quat cameraOrient (mAngle, osg::Vec3d(0,0,-1));
|
|
|
|
|
|
|
|
|
|
// divide into segments
|
|
|
|
|
const int segsX = static_cast<int>(std::ceil(length.x() / mMapWorldSize));
|
|
|
|
|
const int segsY = static_cast<int>(std::ceil(length.y() / mMapWorldSize));
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (int x=0; x<segsX; ++x)
|
|
|
|
|
auto segments = divideIntoSegments(mBounds, mMapWorldSize);
|
|
|
|
|
for (int x = 0; x < segments.first; ++x)
|
|
|
|
|
{
|
|
|
|
|
for (int y=0; y<segsY; ++y)
|
|
|
|
|
for (int y = 0; y < segments.second; ++y)
|
|
|
|
|
{
|
|
|
|
|
osg::Vec2f start = min + osg::Vec2f(mMapWorldSize*x, mMapWorldSize*y);
|
|
|
|
|
osg::Vec2f newcenter = start + osg::Vec2f(mMapWorldSize/2.f, mMapWorldSize/2.f);
|
|
|
|
|
|
|
|
|
|
osg::Quat cameraOrient (mAngle, osg::Vec3d(0,0,-1));
|
|
|
|
|
osg::Vec2f a = newcenter - center;
|
|
|
|
|
osg::Vec3f rotatedCenter = cameraOrient * (osg::Vec3f(a.x(), a.y(), 0));
|
|
|
|
|
|
|
|
|
@ -483,26 +505,24 @@ void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell)
|
|
|
|
|
|
|
|
|
|
setupRenderToTexture(camera, x, y);
|
|
|
|
|
|
|
|
|
|
MapSegment& segment = mSegments[std::make_pair(x,y)];
|
|
|
|
|
auto coords = std::make_pair(x,y);
|
|
|
|
|
MapSegment& segment = mSegments[coords];
|
|
|
|
|
if (!segment.mFogOfWarImage)
|
|
|
|
|
{
|
|
|
|
|
if (!cellHasValidFog)
|
|
|
|
|
segment.initFogOfWar();
|
|
|
|
|
else
|
|
|
|
|
bool loaded = false;
|
|
|
|
|
for(size_t index{}; index < segmentMappings.size(); index++)
|
|
|
|
|
{
|
|
|
|
|
ESM::FogState* fog = cell->getFog();
|
|
|
|
|
|
|
|
|
|
// We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same.
|
|
|
|
|
if (i >= int(fog->mFogTextures.size()))
|
|
|
|
|
if(segmentMappings[index] == coords)
|
|
|
|
|
{
|
|
|
|
|
Log(Debug::Warning) << "Warning: fog texture count mismatch";
|
|
|
|
|
ESM::FogState* fog = cell->getFog();
|
|
|
|
|
segment.loadFogOfWar(fog->mFogTextures[index]);
|
|
|
|
|
loaded = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
segment.loadFogOfWar(fog->mFogTextures[i]);
|
|
|
|
|
}
|
|
|
|
|
if(!loaded)
|
|
|
|
|
segment.initFogOfWar();
|
|
|
|
|
}
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|