Merge branch 'fix_some_terrain_bug' into 'master'

CS: Make painting terrain height over a cell border equalize the height at both edge

See merge request OpenMW/openmw!4898
This commit is contained in:
marius851000 2025-09-20 12:25:42 +00:00
commit a240f01d5f
8 changed files with 207 additions and 315 deletions

View File

@ -371,6 +371,11 @@ float CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int in
return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY);
}
std::optional<float> CSVRender::Cell::getOriginalHeight(int cellX, int cellY, int inCellX, int inCellY)
{
return mTerrainStorage->getOriginalHeight(cellX, cellY, inCellX, inCellY);
}
float* CSVRender::Cell::getAlteredHeight(int inCellX, int inCellY)
{
return mTerrainStorage->getAlteredHeight(inCellX, inCellY);

View File

@ -125,6 +125,8 @@ namespace CSVRender
float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);
std::optional<float> getOriginalHeight(int cellX, int cellY, int inCellX, int inCellY);
float* getAlteredHeight(int inCellX, int inCellY);
void resetAlteredHeights();

View File

@ -824,6 +824,30 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const CSMWorld::CellC
return nullptr;
}
void CSVRender::PagedWorldspaceWidget::setCellAlteredAbsoluteHeight(
const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float absoluteHeight)
{
std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end())
{
std::optional<float> baseHeight
= searchResult->second->getOriginalHeight(coords.getX(), coords.getY(), inCellX, inCellY);
if (baseHeight)
searchResult->second->setAlteredHeight(inCellX, inCellY, absoluteHeight - *baseHeight);
}
}
std::optional<float> CSVRender::PagedWorldspaceWidget::getCellOriginalHeight(
const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY)
{
std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords);
if (searchResult != mCells.end())
{
return searchResult->second->getOriginalHeight(coords.getX(), coords.getY(), inCellX, inCellY);
}
return {};
}
void CSVRender::PagedWorldspaceWidget::setCellAlteredHeight(
const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height)
{

View File

@ -153,6 +153,11 @@ namespace CSVRender
Cell* getCell(const CSMWorld::CellCoordinates& coords) const override;
void setCellAlteredAbsoluteHeight(
const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float absoluteHeight);
std::optional<float> getCellOriginalHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY);
void setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height);
float* getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY);

View File

