From 18d6598565e72bb73a654dd56ed3c44d34f79d76 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 26 Nov 2023 13:11:38 -0600 Subject: [PATCH 01/24] Feat(CS): Add enum for actual LAND flags --- components/esm3/loadland.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/esm3/loadland.hpp b/components/esm3/loadland.hpp index 1e0ec81ecf..5e6f67c521 100644 --- a/components/esm3/loadland.hpp +++ b/components/esm3/loadland.hpp @@ -66,6 +66,13 @@ namespace ESM DATA_VTEX = 16 }; + enum + { + FLAG_HEIGHT = 1, + FLAG_COLOR = 2, + FLAG_TEXTURE = 4 + }; + // default height to use in case there is no Land record static constexpr int DEFAULT_HEIGHT = -2048; From 2541b74fc6d8420c79ea9e4b4047fbfd24bc9277 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 26 Nov 2023 13:12:06 -0600 Subject: [PATCH 02/24] Fix(CS): Apply land flags when appropriate edits are made --- apps/opencs/model/world/columnimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 215e4c3dfc..a3b9f3b5ae 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -202,6 +202,8 @@ namespace CSMWorld copy.getLandData()->mHeights[i] = values[i]; } + copy.mFlags |= Land::FLAG_HEIGHT; + record.setModified(copy); } @@ -249,6 +251,8 @@ namespace CSMWorld copy.getLandData()->mColours[i] = values[i]; } + copy.mFlags |= Land::FLAG_COLOR; + record.setModified(copy); } @@ -296,6 +300,8 @@ namespace CSMWorld copy.getLandData()->mTextures[i] = values[i]; } + copy.mFlags |= Land::FLAG_TEXTURE; + record.setModified(copy); } From 3e7335ddc7a59e9bab730263b3dcce4eb87b0356 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 9 Dec 2023 11:49:38 -0600 Subject: [PATCH 03/24] Fix(CS): Initialize landscape when opening an existing cell without VHGT --- apps/opencs/view/render/cell.cpp | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index f2c851cc72..61a07d94da 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -137,29 +137,29 @@ void CSVRender::Cell::updateLand() { const ESM::Land& esmLand = land.getRecord(mId).get(); - if (esmLand.getLandData(ESM::Land::DATA_VHGT)) + if (!esmLand.getLandData(ESM::Land::DATA_VHGT)) + mTerrainStorage->resetHeights(); + + if (mTerrain) { - if (mTerrain) - { - mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); - mTerrain->clearAssociatedCaches(); - } - else - { - constexpr double expiryDelay = 0; - mTerrain = std::make_unique(mCellNode, mCellNode, mData.getResourceSystem().get(), - mTerrainStorage, Mask_Terrain, ESM::Cell::sDefaultWorldspaceId, expiryDelay); - } - - mTerrain->loadCell(esmLand.mX, esmLand.mY); - - if (!mCellBorder) - mCellBorder = std::make_unique(mCellNode, mCoordinates); - - mCellBorder->buildShape(esmLand); - - return; + mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); + mTerrain->clearAssociatedCaches(); } + else + { + constexpr double expiryDelay = 0; + mTerrain = std::make_unique(mCellNode, mCellNode, mData.getResourceSystem().get(), + mTerrainStorage, Mask_Terrain, ESM::Cell::sDefaultWorldspaceId, expiryDelay); + } + + mTerrain->loadCell(esmLand.mX, esmLand.mY); + + if (!mCellBorder) + mCellBorder = std::make_unique(mCellNode, mCoordinates); + + mCellBorder->buildShape(esmLand); + + return; } // No land data From 014e5118e9aaa16c8a2b83c47b690fa117b295ef Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 9 Dec 2023 11:50:16 -0600 Subject: [PATCH 04/24] Fix(CS): Fix crash when loading cell without VHGT --- components/esmterrain/storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 35ec814aa2..b43b996996 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -346,7 +346,7 @@ namespace ESMTerrain color[i] = colourData->getColors()[srcArrayIndex + i]; // Does nothing by default, override in OpenMW-CS - if (alteration) + if (alteration && heightData != nullptr) adjustColor(col, row, heightData, color); // Unlike normals, colors mostly connect seamlessly between cells, but not always... From 78fd13576485a436fb1cf87e8cb388d4bddced04 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 9 Dec 2023 11:51:25 -0600 Subject: [PATCH 05/24] Fix(CS): Remove unreachable code as land data will always be present --- apps/opencs/view/render/cell.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 61a07d94da..574b88f5c9 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -158,12 +158,7 @@ void CSVRender::Cell::updateLand() mCellBorder = std::make_unique(mCellNode, mCoordinates); mCellBorder->buildShape(esmLand); - - return; } - - // No land data - unloadLand(); } void CSVRender::Cell::unloadLand() From dd7a2f22d1d792c52cf3356a60a2ce1add7bbf1a Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 9 Dec 2023 11:53:18 -0600 Subject: [PATCH 06/24] Cleanup(CSVRender::Cell:updateLand): Early exit when landscape should not be loaded --- apps/opencs/view/render/cell.cpp | 49 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 574b88f5c9..ec8fcc7e2b 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -133,32 +133,33 @@ void CSVRender::Cell::updateLand() // Setup land if available const CSMWorld::IdCollection& land = mData.getLand(); int landIndex = land.searchId(mId); - if (landIndex != -1 && !land.getRecord(mId).isDeleted()) + + if (landIndex == -1 || land.getRecord(mId).isDeleted()) + return; + + const ESM::Land& esmLand = land.getRecord(mId).get(); + + if (!esmLand.getLandData(ESM::Land::DATA_VHGT)) + mTerrainStorage->resetHeights(); + + if (mTerrain) { - const ESM::Land& esmLand = land.getRecord(mId).get(); - - if (!esmLand.getLandData(ESM::Land::DATA_VHGT)) - mTerrainStorage->resetHeights(); - - if (mTerrain) - { - mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); - mTerrain->clearAssociatedCaches(); - } - else - { - constexpr double expiryDelay = 0; - mTerrain = std::make_unique(mCellNode, mCellNode, mData.getResourceSystem().get(), - mTerrainStorage, Mask_Terrain, ESM::Cell::sDefaultWorldspaceId, expiryDelay); - } - - mTerrain->loadCell(esmLand.mX, esmLand.mY); - - if (!mCellBorder) - mCellBorder = std::make_unique(mCellNode, mCoordinates); - - mCellBorder->buildShape(esmLand); + mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); + mTerrain->clearAssociatedCaches(); } + else + { + constexpr double expiryDelay = 0; + mTerrain = std::make_unique(mCellNode, mCellNode, mData.getResourceSystem().get(), + mTerrainStorage, Mask_Terrain, ESM::Cell::sDefaultWorldspaceId, expiryDelay); + } + + mTerrain->loadCell(esmLand.mX, esmLand.mY); + + if (!mCellBorder) + mCellBorder = std::make_unique(mCellNode, mCoordinates); + + mCellBorder->buildShape(esmLand); } void CSVRender::Cell::unloadLand() From f565730a9adbaffaa1e918504414ccc94c546736 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 9 Dec 2023 17:18:57 -0600 Subject: [PATCH 07/24] Fix(cell.cpp): never try to update land for interiors --- apps/opencs/view/render/cell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index ec8fcc7e2b..c0d3583d02 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -118,7 +118,7 @@ bool CSVRender::Cell::addObjects(int start, int end) void CSVRender::Cell::updateLand() { - if (!mUpdateLand || mLandDeleted) + if (!mUpdateLand || mLandDeleted || !mId.startsWith("#")) return; mUpdateLand = false; From b111d28ede3fbd11ca8e77e55da9363317347960 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 10 Dec 2023 08:54:44 -0600 Subject: [PATCH 08/24] Fix(CS): Generate blank lands at default height, not water level --- components/esm3/loadland.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esm3/loadland.cpp b/components/esm3/loadland.cpp index da3cb58344..a69419cd0f 100644 --- a/components/esm3/loadland.cpp +++ b/components/esm3/loadland.cpp @@ -204,9 +204,9 @@ namespace ESM if (mLandData == nullptr) mLandData = std::make_unique(); - mLandData->mHeights.fill(0); - mLandData->mMinHeight = 0; - mLandData->mMaxHeight = 0; + mLandData->mHeights.fill(DEFAULT_HEIGHT); + mLandData->mMinHeight = DEFAULT_HEIGHT; + mLandData->mMaxHeight = DEFAULT_HEIGHT; for (size_t i = 0; i < LandRecordData::sLandNumVerts; ++i) { mLandData->mNormals[i * 3 + 0] = 0; From 731302ecff7cc538212944ad05ee3a880be4cf39 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 10 Dec 2023 11:12:06 -0600 Subject: [PATCH 09/24] Feat(CS): Add undo stack as a member of CSVRender::Cell --- apps/opencs/view/render/cell.cpp | 5 ++++- apps/opencs/view/render/cell.hpp | 6 +++++- apps/opencs/view/render/pagedworldspacewidget.cpp | 7 ++++--- apps/opencs/view/render/unpagedworldspacewidget.cpp | 4 ++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index c0d3583d02..a67923ecf4 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -1,6 +1,7 @@ #include "cell.hpp" #include +#include #include #include @@ -171,8 +172,10 @@ void CSVRender::Cell::unloadLand() mCellBorder.reset(); } -CSVRender::Cell::Cell(CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) +CSVRender::Cell::Cell( + CSMWorld::Data& data, QUndoStack& undoStack, osg::Group* rootNode, const std::string& id, bool deleted) : mData(data) + , mUndoStack(undoStack) , mId(ESM::RefId::stringRefId(id)) , mDeleted(deleted) , mSubMode(0) diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 5bfce47904..e0672fa350 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -9,6 +9,8 @@ #include #include +#include + #include "../../model/world/cellcoordinates.hpp" #include "instancedragmodes.hpp" #include @@ -46,6 +48,7 @@ namespace CSVRender class Cell { CSMWorld::Data& mData; + QUndoStack& mUndoStack; ESM::RefId mId; osg::ref_ptr mCellNode; std::map mObjects; @@ -89,7 +92,8 @@ namespace CSVRender public: /// \note Deleted covers both cells that are deleted and cells that don't exist in /// the first place. - Cell(CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted = false); + Cell(CSMWorld::Data& data, QUndoStack& undoStack, osg::Group* rootNode, const std::string& id, + bool deleted = false); ~Cell(); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 062882bd04..c2b11c0247 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -86,8 +86,8 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells() { modified = true; - auto cell = std::make_unique( - mDocument.getData(), mRootNode, iter->first.getId(mWorldspace), deleted); + auto cell = std::make_unique(mDocument.getData(), mDocument.getUndoStack(), mRootNode, + iter->first.getId(mWorldspace), deleted); delete iter->second; iter->second = cell.release(); @@ -465,7 +465,8 @@ void CSVRender::PagedWorldspaceWidget::addCellToScene(const CSMWorld::CellCoordi bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted; - auto cell = std::make_unique(mDocument.getData(), mRootNode, coordinates.getId(mWorldspace), deleted); + auto cell = std::make_unique( + mDocument.getData(), mDocument.getUndoStack(), mRootNode, coordinates.getId(mWorldspace), deleted); EditMode* editMode = getEditMode(); cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask()); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index fee43b5de5..1a223ed41b 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -79,7 +79,7 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget( update(); - mCell = std::make_unique(document.getData(), mRootNode, mCellId); + mCell = std::make_unique(document.getData(), document.getUndoStack(), mRootNode, mCellId); } void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) @@ -127,7 +127,7 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop( mCellId = universalIdData.begin()->getId(); - mCell = std::make_unique(getDocument().getData(), mRootNode, mCellId); + mCell = std::make_unique(getDocument().getData(), getDocument().getUndoStack(), mRootNode, mCellId); mCamPositionSet = false; mOrbitCamControl->reset(); From d8187e40b253a80c6bf2c010645485c94e39de7a Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 10 Dec 2023 11:16:59 -0600 Subject: [PATCH 10/24] Feat(CS): Create LAND record using the undo stack --- apps/opencs/view/render/cell.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index a67923ecf4..f67c2ec466 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -25,6 +25,7 @@ #include "cellwater.hpp" #include "instancedragmodes.hpp" #include "mask.hpp" +#include "model/world/commands.hpp" #include "object.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" @@ -131,11 +132,17 @@ void CSVRender::Cell::updateLand() return; } - // Setup land if available - const CSMWorld::IdCollection& land = mData.getLand(); + CSMWorld::IdCollection& land = mData.getLand(); int landIndex = land.searchId(mId); - if (landIndex == -1 || land.getRecord(mId).isDeleted()) + if (landIndex == -1) + { + CSMWorld::IdTable& landTable + = dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Land)); + mUndoStack.push(new CSMWorld::CreateCommand(landTable, mId.getRefIdString())); + } + + if (land.getRecord(mId).isDeleted()) return; const ESM::Land& esmLand = land.getRecord(mId).get(); From ad135e3b95a911c134929e3c17ba1201a223d787 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 10 Dec 2023 12:59:45 -0600 Subject: [PATCH 11/24] Fix(cell.cpp): Change commands.hpp to absolute include path --- apps/opencs/view/render/cell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index f67c2ec466..6715aac6a1 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -25,7 +25,6 @@ #include "cellwater.hpp" #include "instancedragmodes.hpp" #include "mask.hpp" -#include "model/world/commands.hpp" #include "object.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" @@ -33,6 +32,7 @@ #include #include #include +#include #include #include #include From 51df924d373c0eacc146183a4bd6ab4b0554f967 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 10 Dec 2023 12:24:36 -0600 Subject: [PATCH 12/24] Fix(Land): Restore vanilla behavior of ignoring LAND subrecords which are not flagged to actually exist --- components/esm3/loadland.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/components/esm3/loadland.cpp b/components/esm3/loadland.cpp index a69419cd0f..6fc2292dbd 100644 --- a/components/esm3/loadland.cpp +++ b/components/esm3/loadland.cpp @@ -101,23 +101,28 @@ namespace ESM { case fourCC("VNML"): esm.skipHSub(); - mDataTypes |= DATA_VNML; + if (mFlags & DATA_VNML) + mDataTypes |= DATA_VNML; break; case fourCC("VHGT"): esm.skipHSub(); - mDataTypes |= DATA_VHGT; + if (mFlags & DATA_VHGT) + mDataTypes |= DATA_VHGT; break; case fourCC("WNAM"): - esm.getHT(mWnam); - mDataTypes |= DATA_WNAM; + esm.getHExact(mWnam.data(), mWnam.size()); + if (mFlags & DATA_WNAM) + mDataTypes |= DATA_WNAM; break; case fourCC("VCLR"): esm.skipHSub(); - mDataTypes |= DATA_VCLR; + if (mFlags & DATA_VCLR) + mDataTypes |= DATA_VCLR; break; case fourCC("VTEX"): esm.skipHSub(); - mDataTypes |= DATA_VTEX; + if (mFlags & DATA_VCLR) + mDataTypes |= DATA_VTEX; break; default: esm.fail("Unknown subrecord"); From 6f2ae4a33b6131e110ad8dec4d481ac8f1bedbcb Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Tue, 12 Dec 2023 00:14:48 -0600 Subject: [PATCH 13/24] Cleanup(CS): Improve readability and clean up some implementations --- CHANGELOG.md | 1 + apps/opencs/view/render/cell.cpp | 4 --- apps/opencs/view/render/cellborder.cpp | 45 ++++++++++++++++++-------- components/esm3/loadland.hpp | 10 ++++-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0fcf8d0b..3b1a590d4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,6 +141,7 @@ Bug #7676: Incorrect magic effect order in alchemy Bug #7679: Scene luminance value flashes when toggling shaders Bug #7685: Corky sometimes doesn't follow Llovyn Andus + Bug #7707: (OpenCS): New landscape records do not contain appropriate flags Bug #7712: Casting doesn't support spells and enchantments with no effects Bug #7721: CS: Special Chars Not Allowed in IDs Bug #7723: Assaulting vampires and werewolves shouldn't be a crime diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 6715aac6a1..ee1d5e4c3e 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -1,7 +1,6 @@ #include "cell.hpp" #include -#include #include #include @@ -147,9 +146,6 @@ void CSVRender::Cell::updateLand() const ESM::Land& esmLand = land.getRecord(mId).get(); - if (!esmLand.getLandData(ESM::Land::DATA_VHGT)) - mTerrainStorage->resetHeights(); - if (mTerrain) { mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index f63814dcb7..704db13e89 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -47,9 +47,6 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) { const ESM::Land::LandData* landData = esmLand.getLandData(ESM::Land::DATA_VHGT); - if (!landData) - return; - mBaseNode->removeChild(mBorderGeometry); mBorderGeometry = new osg::Geometry(); @@ -62,20 +59,40 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) Traverse the cell border counter-clockwise starting at the SW corner vertex (0, 0). Each loop starts at a corner vertex and ends right before the next corner vertex. */ - for (; x < ESM::Land::LAND_SIZE - 1; ++x) - vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); + if (landData) + { + for (; x < ESM::Land::LAND_SIZE - 1; ++x) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); - x = ESM::Land::LAND_SIZE - 1; - for (; y < ESM::Land::LAND_SIZE - 1; ++y) - vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); + x = ESM::Land::LAND_SIZE - 1; + for (; y < ESM::Land::LAND_SIZE - 1; ++y) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); - y = ESM::Land::LAND_SIZE - 1; - for (; x > 0; --x) - vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); + y = ESM::Land::LAND_SIZE - 1; + for (; x > 0; --x) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); - x = 0; - for (; y > 0; --y) - vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); + x = 0; + for (; y > 0; --y) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); + } + else + { + for (; x < ESM::Land::LAND_SIZE - 1; ++x) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), ESM::Land::DEFAULT_HEIGHT)); + + x = ESM::Land::LAND_SIZE - 1; + for (; y < ESM::Land::LAND_SIZE - 1; ++y) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), ESM::Land::DEFAULT_HEIGHT)); + + y = ESM::Land::LAND_SIZE - 1; + for (; x > 0; --x) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), ESM::Land::DEFAULT_HEIGHT)); + + x = 0; + for (; y > 0; --y) + vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), ESM::Land::DEFAULT_HEIGHT)); + } mBorderGeometry->setVertexArray(vertices); diff --git a/components/esm3/loadland.hpp b/components/esm3/loadland.hpp index 5e6f67c521..9e1f71e12b 100644 --- a/components/esm3/loadland.hpp +++ b/components/esm3/loadland.hpp @@ -127,10 +127,16 @@ namespace ESM const LandData* getLandData(int flags) const; /// Return land data without loading first anything. Can return a 0-pointer. - const LandData* getLandData() const { return mLandData.get(); } + const LandData* getLandData() const + { + return mLandData.get(); + } /// Return land data without loading first anything. Can return a 0-pointer. - LandData* getLandData() { return mLandData.get(); } + LandData* getLandData() + { + return mLandData.get(); + } /// \attention Must not be called on objects that aren't fully loaded. /// From 89bce6f6789e5556182f07706791f20918314f0d Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Wed, 13 Dec 2023 17:53:05 -0600 Subject: [PATCH 14/24] Fix(CS): Enum name in accordance with current standards --- apps/opencs/model/world/columnimp.cpp | 6 +++--- components/esm3/loadland.hpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index a3b9f3b5ae..a97fc85ec7 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -202,7 +202,7 @@ namespace CSMWorld copy.getLandData()->mHeights[i] = values[i]; } - copy.mFlags |= Land::FLAG_HEIGHT; + copy.mFlags |= Land::Flag_HeightsNormals; record.setModified(copy); } @@ -251,7 +251,7 @@ namespace CSMWorld copy.getLandData()->mColours[i] = values[i]; } - copy.mFlags |= Land::FLAG_COLOR; + copy.mFlags |= Land::Flag_Colors; record.setModified(copy); } @@ -300,7 +300,7 @@ namespace CSMWorld copy.getLandData()->mTextures[i] = values[i]; } - copy.mFlags |= Land::FLAG_TEXTURE; + copy.mFlags |= Land::Flag_Textures; record.setModified(copy); } diff --git a/components/esm3/loadland.hpp b/components/esm3/loadland.hpp index 9e1f71e12b..3f01f021e0 100644 --- a/components/esm3/loadland.hpp +++ b/components/esm3/loadland.hpp @@ -66,11 +66,11 @@ namespace ESM DATA_VTEX = 16 }; - enum + enum Flags { - FLAG_HEIGHT = 1, - FLAG_COLOR = 2, - FLAG_TEXTURE = 4 + Flag_HeightsNormals = 0x1, + Flag_Colors = 0x2, + Flag_Textures = 0x4 }; // default height to use in case there is no Land record From bdf99c701bfb589c7147925c31c14611a61add7a Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Wed, 13 Dec 2023 17:53:28 -0600 Subject: [PATCH 15/24] Fix(CS): Use ESM flags and not internal ones for verifying presence of a subrecord --- components/esm3/loadland.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/esm3/loadland.cpp b/components/esm3/loadland.cpp index 6fc2292dbd..102f795dab 100644 --- a/components/esm3/loadland.cpp +++ b/components/esm3/loadland.cpp @@ -101,27 +101,27 @@ namespace ESM { case fourCC("VNML"): esm.skipHSub(); - if (mFlags & DATA_VNML) + if (mFlags & Flag_HeightsNormals) mDataTypes |= DATA_VNML; break; case fourCC("VHGT"): esm.skipHSub(); - if (mFlags & DATA_VHGT) + if (mFlags & Flag_HeightsNormals) mDataTypes |= DATA_VHGT; break; case fourCC("WNAM"): esm.getHExact(mWnam.data(), mWnam.size()); - if (mFlags & DATA_WNAM) + if (mFlags & Flag_HeightsNormals) mDataTypes |= DATA_WNAM; break; case fourCC("VCLR"): esm.skipHSub(); - if (mFlags & DATA_VCLR) + if (mFlags & Flag_Colors) mDataTypes |= DATA_VCLR; break; case fourCC("VTEX"): esm.skipHSub(); - if (mFlags & DATA_VCLR) + if (mFlags & Flag_Textures) mDataTypes |= DATA_VTEX; break; default: From 47044e1dc0d07f857ea8a05c26007792728eccc8 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 3 Feb 2024 13:45:27 -0600 Subject: [PATCH 16/24] Cleanup(CS): re-add const for land collection --- apps/opencs/view/render/cell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index ee1d5e4c3e..3d70c21e3e 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -131,7 +131,7 @@ void CSVRender::Cell::updateLand() return; } - CSMWorld::IdCollection& land = mData.getLand(); + const CSMWorld::IdCollection& land = mData.getLand(); int landIndex = land.searchId(mId); if (landIndex == -1) From 0a8fee59dd0deaa64a160adcd6602f01c46c302b Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Thu, 22 Feb 2024 05:42:02 -0600 Subject: [PATCH 17/24] Don't store member variables from document & use simpler and safer inputs to cell constructor. Explicitly tell the constructor whether land should be loaded or not --- apps/opencs/view/render/cell.cpp | 30 ++++++++++--------- apps/opencs/view/render/cell.hpp | 8 ++--- .../view/render/pagedworldspacewidget.cpp | 7 ++--- .../view/render/unpagedworldspacewidget.cpp | 4 +-- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 3d70c21e3e..b20dc5445f 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -24,6 +24,7 @@ #include "cellwater.hpp" #include "instancedragmodes.hpp" #include "mask.hpp" +#include "model/doc/document.hpp" #include "object.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" @@ -119,7 +120,7 @@ bool CSVRender::Cell::addObjects(int start, int end) void CSVRender::Cell::updateLand() { - if (!mUpdateLand || mLandDeleted || !mId.startsWith("#")) + if (mLandDeleted) return; mUpdateLand = false; @@ -132,14 +133,6 @@ void CSVRender::Cell::updateLand() } const CSMWorld::IdCollection& land = mData.getLand(); - int landIndex = land.searchId(mId); - - if (landIndex == -1) - { - CSMWorld::IdTable& landTable - = dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Land)); - mUndoStack.push(new CSMWorld::CreateCommand(landTable, mId.getRefIdString())); - } if (land.getRecord(mId).isDeleted()) return; @@ -176,14 +169,13 @@ void CSVRender::Cell::unloadLand() } CSVRender::Cell::Cell( - CSMWorld::Data& data, QUndoStack& undoStack, osg::Group* rootNode, const std::string& id, bool deleted) - : mData(data) - , mUndoStack(undoStack) + CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted, bool isExterior) + : mData(document.getData()) , mId(ESM::RefId::stringRefId(id)) , mDeleted(deleted) , mSubMode(0) , mSubModeElementMask(0) - , mUpdateLand(true) + , mUpdateLand(isExterior) , mLandDeleted(false) { std::pair result = CSMWorld::CellCoordinates::fromId(id); @@ -209,7 +201,17 @@ CSVRender::Cell::Cell( addObjects(0, rows - 1); - updateLand(); + if (mUpdateLand) + { + int landIndex = document.getData().getLand().searchId(mId); + if (landIndex == -1) + { + CSMWorld::IdTable& landTable + = dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Land)); + document.getUndoStack().push(new CSMWorld::CreateCommand(landTable, mId.getRefIdString())); + } + updateLand(); + } mPathgrid = std::make_unique(mData, mCellNode, mId.getRefIdString(), mCoordinates); mCellWater = std::make_unique(mData, mCellNode, mId.getRefIdString(), mCoordinates); diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index e0672fa350..b894a69cf8 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -9,10 +9,9 @@ #include #include -#include - #include "../../model/world/cellcoordinates.hpp" #include "instancedragmodes.hpp" +#include "model/doc/document.hpp" #include #include @@ -48,7 +47,6 @@ namespace CSVRender class Cell { CSMWorld::Data& mData; - QUndoStack& mUndoStack; ESM::RefId mId; osg::ref_ptr mCellNode; std::map mObjects; @@ -92,8 +90,8 @@ namespace CSVRender public: /// \note Deleted covers both cells that are deleted and cells that don't exist in /// the first place. - Cell(CSMWorld::Data& data, QUndoStack& undoStack, osg::Group* rootNode, const std::string& id, - bool deleted = false); + Cell(CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted = false, + bool isExterior = false); ~Cell(); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index c2b11c0247..3fd35b7740 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -86,8 +86,8 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells() { modified = true; - auto cell = std::make_unique(mDocument.getData(), mDocument.getUndoStack(), mRootNode, - iter->first.getId(mWorldspace), deleted); + auto cell + = std::make_unique(mDocument, mRootNode, iter->first.getId(mWorldspace), deleted, true); delete iter->second; iter->second = cell.release(); @@ -465,8 +465,7 @@ void CSVRender::PagedWorldspaceWidget::addCellToScene(const CSMWorld::CellCoordi bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted; - auto cell = std::make_unique( - mDocument.getData(), mDocument.getUndoStack(), mRootNode, coordinates.getId(mWorldspace), deleted); + auto cell = std::make_unique(mDocument, mRootNode, coordinates.getId(mWorldspace), deleted, true); EditMode* editMode = getEditMode(); cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask()); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 1a223ed41b..a7d8af0a62 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -79,7 +79,7 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget( update(); - mCell = std::make_unique(document.getData(), document.getUndoStack(), mRootNode, mCellId); + mCell = std::make_unique(document, mRootNode, mCellId); } void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) @@ -127,7 +127,7 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop( mCellId = universalIdData.begin()->getId(); - mCell = std::make_unique(getDocument().getData(), getDocument().getUndoStack(), mRootNode, mCellId); + mCell = std::make_unique(getDocument(), mRootNode, mCellId); mCamPositionSet = false; mOrbitCamControl->reset(); From 10eb807e72124702474147da3080c913ea69658f Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 24 Feb 2024 11:16:12 -0600 Subject: [PATCH 18/24] Fix(Cell.hpp): Correct include path for document --- apps/opencs/view/render/cell.cpp | 2 +- apps/opencs/view/render/cell.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index b20dc5445f..1c5309a198 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -16,6 +16,7 @@ #include #include +#include "../../model/doc/document.hpp" #include "../../model/world/idtable.hpp" #include "cellarrow.hpp" @@ -24,7 +25,6 @@ #include "cellwater.hpp" #include "instancedragmodes.hpp" #include "mask.hpp" -#include "model/doc/document.hpp" #include "object.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index b894a69cf8..5ec8d87c33 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -9,9 +9,9 @@ #include #include +#include "../../model/doc/document.hpp" #include "../../model/world/cellcoordinates.hpp" #include "instancedragmodes.hpp" -#include "model/doc/document.hpp" #include #include From 86316fa7ab4758f10cbe7c0b0118d788f077be07 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sat, 24 Feb 2024 13:14:22 -0600 Subject: [PATCH 19/24] Fix(cell.cpp): Don't try to update land for interiors --- apps/opencs/view/render/cell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 1c5309a198..0cabf5dcd5 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -120,7 +120,7 @@ bool CSVRender::Cell::addObjects(int start, int end) void CSVRender::Cell::updateLand() { - if (mLandDeleted) + if (mLandDeleted || !mUpdateLand) return; mUpdateLand = false; From 145f9c1154b7c48c7dae406b3ab68e7440f2f617 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Thu, 16 May 2024 02:14:51 -0500 Subject: [PATCH 20/24] CLEANUP(CS): Do heightData check in actual implementation, revert accidental change to land condition order --- apps/opencs/view/render/cell.cpp | 2 +- apps/opencs/view/render/terrainstorage.cpp | 2 ++ components/esmterrain/storage.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 0cabf5dcd5..3d3b82acf8 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -120,7 +120,7 @@ bool CSVRender::Cell::addObjects(int start, int end) void CSVRender::Cell::updateLand() { - if (mLandDeleted || !mUpdateLand) + if (!mUpdateLand || mLandDeleted) return; mUpdateLand = false; diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index ff9a8e09b1..7b59310bcd 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -154,6 +154,8 @@ namespace CSVRender void TerrainStorage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const { + if (!heightData) + return; // Highlight broken height changes int heightWarningLimit = 1024; if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData->getHeights())) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index b43b996996..35ec814aa2 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -346,7 +346,7 @@ namespace ESMTerrain color[i] = colourData->getColors()[srcArrayIndex + i]; // Does nothing by default, override in OpenMW-CS - if (alteration && heightData != nullptr) + if (alteration) adjustColor(col, row, heightData, color); // Unlike normals, colors mostly connect seamlessly between cells, but not always... From 3066695630f3f2b00ad9930c86ca69f1049cd717 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Thu, 16 May 2024 03:04:02 -0500 Subject: [PATCH 21/24] FIX(loadland.cpp): Minor rebase fail --- components/esm3/loadland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm3/loadland.cpp b/components/esm3/loadland.cpp index 102f795dab..665c53755e 100644 --- a/components/esm3/loadland.cpp +++ b/components/esm3/loadland.cpp @@ -110,7 +110,7 @@ namespace ESM mDataTypes |= DATA_VHGT; break; case fourCC("WNAM"): - esm.getHExact(mWnam.data(), mWnam.size()); + esm.getExact(mWnam.data(), mWnam.size()); if (mFlags & Flag_HeightsNormals) mDataTypes |= DATA_WNAM; break; From 80360ca5ebca809b0bd699beebba402daacfda83 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Thu, 16 May 2024 03:23:51 -0500 Subject: [PATCH 22/24] downgrade clang-format pls --- components/esm3/loadland.hpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/components/esm3/loadland.hpp b/components/esm3/loadland.hpp index 3f01f021e0..f6a25c1ecb 100644 --- a/components/esm3/loadland.hpp +++ b/components/esm3/loadland.hpp @@ -127,16 +127,10 @@ namespace ESM const LandData* getLandData(int flags) const; /// Return land data without loading first anything. Can return a 0-pointer. - const LandData* getLandData() const - { - return mLandData.get(); - } + const LandData* getLandData() const { return mLandData.get(); } /// Return land data without loading first anything. Can return a 0-pointer. - LandData* getLandData() - { - return mLandData.get(); - } + LandData* getLandData() { return mLandData.get(); } /// \attention Must not be called on objects that aren't fully loaded. /// From 798208f6e535a2bf32b8f4b47c9d7cb9ff527565 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Fri, 17 May 2024 02:45:26 -0500 Subject: [PATCH 23/24] FIX: Bad rebase --- components/esm3/loadland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm3/loadland.cpp b/components/esm3/loadland.cpp index 665c53755e..7ab14cc304 100644 --- a/components/esm3/loadland.cpp +++ b/components/esm3/loadland.cpp @@ -110,7 +110,7 @@ namespace ESM mDataTypes |= DATA_VHGT; break; case fourCC("WNAM"): - esm.getExact(mWnam.data(), mWnam.size()); + esm.getHT(mWnam); if (mFlags & Flag_HeightsNormals) mDataTypes |= DATA_WNAM; break; From 659874cb6567baa964b381b19aaca51fc2f7aa40 Mon Sep 17 00:00:00 2001 From: Dave Corley Date: Sun, 9 Jun 2024 22:05:42 -0500 Subject: [PATCH 24/24] FEAT: Update land flag representation in ESMTool --- apps/esmtool/labels.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 83b4a486e1..3d64563923 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -766,18 +767,16 @@ std::string enchantmentFlags(int flags) std::string landFlags(std::uint32_t flags) { std::string properties; - // The ESM component says that this first four bits are used, but - // only the first three bits are used as far as I can tell. - // There's also no enumeration of the bit in the ESM component. if (flags == 0) properties += "[None] "; - if (flags & 0x00000001) - properties += "Unknown1 "; - if (flags & 0x00000004) - properties += "Unknown3 "; - if (flags & 0x00000002) - properties += "Unknown2 "; - if (flags & 0xFFFFFFF8) + if (flags & ESM::Land::Flag_HeightsNormals) + properties += "HeightsNormals "; + if (flags & ESM::Land::Flag_Colors) + properties += "Colors "; + if (flags & ESM::Land::Flag_Textures) + properties += "Textures "; + int unused = 0xFFFFFFFF ^ (ESM::Land::Flag_HeightsNormals | ESM::Land::Flag_Colors | ESM::Land::Flag_Textures); + if (flags & unused) properties += "Invalid "; properties += Misc::StringUtils::format("(0x%08X)", flags); return properties;