diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 8399ea52a0..98b9f2d84c 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -371,6 +371,11 @@ float CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int in return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY); } +std::optional 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); diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 093a047d65..7fcd34cc9b 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -125,6 +125,8 @@ namespace CSVRender float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); + std::optional getOriginalHeight(int cellX, int cellY, int inCellX, int inCellY); + float* getAlteredHeight(int inCellX, int inCellY); void resetAlteredHeights(); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 90670a4d62..f07f6f14f8 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -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::iterator searchResult = mCells.find(coords); + if (searchResult != mCells.end()) + { + std::optional baseHeight + = searchResult->second->getOriginalHeight(coords.getX(), coords.getY(), inCellX, inCellY); + if (baseHeight) + searchResult->second->setAlteredHeight(inCellX, inCellY, absoluteHeight - *baseHeight); + } +} + +std::optional CSVRender::PagedWorldspaceWidget::getCellOriginalHeight( + const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY) +{ + std::map::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) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index dc47d5ea04..e29fbd52f2 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -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 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); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 477ecbd6a5..167f880dd6 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -258,35 +258,12 @@ 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 + for (CSMWorld::CellCoordinates cellCoordinates : mAlteredCells) { - 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; - } + limitAlteredHeights(cellCoordinates); } } @@ -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,113 +606,131 @@ 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 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))) + { + if (allowLandShapeEditing(cellUpLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) + && allowLandShapeEditing(cellUpId, useTool)) + { + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) + == mAlteredCells.end()) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredAbsoluteHeight( + cornerCellCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE - 1, absoluteHeight); + } + else + return; + } + else if ((inCellX == 0 && inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownLeftId))) + { + if (allowLandShapeEditing(cellDownLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) + && allowLandShapeEditing(cellDownId, useTool)) + { + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) + == mAlteredCells.end()) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredAbsoluteHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, 0, absoluteHeight); + } + else + return; + } + else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == 0) && (useTool || isLandLoaded(cellUpRightId))) + { + if (allowLandShapeEditing(cellUpRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) + && allowLandShapeEditing(cellUpId, useTool)) + { + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) + == mAlteredCells.end()) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredAbsoluteHeight(cornerCellCoords, 0, ESM::Land::LAND_SIZE - 1, absoluteHeight); + } + else + return; + } + else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == ESM::Land::LAND_SIZE - 1) + && (useTool || isLandLoaded(cellDownRightId))) + { + if (allowLandShapeEditing(cellDownRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) + && allowLandShapeEditing(cellDownId, useTool)) + { + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) + == mAlteredCells.end()) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredAbsoluteHeight(cornerCellCoords, 0, 0, absoluteHeight); + } + else + return; + } - // Change values of cornering cells - if ((inCellX == 0 && inCellY == 0) && (useTool || isLandLoaded(cellUpLeftId))) - { - if (allowLandShapeEditing(cellUpLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) - && allowLandShapeEditing(cellUpId, useTool)) - { - CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1); - if (useTool - && 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); - } - else - return; - } - else if ((inCellX == 0 && inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownLeftId))) - { - if (allowLandShapeEditing(cellDownLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) - && allowLandShapeEditing(cellDownId, useTool)) - { - CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1); - if (useTool - && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end()) - mAlteredCells.emplace_back(cornerCellCoords); - paged->setCellAlteredHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, 0, alteredHeight); - } - else - return; - } - else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == 0) && (useTool || isLandLoaded(cellUpRightId))) - { - if (allowLandShapeEditing(cellUpRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) - && allowLandShapeEditing(cellUpId, useTool)) - { - CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1); - if (useTool - && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end()) - mAlteredCells.emplace_back(cornerCellCoords); - paged->setCellAlteredHeight(cornerCellCoords, 0, ESM::Land::LAND_SIZE - 1, alteredHeight); - } - else - return; - } - else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == ESM::Land::LAND_SIZE - 1) - && (useTool || isLandLoaded(cellDownRightId))) - { - if (allowLandShapeEditing(cellDownRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) - && allowLandShapeEditing(cellDownId, useTool)) - { - CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1); - if (useTool - && std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) == mAlteredCells.end()) - mAlteredCells.emplace_back(cornerCellCoords); - paged->setCellAlteredHeight(cornerCellCoords, 0, 0, alteredHeight); - } - else - return; - } + // Change values of edging cells + if ((inCellX == 0) && (useTool || isLandLoaded(cellLeftId))) + { + if (allowLandShapeEditing(cellLeftId, useTool)) + { + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + paged->setCellAlteredAbsoluteHeight( + edgeCellCoords, ESM::Land::LAND_SIZE - 1, inCellY, absoluteHeight); + } + } + if ((inCellY == 0) && (useTool || isLandLoaded(cellUpId))) + { + if (allowLandShapeEditing(cellUpId, useTool)) + { + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + paged->setCellAlteredAbsoluteHeight( + edgeCellCoords, inCellX, ESM::Land::LAND_SIZE - 1, absoluteHeight); + } + } - // Change values of edging cells - if ((inCellX == 0) && (useTool || isLandLoaded(cellLeftId))) - { - if (allowLandShapeEditing(cellLeftId, useTool)) - { - CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0); - 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); - } - } - if ((inCellY == 0) && (useTool || isLandLoaded(cellUpId))) - { - if (allowLandShapeEditing(cellUpId, useTool)) - { - CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1); - 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); - } - } - - if ((inCellX == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellRightId))) - { - if (allowLandShapeEditing(cellRightId, useTool)) - { - CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0); - 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); - } - } - if ((inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownId))) - { - if (allowLandShapeEditing(cellDownId, useTool)) - { - CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1); - 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); + if ((inCellX == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellRightId))) + { + if (allowLandShapeEditing(cellRightId, useTool)) + { + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + paged->setCellAlteredAbsoluteHeight(edgeCellCoords, 0, inCellY, absoluteHeight); + } + } + if ((inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownId))) + { + if (allowLandShapeEditing(cellDownId, useTool)) + { + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1); + if (useTool + && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, 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(); - *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(); - *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(); - *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(); - *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 inCellY = 0; inCellY < ESM::Land::LAND_SIZE; ++inCellY) + for (int inCellX = 0; inCellX < ESM::Land::LAND_SIZE; ++inCellX) { - for (int inCellX = 0; inCellX < ESM::Land::LAND_SIZE; ++inCellX) + updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, + &leftAlteredHeight, &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, + &downAlteredHeight); + + if (inCellX == 0) { - std::unique_ptr limitedAlteredHeightXAxis(nullptr); - std::unique_ptr 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( - leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); - else if (leftHeight - thisHeight < -limitHeightChange) - limitedAlteredHeightXAxis = std::make_unique( - leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); - // Check for height limits on y-axis + std::unique_ptr limitedAlteredHeightYAxis(nullptr); if (upHeight - thisHeight > limitHeightChange) limitedAlteredHeightYAxis = std::make_unique(upHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); @@ -1151,49 +1041,26 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi limitedAlteredHeightYAxis = std::make_unique(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) - { - for (int inCellY = ESM::Land::LAND_SIZE - 1; inCellY >= 0; --inCellY) - { - for (int inCellX = ESM::Land::LAND_SIZE - 1; inCellX >= 0; --inCellX) + else { - std::unique_ptr limitedAlteredHeightXAxis(nullptr); - std::unique_ptr 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 limitedAlteredHeightXAxis(nullptr); + if (leftHeight - thisHeight > limitHeightChange) limitedAlteredHeightXAxis = std::make_unique( - rightHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); - else if (rightHeight - thisHeight < -limitHeightChange) + leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + else if (leftHeight - thisHeight < -limitHeightChange) limitedAlteredHeightXAxis = std::make_unique( - rightHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); + leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); - // Check for height limits on y-axis - if (downHeight - thisHeight > limitHeightChange) - limitedAlteredHeightYAxis = std::make_unique( - downHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); - else if (downHeight - thisHeight < -limitHeightChange) - limitedAlteredHeightYAxis = std::make_unique( - 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) diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 2344676cbd..fca3a2be5c 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -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); diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 1c494820dc..e9f3c1fedc 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -59,16 +59,21 @@ namespace CSVRender float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) { - float height = 0.f; + std::optional true_height = getOriginalHeight(cellX, cellY, inCellX, inCellY); + if (true_height) + return *true_height + *getAlteredHeight(inCellX, inCellY); + return 0.0f; + } + std::optional 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 heightData) const { - return heightData[(col - 1) * ESM::Land::LAND_SIZE + row] - + mAlteredHeight[static_cast((col - 1) * ESM::Land::LAND_SIZE + row)]; + return heightData[(col + 1) * ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col + 1) * ESM::Land::LAND_SIZE + row)]; } int TerrainStorage::getDownHeight(int col, int row, std::span heightData) const { - return heightData[(col + 1) * ESM::Land::LAND_SIZE + row] - + mAlteredHeight[static_cast((col + 1) * ESM::Land::LAND_SIZE + row)]; + return heightData[(col - 1) * ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col - 1) * ESM::Land::LAND_SIZE + row)]; } int TerrainStorage::getHeightDifferenceToLeft(int col, int row, std::span 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 heightData) const - { - return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit - || getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit; - } - - bool TerrainStorage::rightOrDownIsOverTheLimit( - int col, int row, int heightWarningLimit, std::span 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; diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index f7a7f72201..3477306e3e 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -31,6 +31,7 @@ namespace CSVRender bool useAlteration() const override { return true; } float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); + std::optional 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 heightData) const; int getHeightDifferenceToUp(int col, int row, std::span heightData) const; int getHeightDifferenceToDown(int col, int row, std::span heightData) const; - bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, std::span heightData) const; - bool rightOrDownIsOverTheLimit( - int col, int row, int heightWarningLimit, std::span 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;