@ -258,36 +258,13 @@ void CSVRender::TerrainShapeMode::dragWheel(int diff, double speedFactor) {}
void CSVRender::TerrainShapeMode::sortAndLimitAlteredCells()
{
bool passing = false;
int passes = 0;
std::sort(mAlteredCells.begin(), mAlteredCells.end());
mAlteredCells.erase(std::unique(mAlteredCells.begin(), mAlteredCells.end()), mAlteredCells.end());
while (!passing) // Multiple passes are needed when steepness problems arise for both x and y axis simultaneously
{
passing = true;
for (CSMWorld::CellCoordinates cellCoordinates : mAlteredCells)
{
limitAlteredHeights(cellCoordinates);
}
std::reverse(mAlteredCells.begin(),
mAlteredCells
.end()); // Instead of alphabetical order, this should be fixed to sort cells by cell coordinates
for (CSMWorld::CellCoordinates cellCoordinates : mAlteredCells)
{
if (!limitAlteredHeights(cellCoordinates, true))
passing = false;
}
++passes;
if (passes > 2)
{
Log(Debug::Warning) << "Warning: User edit exceeds accepted slope steepness. Automatic limiting has "
"failed, edit has been discarded.";
clearTransientEdits();
return;
}
}
}
void CSVRender::TerrainShapeMode::clearTransientEdits()
@ -590,8 +567,8 @@ void CSVRender::TerrainShapeMode::setFlattenToolTargetHeight(const WorldspaceHit
mTargetHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX];
}
void CSVRender::TerrainShapeMode::alterHeight(
const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool)
void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY,
float alteredHeight, bool useTool, bool useAndAlterAdjacentCell)
{
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());
@ -629,9 +606,15 @@ void CSVRender::TerrainShapeMode::alterHeight(
alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight;
}
if (inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1)
if ((inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1)
|| !useAndAlterAdjacentCell)
paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);
else
{
std::optional<float> originalHeight = paged->getCellOriginalHeight(cellCoords, inCellX, inCellY);
if (originalHeight)
{
float absoluteHeight = *originalHeight + alteredHeight;
// Change values of cornering cells
if ((inCellX == 0 && inCellY == 0) && (useTool || isLandLoaded(cellUpLeftId)))
{
@ -640,10 +623,11 @@ void CSVRender::TerrainShapeMode::alterHeight(
{
CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1);
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords)
== mAlteredCells.end())
mAlteredCells.emplace_back(cornerCellCoords);
paged->setCellAlteredHeight(
cornerCellCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE - 1, alteredHeight);
paged->setCellAlteredAbsoluteHeight(
cornerCellCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE - 1, absoluteHeight);
}
else
return;
@ -655,9 +639,10 @@ void CSVRender::TerrainShapeMode::alterHeight(
{
CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1);
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords)
== mAlteredCells.end())
mAlteredCells.emplace_back(cornerCellCoords);
paged->setCellAlteredHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, 0, alteredHeight);
paged->setCellAlteredAbsoluteHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, 0, absoluteHeight);
}
else
return;
@ -669,9 +654,10 @@ void CSVRender::TerrainShapeMode::alterHeight(
{
CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1);
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords)
== mAlteredCells.end())
mAlteredCells.emplace_back(cornerCellCoords);
paged->setCellAlteredHeight(cornerCellCoords, 0, ESM::Land::LAND_SIZE - 1, alteredHeight);
paged->setCellAlteredAbsoluteHeight(cornerCellCoords, 0, ESM::Land::LAND_SIZE - 1, absoluteHeight);
}
else
return;
@ -684,9 +670,10 @@ void CSVRender::TerrainShapeMode::alterHeight(
{
CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1);
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end())
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords)
== mAlteredCells.end())
mAlteredCells.emplace_back(cornerCellCoords);
paged->setCellAlteredHeight(cornerCellCoords, 0, 0, alteredHeight);
paged->setCellAlteredAbsoluteHeight(cornerCellCoords, 0, 0, absoluteHeight);
}
else
return;
@ -698,10 +685,12 @@ void CSVRender::TerrainShapeMode::alterHeight(
if (allowLandShapeEditing(cellLeftId, useTool))
{
CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0);
if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
mAlteredCells.emplace_back(edgeCellCoords);
paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);
paged->setCellAlteredHeight(edgeCellCoords, ESM::Land::LAND_SIZE - 1, inCellY, alteredHeight);
paged->setCellAlteredAbsoluteHeight(
edgeCellCoords, ESM::Land::LAND_SIZE - 1, inCellY, absoluteHeight);
}
}
if ((inCellY == 0) && (useTool || isLandLoaded(cellUpId)))
@ -709,10 +698,12 @@ void CSVRender::TerrainShapeMode::alterHeight(
if (allowLandShapeEditing(cellUpId, useTool))
{
CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1);
if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
mAlteredCells.emplace_back(edgeCellCoords);
paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);
paged->setCellAlteredHeight(edgeCellCoords, inCellX, ESM::Land::LAND_SIZE - 1, alteredHeight);
paged->setCellAlteredAbsoluteHeight(
edgeCellCoords, inCellX, ESM::Land::LAND_SIZE - 1, absoluteHeight);
}
}
@ -721,10 +712,11 @@ void CSVRender::TerrainShapeMode::alterHeight(
if (allowLandShapeEditing(cellRightId, useTool))
{
CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0);
if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
mAlteredCells.emplace_back(edgeCellCoords);
paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);
paged->setCellAlteredHeight(edgeCellCoords, 0, inCellY, alteredHeight);
paged->setCellAlteredAbsoluteHeight(edgeCellCoords, 0, inCellY, absoluteHeight);
}
}
if ((inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownId)))
@ -732,10 +724,13 @@ void CSVRender::TerrainShapeMode::alterHeight(
if (allowLandShapeEditing(cellDownId, useTool))
{
CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1);
if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
if (useTool
&& std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end())
mAlteredCells.emplace_back(edgeCellCoords);
paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight);
paged->setCellAlteredHeight(edgeCellCoords, inCellX, 0, alteredHeight);
paged->setCellAlteredAbsoluteHeight(edgeCellCoords, inCellX, 0, absoluteHeight);
}
}
}
}
}
@ -966,70 +961,6 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor
*rightHeight = *thisHeight;
*downHeight = *thisHeight;
// If at edge, get values from neighboring cell
if (inCellX == 0)
{
if (isLandLoaded(cellLeftId))
{
const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer
= landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>();
*leftHeight = landLeftShapePointer[inCellY * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE - 2)];
if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY))
{
*leftAlteredHeight
= *paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY);
*leftHeight += *leftAlteredHeight;
}
}
}
if (inCellY == 0)
{
if (isLandLoaded(cellUpId))
{
const CSMWorld::LandHeightsColumn::DataType landUpShapePointer
= landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>();
*upHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 2) * ESM::Land::LAND_SIZE + inCellX];
if (paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2))
{
*upAlteredHeight
= *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2);
*upHeight += *upAlteredHeight;
}
}
}
if (inCellX == ESM::Land::LAND_SIZE - 1)
{
if (isLandLoaded(cellRightId))
{
const CSMWorld::LandHeightsColumn::DataType landRightShapePointer
= landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>();
*rightHeight = landRightShapePointer[inCellY * ESM::Land::LAND_SIZE + 1];
if (paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY))
{
*rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY);
*rightHeight += *rightAlteredHeight;
}
}
}
if (inCellY == ESM::Land::LAND_SIZE - 1)
{
if (isLandLoaded(cellDownId))
{
const CSMWorld::LandHeightsColumn::DataType landDownShapePointer
= landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>();
*downHeight = landDownShapePointer[ESM::Land::LAND_SIZE + inCellX];
if (paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1))
{
*downAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1);
*downHeight += *downAlteredHeight;
}
}
}
// If not at edge, get values from the same cell
if (inCellX != 0)
{
@ -1063,38 +994,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor
}
}
void CSVRender::TerrainShapeMode::compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY,
float* limitedAlteredHeightXAxis, float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits)
{
if (limitedAlteredHeightXAxis)
{
if (limitedAlteredHeightYAxis)
{
if (std::abs(*limitedAlteredHeightXAxis) >= std::abs(*limitedAlteredHeightYAxis))
{
alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false);
*steepnessIsWithinLimits = false;
}
else
{
alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false);
*steepnessIsWithinLimits = false;
}
}
else
{
alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false);
*steepnessIsWithinLimits = false;
}
}
else if (limitedAlteredHeightYAxis)
{
alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false);
*steepnessIsWithinLimits = false;
}
}
bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode)
void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable
@ -1104,7 +1004,6 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());
int limitHeightChange = 1016.0f; // Limited by save format
bool steepnessIsWithinLimits = true;
if (isLandLoaded(cellId))
{
@ -1123,27 +1022,18 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi
float downHeight = 0.0f;
float downAlteredHeight = 0.0f;
if (!reverseMode)
{
for (int inCellY = 0; inCellY < ESM::Land::LAND_SIZE; ++inCellY)
{
for (int inCellX = 0; inCellX < ESM::Land::LAND_SIZE; ++inCellX)
{
std::unique_ptr<float> limitedAlteredHeightXAxis(nullptr);
std::unique_ptr<float> limitedAlteredHeightYAxis(nullptr);
updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight,
&leftAlteredHeight, &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight,
&downAlteredHeight);
// Check for height limits on x-axis
if (leftHeight - thisHeight > limitHeightChange)
limitedAlteredHeightXAxis = std::make_unique<float>(
leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight));
else if (leftHeight - thisHeight < -limitHeightChange)
limitedAlteredHeightXAxis = std::make_unique<float>(
leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight));
if (inCellX == 0)
{
// Check for height limits on y-axis
std::unique_ptr<float> limitedAlteredHeightYAxis(nullptr);
if (upHeight - thisHeight > limitHeightChange)
limitedAlteredHeightYAxis
= std::make_unique<float>(upHeight - limitHeightChange - (thisHeight - thisAlteredHeight));
@ -1151,49 +1041,26 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi
limitedAlteredHeightYAxis
= std::make_unique<float>(upHeight + limitHeightChange - (thisHeight - thisAlteredHeight));
// Limit altered height value based on x or y, whichever is the smallest
compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis.get(),
limitedAlteredHeightYAxis.get(), &steepnessIsWithinLimits);
if (limitedAlteredHeightYAxis)
alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false, false);
}
}
}
if (reverseMode)
else
{
for (int inCellY = ESM::Land::LAND_SIZE - 1; inCellY >= 0; --inCellY)
{
for (int inCellX = ESM::Land::LAND_SIZE - 1; inCellX >= 0; --inCellX)
{
std::unique_ptr<float> limitedAlteredHeightXAxis(nullptr);
std::unique_ptr<float> limitedAlteredHeightYAxis(nullptr);
updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight,
&leftAlteredHeight, &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight,
&downAlteredHeight);
// Check for height limits on x-axis
if (rightHeight - thisHeight > limitHeightChange)
std::unique_ptr<float> limitedAlteredHeightXAxis(nullptr);
if (leftHeight - thisHeight > limitHeightChange)
limitedAlteredHeightXAxis = std::make_unique<float>(
rightHeight - limitHeightChange - (thisHeight - thisAlteredHeight));
else if (rightHeight - thisHeight < -limitHeightChange)
leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight));
else if (leftHeight - thisHeight < -limitHeightChange)
limitedAlteredHeightXAxis = std::make_unique<float>(
rightHeight + limitHeightChange - (thisHeight - thisAlteredHeight));
leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight));
// Check for height limits on y-axis
if (downHeight - thisHeight > limitHeightChange)
limitedAlteredHeightYAxis = std::make_unique<float>(
downHeight - limitHeightChange - (thisHeight - thisAlteredHeight));
else if (downHeight - thisHeight < -limitHeightChange)
limitedAlteredHeightYAxis = std::make_unique<float>(
downHeight + limitHeightChange - (thisHeight - thisAlteredHeight));
// Limit altered height value based on x or y, whichever is the smallest
compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis.get(),
limitedAlteredHeightYAxis.get(), &steepnessIsWithinLimits);
if (limitedAlteredHeightXAxis)
alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false, false);
}
}
}
}
return steepnessIsWithinLimits;
}
bool CSVRender::TerrainShapeMode::isInCellSelection(int globalSelectionX, int globalSelectionY)

