diff --git a/CHANGELOG.md b/CHANGELOG.md index 0469539963..61ee7387d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ Bug #6992: Crossbow reloading doesn't look the same as in Morrowind Bug #6993: Shooting your last round of ammunition causes the attack animation to cancel Bug #7009: Falling actors teleport to the ground without receiving any damage on cell loading + Bug #7013: Local map rendering in some cells is broken Bug #7034: Misc items defined in one content file are not treated as keys if another content file uses them as such Bug #7040: Incorrect rendering order for Rebirth's Stormfang Bug #7042: Weapon follow animations that immediately follow the hit animations cause multiple hits diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index dd0268c530..f33ef35a52 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -137,6 +137,8 @@ namespace MWRender fog->mBounds.mMinY = mBounds.yMin(); fog->mBounds.mMaxY = mBounds.yMax(); fog->mNorthMarkerAngle = mAngle; + fog->mCenterX = mCenter.x(); + fog->mCenterY = mCenter.y(); fog->mFogTextures.reserve(segments.first * segments.second); @@ -145,15 +147,12 @@ namespace MWRender for (int y = 0; y < segments.second; ++y) { const MapSegment& segment = mInteriorSegments[std::make_pair(x, y)]; - - fog->mFogTextures.emplace_back(); - - // saving even if !segment.mHasFogState so we don't mess up the segmenting - // plus, older openmw versions can't deal with empty images - segment.saveFogOfWar(fog->mFogTextures.back()); - - fog->mFogTextures.back().mX = x; - fog->mFogTextures.back().mY = y; + if (!segment.mHasFogState) + continue; + ESM::FogTexture& texture = fog->mFogTextures.emplace_back(); + segment.saveFogOfWar(texture); + texture.mX = x; + texture.mY = y; } } @@ -332,22 +331,20 @@ namespace MWRender float zMin = mBounds.zMin(); float zMax = mBounds.zMax(); + mCenter = osg::Vec2f(mBounds.center().x(), mBounds.center().y()); // 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 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. - std::vector> segmentMappings; - if (cell->getFog()) + int xOffset = 0; + int yOffset = 0; + if (const ESM::FogState* fog = cell->getFog()) { - ESM::FogState* fog = cell->getFog(); - if (std::abs(mAngle - fog->mNorthMarkerAngle) < osg::DegreesToRadians(5.f)) { // 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; @@ -355,8 +352,7 @@ namespace MWRender else if (fog->mBounds.mMinX > mBounds.xMin()) { float diff = fog->mBounds.mMinX - mBounds.xMin(); - xOffset += diff / mMapWorldSize; - xOffset++; + xOffset = std::ceil(diff / mMapWorldSize); mBounds.xMin() = fog->mBounds.mMinX - xOffset * mMapWorldSize; } if (fog->mBounds.mMinY < mBounds.yMin()) @@ -366,8 +362,7 @@ namespace MWRender else if (fog->mBounds.mMinY > mBounds.yMin()) { float diff = fog->mBounds.mMinY - mBounds.yMin(); - yOffset += diff / mMapWorldSize; - yOffset++; + yOffset = std::ceil(diff / mMapWorldSize); mBounds.yMin() = fog->mBounds.mMinY - yOffset * mMapWorldSize; } if (fog->mBounds.mMaxX > mBounds.xMax()) @@ -378,22 +373,14 @@ namespace MWRender 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; + mCenter.x() = fog->mCenterX; + mCenter.y() = fog->mCenterY; } } osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); - osg::Vec2f center(mBounds.center().x(), mBounds.center().y()); osg::Quat cameraOrient(mAngle, osg::Vec3d(0, 0, -1)); auto segments = divideIntoSegments(mBounds, mMapWorldSize); @@ -404,10 +391,10 @@ namespace MWRender osg::Vec2f start = min + osg::Vec2f(mMapWorldSize * x, mMapWorldSize * y); osg::Vec2f newcenter = start + osg::Vec2f(mMapWorldSize / 2.f, mMapWorldSize / 2.f); - osg::Vec2f a = newcenter - center; + osg::Vec2f a = newcenter - mCenter; osg::Vec3f rotatedCenter = cameraOrient * (osg::Vec3f(a.x(), a.y(), 0)); - osg::Vec2f pos = osg::Vec2f(rotatedCenter.x(), rotatedCenter.y()) + center; + osg::Vec2f pos = osg::Vec2f(rotatedCenter.x(), rotatedCenter.y()) + mCenter; setupRenderToTexture(x, y, pos.x(), pos.y(), osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax); @@ -416,14 +403,16 @@ namespace MWRender if (!segment.mFogOfWarImage) { bool loaded = false; - for (size_t index{}; index < segmentMappings.size(); index++) + if (const ESM::FogState* fog = cell->getFog()) { - if (segmentMappings[index] == coords) + auto match = std::find_if( + fog->mFogTextures.begin(), fog->mFogTextures.end(), [&](const ESM::FogTexture& texture) { + return texture.mX == x - xOffset && texture.mY == y - yOffset; + }); + if (match != fog->mFogTextures.end()) { - ESM::FogState* fog = cell->getFog(); - segment.loadFogOfWar(fog->mFogTextures[index]); + segment.loadFogOfWar(*match); loaded = true; - break; } } if (!loaded) @@ -435,7 +424,7 @@ namespace MWRender void LocalMap::worldToInteriorMapPosition(osg::Vec2f pos, float& nX, float& nY, int& x, int& y) { - pos = rotatePoint(pos, osg::Vec2f(mBounds.center().x(), mBounds.center().y()), mAngle); + pos = rotatePoint(pos, mCenter, mAngle); osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); @@ -451,7 +440,7 @@ namespace MWRender osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); osg::Vec2f pos(mMapWorldSize * (nX + x) + min.x(), mMapWorldSize * (1.0f - nY + y) + min.y()); - pos = rotatePoint(pos, osg::Vec2f(mBounds.center().x(), mBounds.center().y()), -mAngle); + pos = rotatePoint(pos, mCenter, -mAngle); return pos; } @@ -590,7 +579,7 @@ namespace MWRender MyGUI::IntRect LocalMap::getInteriorGrid() const { auto segments = divideIntoSegments(mBounds, mMapWorldSize); - return { 0, 0, segments.first - 1, segments.second - 1 }; + return { -1, -1, segments.first, segments.second }; } void LocalMap::MapSegment::createFogOfWarTexture() diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 555170e1aa..3ba07ff2ee 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -108,9 +108,6 @@ namespace MWRender typedef std::vector> RTTVector; RTTVector mLocalMapRTTs; - typedef std::set> Grid; - Grid mCurrentGrid; - enum NeighbourCellFlag : std::uint8_t { NeighbourCellTopLeft = 1, @@ -160,8 +157,9 @@ namespace MWRender void setupRenderToTexture( int segment_x, int segment_y, float left, float top, const osg::Vec3d& upVector, float zmin, float zmax); - bool mInterior; osg::BoundingBox mBounds; + osg::Vec2f mCenter; + bool mInterior; std::uint8_t getExteriorNeighbourFlags(int cellX, int cellY) const; }; diff --git a/components/esm3/fogstate.cpp b/components/esm3/fogstate.cpp index 2c07438070..194e4ae745 100644 --- a/components/esm3/fogstate.cpp +++ b/components/esm3/fogstate.cpp @@ -61,6 +61,11 @@ namespace ESM if (esm.isNextSub("BOUN")) esm.getHT(mBounds.mMinX, mBounds.mMinY, mBounds.mMaxX, mBounds.mMaxY); esm.getHNOT(mNorthMarkerAngle, "ANGL"); + if (!esm.getHNOT("CNTR", mCenterX, mCenterY)) + { + mCenterX = (mBounds.mMinX + mBounds.mMaxX) / 2; + mCenterY = (mBounds.mMinY + mBounds.mMaxY) / 2; + } const FormatVersion dataFormat = esm.getFormatVersion(); while (esm.isNextSub("FTEX")) { @@ -87,13 +92,17 @@ namespace ESM { esm.writeHNT("BOUN", mBounds); esm.writeHNT("ANGL", mNorthMarkerAngle); + esm.startSubRecord("CNTR"); + esm.writeT(mCenterX); + esm.writeT(mCenterY); + esm.endRecord("CNTR"); } - for (std::vector::const_iterator it = mFogTextures.begin(); it != mFogTextures.end(); ++it) + for (const FogTexture& texture : mFogTextures) { esm.startSubRecord("FTEX"); - esm.writeT(it->mX); - esm.writeT(it->mY); - esm.write(it->mImageData.data(), it->mImageData.size()); + esm.writeT(texture.mX); + esm.writeT(texture.mY); + esm.write(texture.mImageData.data(), texture.mImageData.size()); esm.endRecord("FTEX"); } } diff --git a/components/esm3/fogstate.hpp b/components/esm3/fogstate.hpp index 7348f3cb01..dd6c95fe1f 100644 --- a/components/esm3/fogstate.hpp +++ b/components/esm3/fogstate.hpp @@ -28,6 +28,8 @@ namespace ESM float mMaxX; float mMaxY; } mBounds; + float mCenterX; + float mCenterY; std::vector mFogTextures; diff --git a/components/esm3/formatversion.hpp b/components/esm3/formatversion.hpp index b09a42cae5..32e245d7d1 100644 --- a/components/esm3/formatversion.hpp +++ b/components/esm3/formatversion.hpp @@ -28,7 +28,7 @@ namespace ESM inline constexpr FormatVersion MaxOldCountFormatVersion = 30; inline constexpr FormatVersion MaxActiveSpellTypeVersion = 31; inline constexpr FormatVersion MaxPlayerBeforeCellDataFormatVersion = 32; - inline constexpr FormatVersion CurrentSaveGameFormatVersion = 33; + inline constexpr FormatVersion CurrentSaveGameFormatVersion = 34; inline constexpr FormatVersion MinSupportedSaveGameFormatVersion = 5; inline constexpr FormatVersion OpenMW0_48SaveGameFormatVersion = 21;