From 564a0d7d559622d4d8ff7baebdbf9e4c950eb160 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 9 Jan 2021 18:25:46 +0100 Subject: [PATCH] Don't nuke fog when bounds have changed --- CHANGELOG.md | 1 + apps/openmw/mwrender/localmap.cpp | 126 +++++++++++++++++------------- 2 files changed, 74 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa3572282..3dce266d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ Bug #5441: Enemies can't push a player character when in critical strike stance Bug #5451: Magic projectiles don't disappear with the caster Bug #5452: Autowalk is being included in savegames + Bug #5469: Local map is reset when re-entering certain cells Bug #5472: Mistify mod causes CTD in 0.46 on Mac Bug #5479: NPCs who should be walking around town are standing around without walking Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 401e21ae4..5fa1a0e29 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -65,6 +65,15 @@ namespace return val*val; } + std::pair 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(std::ceil(length.x() / mapSize)); + const int segsY = static_cast(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(std::ceil(length.x() / mMapWorldSize)); - const int segsY = static_cast(std::ceil(length.y() / mMapWorldSize)); + auto segments = divideIntoSegments(mBounds, mMapWorldSize); std::unique_ptr 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> 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(std::ceil(length.x() / mMapWorldSize)); - const int segsY = static_cast(std::ceil(length.y() / mMapWorldSize)); - - int i = 0; - for (int x=0; xgetFog(); - - // 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; } } }