Editor: Fix crash if the smoothed terrain vertex belongs to an unloaded cell (#8686)

This commit is contained in:
Alexei Kotov 2025-08-27 15:37:55 +03:00
parent 0c95d62e89
commit 35cc6e6daf

View File

@ -743,17 +743,19 @@ void CSVRender::TerrainShapeMode::alterHeight(
void CSVRender::TerrainShapeMode::smoothHeight( void CSVRender::TerrainShapeMode::smoothHeight(
const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength) const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength)
{ {
if (CSVRender::PagedWorldspaceWidget* paged auto paged = dynamic_cast<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget());
= dynamic_cast<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget())) if (paged == nullptr)
{ return;
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());
if (!allowLandShapeEditing(cellId))
return;
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable CSMWorld::IdTable& landTable
= dynamic_cast<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land)); = dynamic_cast<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land));
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); const int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const auto landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY());
const CSMWorld::LandHeightsColumn::DataType landShapePointer
= landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>(); .value<CSMWorld::LandHeightsColumn::DataType>();
// ### Variable naming key ### // ### Variable naming key ###
@ -762,112 +764,99 @@ void CSVRender::TerrainShapeMode::smoothHeight(
// left = x - 1, up = y - 1, right = x + 1, down = y + 1 // left = x - 1, up = y - 1, right = x + 1, down = y + 1
// Altered = transient edit (in current edited) // Altered = transient edit (in current edited)
float thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX]; float thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX];
float* thisAlteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); if (const float* thisAlteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX, inCellY))
float thisAlteredHeight = thisAlteredHeightPtr != nullptr ? *thisAlteredHeightPtr : 0.f; thisHeight += *thisAlteredHeightPtr;
float leftHeight = thisHeight; float leftHeight = thisHeight;
float leftAlteredHeight = thisAlteredHeight;
float rightHeight = thisHeight; float rightHeight = thisHeight;
float rightAlteredHeight = thisAlteredHeight;
float downHeight = thisHeight; float downHeight = thisHeight;
float downAlteredHeight = thisAlteredHeight;
float upHeight = thisHeight; float upHeight = thisHeight;
float upAlteredHeight = thisAlteredHeight;
if (allowLandShapeEditing(cellId))
{
// Get key values for calculating average, handle cell edges, check for null pointers // Get key values for calculating average, handle cell edges, check for null pointers
if (inCellX == 0) if (inCellX == 0)
{ {
cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY());
if (isLandLoaded(cellId)) if (isLandLoaded(cellId))
{ {
const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer const auto landLeftShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
= landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>(); .value<CSMWorld::LandHeightsColumn::DataType>();
leftHeight = landLeftShapePointer[inCellY * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE - 2)]; leftHeight = landLeftShapePointer[inCellY * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE - 2)];
float* alteredHeightPtr if (const float* alteredHeightPtr
= paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY); = paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY))
leftAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f; leftHeight += *alteredHeightPtr;
} }
} }
else if (inCellX > 0)
{
leftHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX - 1];
if (const float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY))
leftHeight += *alteredHeightPtr;
}
if (inCellY == 0) if (inCellY == 0)
{ {
cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1);
if (isLandLoaded(cellId)) if (isLandLoaded(cellId))
{ {
const CSMWorld::LandHeightsColumn::DataType landUpShapePointer const auto landUpShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
= landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>(); .value<CSMWorld::LandHeightsColumn::DataType>();
upHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 2) * ESM::Land::LAND_SIZE + inCellX]; upHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 2) * ESM::Land::LAND_SIZE + inCellX];
float* alteredHeightPtr if (const float* alteredHeightPtr
= paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2); = paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2))
upAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f; upHeight += *alteredHeightPtr;
} }
} }
if (inCellX > 0) else if (inCellY > 0)
{
leftHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX - 1];
float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY);
leftAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f;
}
if (inCellY > 0)
{ {
upHeight = landShapePointer[(inCellY - 1) * ESM::Land::LAND_SIZE + inCellX]; upHeight = landShapePointer[(inCellY - 1) * ESM::Land::LAND_SIZE + inCellX];
float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1); if (const float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1))
upAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f; upHeight += *alteredHeightPtr;
} }
if (inCellX == ESM::Land::LAND_SIZE - 1) if (inCellX == ESM::Land::LAND_SIZE - 1)
{ {
cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY());
if (isLandLoaded(cellId)) if (isLandLoaded(cellId))
{ {
const CSMWorld::LandHeightsColumn::DataType landRightShapePointer const auto landRightShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
= landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>(); .value<CSMWorld::LandHeightsColumn::DataType>();
rightHeight = landRightShapePointer[inCellY * ESM::Land::LAND_SIZE + 1]; rightHeight = landRightShapePointer[inCellY * ESM::Land::LAND_SIZE + 1];
float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY); if (const float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY))
rightAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f; rightHeight += *alteredHeightPtr;
} }
} }
else if (inCellX < ESM::Land::LAND_SIZE - 1)
{
rightHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX + 1];
if (const float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY))
rightHeight += *alteredHeightPtr;
}
if (inCellY == ESM::Land::LAND_SIZE - 1) if (inCellY == ESM::Land::LAND_SIZE - 1)
{ {
cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1);
if (isLandLoaded(cellId)) if (isLandLoaded(cellId))
{ {
const CSMWorld::LandHeightsColumn::DataType landDownShapePointer const auto landDownShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
= landTable.data(landTable.getModelIndex(cellId, landshapeColumn))
.value<CSMWorld::LandHeightsColumn::DataType>(); .value<CSMWorld::LandHeightsColumn::DataType>();
downHeight = landDownShapePointer[1 * ESM::Land::LAND_SIZE + inCellX]; downHeight = landDownShapePointer[1 * ESM::Land::LAND_SIZE + inCellX];
float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1); if (const float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1))
downAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f; downHeight += *alteredHeightPtr;
} }
} }
if (inCellX < ESM::Land::LAND_SIZE - 1) else if (inCellY < ESM::Land::LAND_SIZE - 1)
{
rightHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX + 1];
float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY);
rightAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f;
}
if (inCellY < ESM::Land::LAND_SIZE - 1)
{ {
downHeight = landShapePointer[(inCellY + 1) * ESM::Land::LAND_SIZE + inCellX]; downHeight = landShapePointer[(inCellY + 1) * ESM::Land::LAND_SIZE + inCellX];
float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1); if (const float* alteredHeightPtr = paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1))
downAlteredHeight = alteredHeightPtr != nullptr ? *alteredHeightPtr : 0.f; downHeight += *alteredHeightPtr;
} }
float averageHeight = (upHeight + downHeight + rightHeight + leftHeight + upAlteredHeight const float averageHeight = (upHeight + downHeight + rightHeight + leftHeight) / 4.f;
+ downAlteredHeight + rightAlteredHeight + leftAlteredHeight) if (thisHeight == averageHeight)
/ 4; return;
if ((thisHeight + thisAlteredHeight) != averageHeight)
mAlteredCells.emplace_back(cellCoords); mAlteredCells.emplace_back(cellCoords);
if (toolStrength > abs(thisHeight + thisAlteredHeight - averageHeight)) const float delta = std::min(static_cast<float>(toolStrength), std::abs(thisHeight - averageHeight));
toolStrength = abs(thisHeight + thisAlteredHeight - averageHeight); alterHeight(cellCoords, inCellX, inCellY, thisHeight < averageHeight ? delta : -delta);
if (thisHeight + thisAlteredHeight > averageHeight)
alterHeight(cellCoords, inCellX, inCellY, -toolStrength);
if (thisHeight + thisAlteredHeight < averageHeight)
alterHeight(cellCoords, inCellX, inCellY, +toolStrength);
}
}
} }
void CSVRender::TerrainShapeMode::flattenHeight( void CSVRender::TerrainShapeMode::flattenHeight(