View File

@ -137,7 +137,7 @@ namespace CSVRender
/// Do a single height alteration for transient shape edit map
void alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight,
bool useTool = true);
bool useTool = true, bool useAndAlterAdjacentCell = true);
/// Do a single smoothing height alteration for transient shape edit map
void smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength);
@ -149,19 +149,15 @@ namespace CSVRender
/// Do a single equalize alteration for transient shape edit map
void equalizeHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int targetHeight);
/// Get altered height values around one vertex
/// Get altered height values around one vertex. Default to value of this height for height out of this cell,
/// with 0 for altered height.
void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY,
float* thisHeight, float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight,
float* upAlteredHeight, float* rightHeight, float* rightAlteredHeight, float* downHeight,
float* downAlteredHeight);
/// Limit steepness based on either X or Y and return false if steepness is limited
void compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY,
float* limitedAlteredHeightXAxis, float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits);
/// Check that the edit doesn't break save format limits, fix if necessary, return true if slope steepness is
/// within limits
bool limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false);
/// Check that the edit doesn't break save format limits, fix if necessary
void limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords);
/// Check if global selection coordinate belongs to cell in view
bool isInCellSelection(int globalSelectionX, int globalSelectionY);

View File

@ -59,16 +59,21 @@ namespace CSVRender
float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)
{
float height = 0.f;
std::optional<float> true_height = getOriginalHeight(cellX, cellY, inCellX, inCellY);
if (true_height)
return *true_height + *getAlteredHeight(inCellX, inCellY);
return 0.0f;
}
std::optional<float> TerrainStorage::getOriginalHeight(int cellX, int cellY, int inCellX, int inCellY)
{
const int index
= mData.getLand().searchId(ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)));
if (index == -1) // no land!
return height;
return {};
const ESM::Land::LandData* landData = mData.getLand().getRecord(index).get().getLandData(ESM::Land::DATA_VHGT);
height = landData->mHeights[inCellY * ESM::Land::LAND_SIZE + inCellX];
return mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX] + height;
return landData->mHeights[inCellY * ESM::Land::LAND_SIZE + inCellX];
}
float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY)
@ -102,14 +107,14 @@ namespace CSVRender
int TerrainStorage::getUpHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[(col - 1) * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>((col - 1) * ESM::Land::LAND_SIZE + row)];
return heightData[(col + 1) * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>((col + 1) * ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getDownHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[(col + 1) * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>((col + 1) * ESM::Land::LAND_SIZE + row)];
return heightData[(col - 1) * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>((col - 1) * ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getHeightDifferenceToLeft(int col, int row, std::span<const float> heightData) const
@ -132,29 +137,19 @@ namespace CSVRender
return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));
}
bool TerrainStorage::leftOrUpIsOverTheLimit(
int col, int row, int heightWarningLimit, std::span<const float> heightData) const
{
return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit
|| getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;
}
bool TerrainStorage::rightOrDownIsOverTheLimit(
int col, int row, int heightWarningLimit, std::span<const float> heightData) const
{
return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit
|| getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;
}
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()))
|| ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1)
&& rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData->getHeights())))
if ((row > 0 && (getHeightDifferenceToRight(col, row, heightData->getHeights()) >= heightWarningLimit))
|| (row > 0 && row < ESM::Land::LAND_SIZE
&& (getHeightDifferenceToLeft(col, row, heightData->getHeights()) >= heightWarningLimit))
|| (row == 0 && col > 0
&& (getHeightDifferenceToDown(col, row, heightData->getHeights()) >= heightWarningLimit))
|| (row == 0 && col < ESM::Land::LAND_SIZE
&& (getHeightDifferenceToUp(col, row, heightData->getHeights()) >= heightWarningLimit)))
{
color.r() = 255;
color.g() = 0;

View File

@ -31,6 +31,7 @@ namespace CSVRender
bool useAlteration() const override { return true; }
float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);
std::optional<float> getOriginalHeight(int cellX, int cellY, int inCellX, int inCellY);
float* getAlteredHeight(int inCellX, int inCellY);
private:
@ -51,9 +52,6 @@ namespace CSVRender
int getHeightDifferenceToRight(int col, int row, std::span<const float> heightData) const;
int getHeightDifferenceToUp(int col, int row, std::span<const float> heightData) const;
int getHeightDifferenceToDown(int col, int row, std::span<const float> heightData) const;
bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, std::span<const float> heightData) const;
bool rightOrDownIsOverTheLimit(
int col, int row, int heightWarningLimit, std::span<const float> heightData) const;
void adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const override;
float getAlteredHeight(int col, int row) const override;