Skip to content

Commit

Permalink
Merge branch 'poisoncrank' into 'master'
Browse files Browse the repository at this point in the history
Save interior fog bounds center to preserve rotation when expanding them

Closes #7013

See merge request OpenMW/openmw!4466
  • Loading branch information
psi29a committed Nov 27, 2024
2 parents 1dac165 + 4b93278 commit 7b992f9
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
67 changes: 28 additions & 39 deletions apps/openmw/mwrender/localmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
}
}

Expand Down Expand Up @@ -332,31 +331,28 @@ 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<std::pair<int, int>> 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;
}
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())
Expand All @@ -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())
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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)
Expand All @@ -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());

Expand All @@ -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;
}

Expand Down Expand Up @@ -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()
Expand Down
6 changes: 2 additions & 4 deletions apps/openmw/mwrender/localmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ namespace MWRender
typedef std::vector<osg::ref_ptr<LocalMapRenderToTexture>> RTTVector;
RTTVector mLocalMapRTTs;

typedef std::set<std::pair<int, int>> Grid;
Grid mCurrentGrid;

enum NeighbourCellFlag : std::uint8_t
{
NeighbourCellTopLeft = 1,
Expand Down Expand Up @@ -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;
};
Expand Down
17 changes: 13 additions & 4 deletions components/esm3/fogstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
{
Expand All @@ -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<FogTexture>::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");
}
}
Expand Down
2 changes: 2 additions & 0 deletions components/esm3/fogstate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ namespace ESM
float mMaxX;
float mMaxY;
} mBounds;
float mCenterX;
float mCenterY;

std::vector<FogTexture> mFogTextures;

Expand Down
2 changes: 1 addition & 1 deletion components/esm3/formatversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 7b992f9

Please sign in to comment.