From 16138fc896002dc3925cdd3775bf49e1996bcecc Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 11 Sep 2019 12:59:15 +0300 Subject: [PATCH 01/46] Transient land shape editing --- CHANGELOG.md | 2 + apps/opencs/CMakeLists.txt | 4 +- apps/opencs/model/prefs/state.cpp | 6 +- apps/opencs/view/render/cell.cpp | 31 +- apps/opencs/view/render/cell.hpp | 12 + .../view/render/pagedworldspacewidget.cpp | 47 +- .../view/render/pagedworldspacewidget.hpp | 10 + apps/opencs/view/render/terrainselection.cpp | 12 +- apps/opencs/view/render/terrainshapemode.cpp | 1156 +++++++++++++++++ apps/opencs/view/render/terrainshapemode.hpp | 157 +++ apps/opencs/view/render/terrainstorage.cpp | 213 +++ apps/opencs/view/render/terrainstorage.hpp | 14 + .../view/render/unpagedworldspacewidget.cpp | 5 + .../view/render/unpagedworldspacewidget.hpp | 3 + apps/opencs/view/render/worldspacewidget.hpp | 3 + .../view/widget/scenetoolshapebrush.cpp | 265 ++++ .../view/widget/scenetoolshapebrush.hpp | 130 ++ components/esmterrain/storage.cpp | 102 -- components/esmterrain/storage.hpp | 124 +- 19 files changed, 2168 insertions(+), 128 deletions(-) create mode 100644 apps/opencs/view/render/terrainshapemode.cpp create mode 100644 apps/opencs/view/render/terrainshapemode.hpp create mode 100644 apps/opencs/view/widget/scenetoolshapebrush.cpp create mode 100644 apps/opencs/view/widget/scenetoolshapebrush.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index d369f2d259..ad8c814ad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,6 +184,8 @@ Feature #4784: Launcher: Duplicate Content Lists Feature #4812: Support NiSwitchNode Feature #4836: Daytime node switch + Feature #4840: Editor: Transient terrain change support + Feature #????: Editor: Land shape editing, land selection Feature #4859: Make water reflections more configurable Feature #4882: Support for NiPalette node Feature #4887: Add openmw command option to set initial random seed diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 00855dad0c..a62dcb42fe 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -82,14 +82,14 @@ opencs_units_noqt (view/world opencs_units (view/widget scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton - scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit + scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit ) opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode actor terrainselection + cellwater terraintexturemode actor terrainselection terrainshapemode ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index bfe907c198..35a8ef2df2 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -170,7 +170,7 @@ void CSMPrefs::State::declare() "list go to the first/last item"); declareCategory ("3D Scene Input"); - + declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0); declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0); declareSeparator(); @@ -178,7 +178,7 @@ void CSMPrefs::State::declare() declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0); declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false); declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0); - declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28); + declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28); declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0); declareSeparator(); @@ -248,6 +248,8 @@ void CSMPrefs::State::declare() addValues (landeditOutsideVisibleCell); declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). setMin (1); + declareInt ("shapebrush-maximumsize", "Maximum texture brush size", 100). + setMin (1); declareBool ("open-list-view", "Open displays list view", false). setTooltip ("When opening a reference from the scene view, it will open the" " instance list view instead of the individual instance record view."); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index a0c408df0f..3915ee4fbd 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand() else { mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, - mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); + mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain)); } mTerrain->loadCell(esmLand.mX, esmLand.mY); @@ -169,6 +169,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st { std::pair result = CSMWorld::CellCoordinates::fromId (id); + mTerrainStorage = new TerrainStorage(mData); + if (result.second) mCoordinates = result.first; @@ -347,6 +349,33 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int return addObjects (start, end); } +void CSVRender::Cell::setAlteredHeight(int inCellX, int inCellY, float height) +{ + mTerrainStorage->setAlteredHeight(inCellX, inCellY, height); + mUpdateLand = true; +} + +float CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) +{ + return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY); +} + +float* CSVRender::Cell::getAlteredHeights() +{ + return mTerrainStorage->getAlteredHeights(); +} + +float* CSVRender::Cell::getAlteredHeight(int inCellX, int inCellY) +{ + return mTerrainStorage->getAlteredHeight(inCellX, inCellY); +} + +void CSVRender::Cell::resetAlteredHeights() +{ + mTerrainStorage->resetHeights(); + mUpdateLand = true; +} + void CSVRender::Cell::pathgridModified() { if (mPathgrid) diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 4446086881..36ac7ff811 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -9,6 +9,7 @@ #include #include "../../model/world/cellcoordinates.hpp" +#include "terrainstorage.hpp" class QModelIndex; @@ -58,6 +59,7 @@ namespace CSVRender int mSubMode; unsigned int mSubModeElementMask; bool mUpdateLand, mLandDeleted; + TerrainStorage *mTerrainStorage; /// Ignored if cell does not have an object with the given ID. /// @@ -118,6 +120,16 @@ namespace CSVRender /// this cell? bool referenceAdded (const QModelIndex& parent, int start, int end); + void setAlteredHeight(int inCellX, int inCellY, float height); + + float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); + + float* getAlteredHeights(); + + float* getAlteredHeight(int inCellX, int inCellY); + + void resetAlteredHeights(); + void pathgridModified(); void pathgridRemoved(); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 540a15dd10..21624740e7 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -25,6 +25,7 @@ #include "cameracontroller.hpp" #include "cellarrow.hpp" #include "terraintexturemode.hpp" +#include "terrainshapemode.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { @@ -137,11 +138,9 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( /// \todo replace EditMode with suitable subclasses tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), - "terrain-shape"); + new TerrainShapeMode (this, mRootNode, tool), "terrain-shape"); tool->addButton ( - new TerrainTextureMode (this, mRootNode, tool), - "terrain-texture"); + new TerrainTextureMode (this, mRootNode, tool), "terrain-texture"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex"); @@ -791,6 +790,46 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& poi return 0; } +CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const +{ + std::map::const_iterator searchResult = mCells.find(coords); + if (searchResult != mCells.end()) + return searchResult->second; + else + return 0; +} + +void CSVRender::PagedWorldspaceWidget::setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height) +{ + std::map::iterator searchResult = mCells.find(coords); + if (searchResult != mCells.end()) searchResult->second->setAlteredHeight(inCellX, inCellY, height); +} + +float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeights(const CSMWorld::CellCoordinates& coords) +{ + std::map::iterator searchResult = mCells.find(coords); + if (searchResult != mCells.end()) return searchResult->second->getAlteredHeights(); + return nullptr; +} + +float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY) +{ + std::map::iterator searchResult = mCells.find(coords); + if (searchResult != mCells.end()) return searchResult->second->getAlteredHeight(inCellX, inCellY); + return nullptr; +} + +void CSVRender::PagedWorldspaceWidget::resetAllAlteredHeights() +{ + std::map::iterator iter (mCells.begin()); + + while (iter!=mCells.end()) + { + iter->second->resetAlteredHeights(); + ++iter; + } +} + std::vector > CSVRender::PagedWorldspaceWidget::getSelection ( unsigned int elementMask) const { diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 6672c2268f..db93e8435b 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -124,6 +124,16 @@ namespace CSVRender virtual Cell* getCell(const osg::Vec3d& point) const; + virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const; + + void setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height); + + float* getCellAlteredHeights(const CSMWorld::CellCoordinates& coords); + + float* getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY); + + void resetAllAlteredHeights(); + virtual std::vector > getSelection (unsigned int elementMask) const; diff --git a/apps/opencs/view/render/terrainselection.cpp b/apps/opencs/view/render/terrainselection.cpp index 225cfc20b6..092688da2d 100644 --- a/apps/opencs/view/render/terrainselection.cpp +++ b/apps/opencs/view/render/terrainselection.cpp @@ -249,13 +249,11 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver int localX = x - cellX * (ESM::Land::LAND_SIZE - 1); int localY = y - cellY * (ESM::Land::LAND_SIZE - 1); - std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY); + CSMWorld::CellCoordinates coords (cellX, cellY); - CSMDoc::Document& document = mWorldspaceWidget->getDocument(); - CSMWorld::IdTable& landTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); - int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); - const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + float landHeight = 0.f; + if (CSVRender::Cell* cell = dynamic_cast(mWorldspaceWidget->getCell(coords))) + landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY); - return mPointer[localY*ESM::Land::LAND_SIZE + localX]; + return landHeight; } diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp new file mode 100644 index 0000000000..aa91256962 --- /dev/null +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -0,0 +1,1156 @@ +#include "terrainshapemode.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "../widget/modebutton.hpp" +#include "../widget/scenetoolbar.hpp" +#include "../widget/scenetoolshapebrush.hpp" + +#include "../../model/doc/document.hpp" +#include "../../model/prefs/state.hpp" +#include "../../model/world/columnbase.hpp" +#include "../../model/world/commandmacro.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/idtree.hpp" +#include "../../model/world/land.hpp" +#include "../../model/world/resourcetable.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/universalid.hpp" + +#include "editmode.hpp" +#include "pagedworldspacewidget.hpp" +#include "mask.hpp" +#include "object.hpp" // Something small needed regarding pointers from here () +#include "terrainselection.hpp" +#include "worldspacewidget.hpp" + +CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), + mBrushSize(0), + mBrushShape(0), + mShapeBrushScenetool(0), + mDragMode(InteractionType_None), + mParentNode(parentNode), + mIsEditing(false), + mTotalDiffY(0), + mShapeEditTool(0), + mShapeEditToolStrength(0), + mTargetHeight(0) +{ +} + +void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) +{ + if (!mTerrainShapeSelection) + { + mTerrainShapeSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Shape)); + } + + if(!mShapeBrushScenetool) + { + mShapeBrushScenetool = new CSVWidget::SceneToolShapeBrush (toolbar, "scenetoolshapebrush", getWorldspaceWidget().getDocument()); + connect(mShapeBrushScenetool, SIGNAL (clicked()), mShapeBrushScenetool, SLOT (activate())); + connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int))); + connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setBrushShape(int))); + connect(mShapeBrushScenetool->mShapeBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int))); + connect(mShapeBrushScenetool->mShapeBrushWindow->mToolSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(setShapeEditTool(int))); + connect(mShapeBrushScenetool->mShapeBrushWindow->mToolStrengthSlider, SIGNAL(valueChanged(int)), this, SLOT(setShapeEditToolStrength(int))); + } + + EditMode::activate(toolbar); + toolbar->addTool (mShapeBrushScenetool); +} + +void CSVRender::TerrainShapeMode::deactivate(CSVWidget::SceneToolbar* toolbar) +{ + if(mShapeBrushScenetool) + { + toolbar->removeTool (mShapeBrushScenetool); + delete mShapeBrushScenetool; + mShapeBrushScenetool = 0; + } + EditMode::deactivate(toolbar); +} + +void CSVRender::TerrainShapeMode::primaryOpenPressed (const WorldspaceHitResult& hit) // Apply changes here +{ +} + +void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult& hit) +{ + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + + if (hit.hit && hit.tag == 0) + { + } + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + paged->resetAllAlteredHeights(); + mTotalDiffY = 0; + } +} + +void CSVRender::TerrainShapeMode::primarySelectPressed(const WorldspaceHitResult& hit) +{ + if(hit.hit && hit.tag == 0) + { + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, false); + } +} + +void CSVRender::TerrainShapeMode::secondarySelectPressed(const WorldspaceHitResult& hit) +{ + if(hit.hit && hit.tag == 0) + { + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, false); + } +} + +bool CSVRender::TerrainShapeMode::primaryEditStartDrag (const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + + mDragMode = InteractionType_PrimaryEdit; + + if (hit.hit && hit.tag == 0) + { + mEditingPos = hit.worldPos; + mIsEditing = true; + if (mShapeEditTool == 4) + { + std::pair vertexCoords = CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos); + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); + int inCellX = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); + int inCellY = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + + mTargetHeight = landShapePointer[inCellY * landSize + inCellX]; + } + } + + return true; +} + +bool CSVRender::TerrainShapeMode::secondaryEditStartDrag (const QPoint& pos) +{ + return false; +} + +bool CSVRender::TerrainShapeMode::primarySelectStartDrag (const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + mDragMode = InteractionType_PrimarySelect; + if (!hit.hit || hit.tag != 0) + { + mDragMode = InteractionType_None; + return false; + } + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true); + return false; +} + +bool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + mDragMode = InteractionType_SecondarySelect; + if (!hit.hit || hit.tag != 0) + { + mDragMode = InteractionType_None; + return false; + } + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true); + return false; +} + +void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) +{ + if (mDragMode == InteractionType_PrimaryEdit) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); + mTotalDiffY += diffY; + if (mIsEditing == true && mShapeEditTool == 0) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true); + if (mIsEditing == true && mShapeEditTool > 0) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + } + + if (mDragMode == InteractionType_PrimarySelect) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + if (hit.hit && hit.tag == 0) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true); + } + + if (mDragMode == InteractionType_SecondarySelect) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + if (hit.hit && hit.tag == 0) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true); + } +} + +void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) +{ + if (mDragMode == InteractionType_PrimaryEdit) + { + if (mIsEditing == true) + { + mTotalDiffY = 0; + mIsEditing = false; + } + + std::sort(mAlteredCells.begin(), mAlteredCells.end()); + std::set removeDuplicates(mAlteredCells.begin(), mAlteredCells.end()); + mAlteredCells.assign(removeDuplicates.begin(), removeDuplicates.end()); + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); + + QUndoStack& undoStack = document.getUndoStack(); + + undoStack.beginMacro ("Edit shape and normal records"); + + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) + { + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); + undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); + for(int i = 0; i < landSize; ++i) + { + for(int j = 0; j < landSize; ++j) + { + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (paged->getCellAlteredHeight(cellCoordinates, i, j)) + landShapeNew[j * landSize + i] = landShapePointer[j * landSize + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); + else + landShapeNew[j * landSize + i] = 0; + } + } + } + if (allowLandShapeEditing(cellId) == true) pushEditToCommand(landShapeNew, document, landTable, cellId); + } + + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) + { + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY()), landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1), landshapeColumn)).value(); + const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value(); + CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer); + + for(int i = 0; i < landSize; ++i) + { + for(int j = 0; j < landSize; ++j) + { + float v1[3]; + float v2[3]; + float normal[3]; + float hyp; + + v1[0] = 128; + v1[1] = 0; + if (i < landSize - 1) v1[2] = landShapePointer[j*landSize+i+1] - landShapePointer[j*landSize+i]; + else + { + bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; + bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; + if (!noLand && !noCell) + v1[2] = landRightShapePointer[j*landSize+1] - landShapePointer[j*landSize+i]; + else + v1[2] = 0; + } + + v2[0] = 0; + v2[1] = 128; + if (j < landSize - 1) v2[2] = landShapePointer[(j+1)*landSize+i] - landShapePointer[j*landSize+i]; + else + { + bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; + bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; + if (!noLand && !noCell) + v2[2] = landDownShapePointer[landSize+i] - landShapePointer[j*landSize+i]; + else + v2[2] = 0; + } + + normal[1] = v1[2]*v2[0] - v1[0]*v2[2]; + normal[0] = v1[1]*v2[2] - v1[2]*v2[1]; + normal[2] = v1[0]*v2[1] - v1[1]*v2[0]; + + hyp = sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]) / 127.0f; + + normal[0] /= hyp; + normal[1] /= hyp; + normal[2] /= hyp; + + landNormalsNew[(j*landSize+i)*3+0] = normal[0]; + landNormalsNew[(j*landSize+i)*3+1] = normal[1]; + landNormalsNew[(j*landSize+i)*3+2] = normal[2]; + } + } + if (allowLandShapeEditing(cellId) == true) pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); + } + undoStack.endMacro(); + mAlteredCells.clear(); + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + paged->resetAllAlteredHeights(); + mTotalDiffY = 0; + } + } +} + + +void CSVRender::TerrainShapeMode::dragAborted() +{ + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + paged->resetAllAlteredHeights(); + mTotalDiffY = 0; + } +} + +void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor) +{ +} + +void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair& vertexCoords, bool dragOperation) +{ + int r = mBrushSize / 2; + + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); + CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (mShapeEditTool == 0) paged->resetAllAlteredHeights(); + } + + if(allowLandShapeEditing(cellId)==true) + { + if (mBrushShape == 0) + { + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); + if (mShapeEditTool == 0) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == 1 || mShapeEditTool == 2) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + } + + if (mBrushShape == 1) + { + for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + { + for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + { + cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); + cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); + if (mShapeEditTool == 0) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == 1 || mShapeEditTool == 2) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + } + } + } + + if (mBrushShape == 2) + { + for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + { + for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + { + cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); + cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int distanceX = abs(i - vertexCoords.first); + int distanceY = abs(j - vertexCoords.second); + int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); + float distancePerRadius = 1.0f * distance / r; + float smoothedByDistance = 0.0f; + if (mShapeEditTool == 0) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (mShapeEditTool == 1 || mShapeEditTool == 2) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (distance < r) + { + if (mShapeEditTool >= 0 && mShapeEditTool < 3) alterHeight(cellCoords, x, y, smoothedByDistance); + if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + } + } + } + } + if (mBrushShape == 3) + { + if(!mCustomBrushShape.empty()) + { + for(auto const& value: mCustomBrushShape) + { + cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second)); + cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second); + if (mShapeEditTool == 0) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == 1 || mShapeEditTool == 2) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + } + } + } + + } +} + +void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool) +{ + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); + std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); + std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); + std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); + std::string cellUpLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() - 1); + std::string cellUpRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() - 1); + std::string cellDownLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() + 1); + std::string cellDownRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() + 1); + + if(allowLandShapeEditing(cellId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords); + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (useTool) + { + if (mShapeEditTool == 0) + { + // Get distance from modified land, alter land change based on zoom + osg::Vec3d eye, center, up; + paged->getCamera()->getViewMatrixAsLookAt(eye, center, up); + osg::Vec3d distance = eye - mEditingPos; + alteredHeight = alteredHeight * (distance.length() / 500); + } + if (mShapeEditTool == 1) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; + if (mShapeEditTool == 2) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) - alteredHeight; + if (mShapeEditTool == 3) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; + } + + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + + // Change values of cornering cells + if (inCellX == 0 && inCellY == 0) + { + if(allowLandShapeEditing(cellUpLeftId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(-1, -1)); + paged->setCellAlteredHeight(cellCoords.move(-1, -1), landSize - 1, landSize - 1, alteredHeight); + } + } + else if (inCellX == 0 && inCellY == landSize - 1) + { + if(allowLandShapeEditing(cellDownLeftId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(-1, 1)); + paged->setCellAlteredHeight(cellCoords.move(-1, 1), landSize - 1, 0, alteredHeight); + } + } + else if (inCellX == landSize - 1 && inCellY == 0) + { + if(allowLandShapeEditing(cellUpRightId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(1, -1)); + paged->setCellAlteredHeight(cellCoords.move(1, -1), 0, landSize - 1, alteredHeight); + } + } + else if (inCellX == landSize - 1 && inCellY == landSize -1) + { + if(allowLandShapeEditing(cellDownRightId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(1, 1)); + paged->setCellAlteredHeight(cellCoords.move(1, 1), 0, 0, alteredHeight); + } + } + + // Change values of edging cells + if (inCellX == 0) + { + if(allowLandShapeEditing(cellLeftId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(-1, 0)); + paged->setCellAlteredHeight(cellCoords.move(-1, 0), landSize - 1, inCellY, alteredHeight); + } + } + if (inCellY == 0) + { + if(allowLandShapeEditing(cellUpId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(0, -1)); + paged->setCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 1, alteredHeight); + } + } + + if (inCellX == landSize - 1) + { + if(allowLandShapeEditing(cellRightId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(1, 0)); + paged->setCellAlteredHeight(cellCoords.move(1, 0), 0, inCellY, alteredHeight); + } + } + if (inCellY == landSize - 1) + { + if(allowLandShapeEditing(cellUpId)==true) + { + if (useTool) mAlteredCells.emplace_back(cellCoords.move(0, 1)); + paged->setCellAlteredHeight(cellCoords.move(0, 1), inCellX, 0, alteredHeight); + } + } + + } + } +} + +void CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength) +{ + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + + // ### Variable naming key ### + // Variables here hold either the real value, or the altered value of current edit. + // this = this Cell + // left = x - 1, up = y - 1, right = x + 1, down = y + 1 + // Altered = transient edit (in current edited) + float thisAlteredHeight = 0.0f; + if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) != nullptr) + thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); + float thisHeight = landShapePointer[inCellY * landSize + inCellX]; + float leftHeight = 0.0f; + float leftAlteredHeight = 0.0f; + float upAlteredHeight = 0.0f; + float rightHeight = 0.0f; + float rightAlteredHeight = 0.0f; + float downHeight = 0.0f; + float downAlteredHeight = 0.0f; + float upHeight = 0.0f; + + if(allowLandShapeEditing(cellId)==true) + { + //Get key values for calculating average, handle cell edges, check for null pointers + if (inCellX == 0) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); + const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + leftHeight = landLeftShapePointer[inCellY * landSize + (landSize - 2)]; + if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), inCellX, landSize - 2)) + leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), landSize - 2, inCellY); + } + if (inCellY == 0) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); + const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + upHeight = landUpShapePointer[(landSize - 2) * landSize + inCellX]; + if (paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 2)) + upAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 2); + } + if (inCellX > 0) + { + leftHeight = landShapePointer[inCellY * landSize + inCellX - 1]; + leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY); + } + if (inCellY > 0) + { + upHeight = landShapePointer[(inCellY - 1) * landSize + inCellX]; + upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1); + } + if (inCellX == landSize - 1) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + rightHeight = landRightShapePointer[inCellY * landSize + 1]; + if (paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY)) + { + rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY); + } + } + if (inCellY == landSize - 1) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + downHeight = landDownShapePointer[1 * landSize + inCellX]; + if (paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1)) + { + downAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1); + } + } + if (inCellX < landSize - 1) + { + rightHeight = landShapePointer[inCellY * landSize + inCellX + 1]; + if(paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY)) + rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY); + } + if (inCellY < landSize - 1) + { + downHeight = landShapePointer[(inCellY + 1) * landSize + inCellX]; + if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1)) + downAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1); + } + + float averageHeight = (upHeight + downHeight + rightHeight + leftHeight + + upAlteredHeight + downAlteredHeight + rightAlteredHeight + leftAlteredHeight) / 4; + if ((thisHeight + thisAlteredHeight) != averageHeight) mAlteredCells.emplace_back(cellCoords); + if (toolStrength > abs(thisHeight + thisAlteredHeight - averageHeight) && toolStrength > 8.0f) toolStrength = + abs(thisHeight + thisAlteredHeight - averageHeight); //Cut down excessive changes + if (thisHeight + thisAlteredHeight > averageHeight) alterHeight(cellCoords, inCellX, inCellY, -toolStrength); + if (thisHeight + thisAlteredHeight < averageHeight) alterHeight(cellCoords, inCellX, inCellY, +toolStrength); + } + } +} + +void CSVRender::TerrainShapeMode::flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + + float thisHeight = 0.0f; + float thisAlteredHeight = 0.0f; + + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + bool noCell = document.getData().getCells().searchId (cellId) == -1; + bool noLand = document.getData().getLand().searchId (cellId) == -1; + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (!noCell && !noLand) + { + const CSMWorld::LandHeightsColumn::DataType landShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + + if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY)) + thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); + thisHeight = landShapePointer[inCellY * landSize + inCellX]; + } + } + + if (toolStrength > abs(thisHeight - targetHeight) && toolStrength > 8.0f) toolStrength = + abs(thisHeight - targetHeight); //Cut down excessive changes + if (thisHeight + thisAlteredHeight > targetHeight) alterHeight(cellCoords, inCellX, inCellY, -toolStrength); + if (thisHeight + thisAlteredHeight < targetHeight) alterHeight(cellCoords, inCellX, inCellY, +toolStrength); +} + +void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight, + float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); + std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); + std::string cellUpLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() - 1); + bool noCell = document.getData().getCells().searchId (cellId) == -1; + bool noLand = document.getData().getLand().searchId (cellId) == -1; + bool noLeftCell = document.getData().getCells().searchId (cellLeftId) == -1; + bool noLeftLand = document.getData().getLand().searchId (cellLeftId) == -1; + bool noUpCell = document.getData().getCells().searchId (cellUpId) == -1; + bool noUpLand = document.getData().getLand().searchId (cellUpId) == -1; + + *thisHeight = 0.0f; // real + altered height + *thisAlteredHeight = 0.0f; // only altered height + *leftHeight = 0.0f; + *leftAlteredHeight = 0.0f; + *upHeight = 0.0f; + *upAlteredHeight = 0.0f; + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (!noCell && !noLand) + { + const CSMWorld::LandHeightsColumn::DataType landShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + + if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY)) + *thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); + *thisHeight = landShapePointer[inCellY * landSize + inCellX] + *thisAlteredHeight; + + // In case of cell edge, and touching cell/land is not found, assume that left and up -heights would be the same + // This is to prevent unnecessary action at limitHeightChange() + *leftHeight = *thisHeight; + *upHeight = *thisHeight; + + if (inCellX == 0) + { + if(!noLeftCell && !noLeftLand) + { + const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = + landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); + *leftHeight = landLeftShapePointer[inCellY * landSize + (landSize - 2)]; + if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), landSize - 2, inCellY)) + { + *leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), landSize - 2, inCellY); + *leftHeight += *leftAlteredHeight; + } + } + } + if (inCellY == 0) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); + if(!noUpCell && !noUpLand) + { + const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = + landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); + *upHeight = landUpShapePointer[(landSize - 2) * landSize + inCellX]; + if (paged->getCellAlteredHeight(cellCoords.move(0,-1), inCellX, landSize - 2)) + { + *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 2); + *upHeight += *upAlteredHeight; + } + } + } + if (inCellX != 0) + { + *leftHeight = landShapePointer[inCellY * landSize + inCellX - 1]; + if (paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY)) + *leftAlteredHeight += *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY); + *leftHeight += *leftAlteredHeight; + } + if (inCellY != 0) + { + *upHeight = landShapePointer[(inCellY - 1) * landSize + inCellX]; + if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1)) + *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1); + *upHeight += *upAlteredHeight; + } + } + } +} + +void CSVRender::TerrainShapeMode::fixEdges(const CSMWorld::CellCoordinates& cellCoords) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); + std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); + std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); + std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); + if (allowLandShapeEditing(cellId) == true) + { + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); + CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); + for(int i = 0; i < landSize; ++i) + { + if(document.getData().getCells().searchId (cellLeftId) != -1 && document.getData().getLand().searchId (cellLeftId) != -1 && landShapePointer[i * landSize] != landLeftShapePointer[i * landSize + landSize - 1]) landShapeNew[i * landSize] = landLeftShapePointer[i * landSize + landSize - 1]; + if(document.getData().getCells().searchId (cellRightId) != -1 && document.getData().getLand().searchId (cellRightId) != -1 && landShapePointer[i * landSize + landSize - 1] != landRightShapePointer[i * landSize]) landShapeNew[i * landSize + landSize - 1] = landRightShapePointer[i * landSize]; + if(document.getData().getCells().searchId (cellUpId) != -1 && document.getData().getLand().searchId (cellUpId) != -1 && landShapePointer[i] != landUpShapePointer[(landSize - 1) * landSize + i]) landShapeNew[i] = landUpShapePointer[(landSize - 1) * landSize + i]; + if(document.getData().getCells().searchId (cellDownId) != -1 && document.getData().getLand().searchId (cellDownId) != -1 && landShapePointer[(landSize - 1) * landSize + i] != landDownShapePointer[i]) landShapeNew[(landSize - 1) * landSize + i] = landDownShapePointer[i]; + } + pushEditToCommand(landShapeNew, document, landTable, cellId); + } +} + +void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + + int limitHeightChange = 1024.0f; // Limited by save format + bool noCell = document.getData().getCells().searchId (cellId) == -1; + bool noLand = document.getData().getLand().searchId (cellId) == -1; + bool dataAlteredAtThisCell = false; + int maxPasses = 5; //Multiple passes are needed if there are consecutive height differences over the limit + + if (!noCell && !noLand) + { + const CSMWorld::LandHeightsColumn::DataType landShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + + for (int passes = 0; passes < maxPasses; ++passes) + { + for(int inCellX = 0; inCellX < landSize; ++inCellX) + { + for(int inCellY = 0; inCellY < landSize; ++inCellY) + { + // ### Variable naming key ### + // Variables here aim to hold the final value, which is (real_value + altered_value) + // this = this Cell + // left = x - 1, up = y - 1 + // Altered = transient edit (in current edited) + // Binding = the last row or column, the one that binds cell land together + + float thisHeight = 0.0f; + float thisAlteredHeight = 0.0f; + float leftHeight = 0.0f; + float leftAlteredHeight = 0.0f; + float upHeight = 0.0f; + float upAlteredHeight = 0.0f; + + updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight, + &upHeight, &upAlteredHeight); + + bool doChange = false; + + // Check for height limits, prioritize left over up, except in left-right -cell edges + if (thisHeight - upHeight >= limitHeightChange) + { + thisAlteredHeight = upHeight + (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + if (thisHeight - upHeight <= -limitHeightChange) + { + thisAlteredHeight = upHeight - (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + if (thisHeight - leftHeight >= limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) + { + thisAlteredHeight = leftHeight + (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + if (thisHeight - leftHeight <= -limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) + { + thisAlteredHeight = leftHeight - (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + + // Apply limits + if (doChange == true) + { + alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight, false); + dataAlteredAtThisCell = true; + } + } + } + + //Skip unneeded extra passes + if (dataAlteredAtThisCell == false) + { + passes = maxPasses; + continue; + } + + //Inverse check (implement properly after default check works) + for(int inCellX = landSize - 1; inCellX >= 0; --inCellX) + { + for(int inCellY = landSize - 1; inCellY >= 0; --inCellY) + { + // ### Variable naming key ### + // Variables here aim to hold the final value, which is (real_value + altered_value) + // this = this Cell + // left = x - 1, up = y - 1 + // Altered = transient edit (in current edited) + // Binding = the last row or column, the one that binds cell land together + + float thisHeight = 0.0f; + float thisAlteredHeight = 0.0f; + float leftHeight = 0.0f; + float leftAlteredHeight = 0.0f; + float upHeight = 0.0f; + float upAlteredHeight = 0.0f; + + updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight, + &upHeight, &upAlteredHeight); + + bool doChange = false; + + // Check for height limits, prioritize left over up, except in left-right -cell edges + if (thisHeight - upHeight >= limitHeightChange) + { + thisAlteredHeight = upHeight + limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + if (thisHeight - upHeight <= -limitHeightChange) + { + thisAlteredHeight = upHeight - limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + if (thisHeight - leftHeight >= limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) + { + thisAlteredHeight = leftHeight + limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + if (thisHeight - leftHeight <= -limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) + { + thisAlteredHeight = leftHeight - limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; + doChange = true; + } + + // Apply limits + if (doChange == true) + { + alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight, false); + dataAlteredAtThisCell = true; + } + } + } + } + } +} + +void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& vertexCoords, unsigned char selectMode, bool dragOperation) +{ + int r = mBrushSize / 2; + std::vector> selections; + + if (mBrushShape == 0) + { + selections.emplace_back(vertexCoords); + } + + if (mBrushShape == 1) + { + for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + { + for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + { + selections.emplace_back(std::make_pair(i, j)); + } + } + } + + if (mBrushShape == 2) + { + for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + { + for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + { + int distanceX = abs(i - vertexCoords.first); + int distanceY = abs(j - vertexCoords.second); + int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + if (distance < r) selections.emplace_back(std::make_pair(i, j)); + } + } + } + + if (mBrushShape == 3) + { + if(!mCustomBrushShape.empty()) + { + for(auto const& value: mCustomBrushShape) + { + selections.emplace_back(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second)); + } + } + } + + if(selectMode == 0) mTerrainShapeSelection->onlySelect(selections); + if(selectMode == 1) mTerrainShapeSelection->toggleSelect(selections, dragOperation); + +} + +void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, std::string cellId) +{ + QVariant changedLand; + changedLand.setValue(newLandGrid); + + QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandHeightsIndex))); + + QUndoStack& undoStack = document.getUndoStack(); + undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); +} + +void CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, std::string cellId) +{ + QVariant changedLand; + changedLand.setValue(newLandGrid); + + QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandNormalsIndex))); + + QUndoStack& undoStack = document.getUndoStack(); + undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); +} + +bool CSVRender::TerrainShapeMode::allowLandShapeEditing(std::string cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTree& cellTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + bool noCell = document.getData().getCells().searchId (cellId)==-1; + bool noLand = document.getData().getLand().searchId (cellId)==-1; + + if (noCell) + { + std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); + + // target cell does not exist + if (mode=="Discard") + return false; + + if (mode=="Create cell and land, then edit") + { + std::unique_ptr createCommand ( + new CSMWorld::CreateCommand (cellTable, cellId)); + int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); + int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior); + createCommand->addNestedValue (parentIndex, index, false); + document.getUndoStack().push (createCommand.release()); + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + } + } + else if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first)) + { + // target cell exists, but is not shown + std::string mode = + CSMPrefs::get()["3D Scene Editing"]["outside-visible-landedit"].toString(); + + if (mode=="Discard") + return false; + + if (mode=="Show cell and edit") + { + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + } + } + + if (noLand) + { + std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); + + // target cell does not exist + if (mode=="Discard") + return false; + + if (mode=="Create cell and land, then edit") + { + document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId)); + fixEdges(CSMWorld::CellCoordinates::fromId (cellId).first); + } + } + + return true; +} + +void CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event) +{ +} + +void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) +{ + mBrushSize = brushSize; +} + +void CSVRender::TerrainShapeMode::setBrushShape(int brushShape) +{ + mBrushShape = brushShape; + + //Set custom brush shape + if (mBrushShape == 3 && !mTerrainShapeSelection->getTerrainSelection().empty()) + { + auto terrainSelection = mTerrainShapeSelection->getTerrainSelection(); + int selectionCenterX = 0; + int selectionCenterY = 0; + int selectionAmount = 0; + + for(auto const& value: terrainSelection) + { + selectionCenterX = selectionCenterX + value.first; + selectionCenterY = selectionCenterY + value.second; + ++selectionAmount; + } + selectionCenterX = selectionCenterX / selectionAmount; + selectionCenterY = selectionCenterY / selectionAmount; + + mCustomBrushShape.clear(); + std::pair differentialPos {}; + for(auto const& value: terrainSelection) + { + differentialPos.first = value.first - selectionCenterX; + differentialPos.second = value.second - selectionCenterY; + mCustomBrushShape.push_back(differentialPos); + } + } +} + +void CSVRender::TerrainShapeMode::setShapeEditTool(int shapeEditTool) +{ + mShapeEditTool = shapeEditTool; +} + +void CSVRender::TerrainShapeMode::setShapeEditToolStrength(int shapeEditToolStrength) +{ + mShapeEditToolStrength = shapeEditToolStrength; +} + +CSVRender::PagedWorldspaceWidget& CSVRender::TerrainShapeMode::getPagedWorldspaceWidget() +{ + return dynamic_cast(getWorldspaceWidget()); +} diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp new file mode 100644 index 0000000000..f910b9ecdd --- /dev/null +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -0,0 +1,157 @@ +#ifndef CSV_RENDER_TERRAINSHAPEMODE_H +#define CSV_RENDER_TERRAINSHAPEMODE_H + +#include "editmode.hpp" + +#include +#include + +#include +#include + +#ifndef Q_MOC_RUN +#include "../../model/world/data.hpp" +#include "../../model/world/land.hpp" + +#include "../../model/doc/document.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/landtexture.hpp" +#endif + +#include "terrainselection.hpp" + +namespace CSVWidget +{ + class SceneToolShapeBrush; +} + +namespace CSVRender +{ + class PagedWorldspaceWidget; + + /// \brief EditMode for handling the terrain shape editing + class TerrainShapeMode : public EditMode + { + Q_OBJECT + + public: + + enum InteractionType + { + InteractionType_PrimaryEdit, + InteractionType_PrimarySelect, + InteractionType_SecondaryEdit, + InteractionType_SecondarySelect, + InteractionType_None + }; + + /// Editmode for terrain shape grid + TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); + + void primaryOpenPressed (const WorldspaceHitResult& hit); + + /// Create single command for one-click shape editing + void primaryEditPressed (const WorldspaceHitResult& hit); + + /// Open brush settings window + void primarySelectPressed(const WorldspaceHitResult&); + + void secondarySelectPressed(const WorldspaceHitResult&); + + void activate(CSVWidget::SceneToolbar*); + void deactivate(CSVWidget::SceneToolbar*); + + /// Start shape editing command macro + virtual bool primaryEditStartDrag (const QPoint& pos); + + virtual bool secondaryEditStartDrag (const QPoint& pos); + virtual bool primarySelectStartDrag (const QPoint& pos); + virtual bool secondarySelectStartDrag (const QPoint& pos); + + /// Handle shape edit behavior during dragging + virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); + + /// End shape editing command macro + virtual void dragCompleted(const QPoint& pos); + + /// Cancel shape editing, and reset all pending changes + virtual void dragAborted(); + + virtual void dragWheel (int diff, double speedFactor); + virtual void dragMoveEvent (QDragMoveEvent *event); + + /// Handle brush mechanics for shape editing + void editTerrainShapeGrid (const std::pair& vertexCoords, bool dragOperation); + + /// 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); + + /// Do a single smoothing height alteration for transient shape edit map + void smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength); + + /// Do a single flattening height alteration for transient shape edit map + void flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight); + + /// Update the key values used in height calculations + void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight, + float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight); + + /// Bind edge vertices to next cells + void fixEdges(const CSMWorld::CellCoordinates& cellCoords); + + /// Check that the edit doesn't break save format limits, fix if necessary + void limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords); + + /// Handle brush mechanics for terrain shape selection + void selectTerrainShapes (const std::pair& vertexCoords, unsigned char selectMode, bool dragOperation); + + /// Push terrain shape edits to command macro + void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, std::string cellId); + + /// Push land normals edits to command macro + void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, std::string cellId); + + /// Create new cell and land if needed + bool allowLandShapeEditing(std::string textureFileName); + + private: + std::string mCellId; + std::string mBrushTexture; + int mBrushSize; + int mBrushShape; + std::vector> mCustomBrushShape; + CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool; + int mDragMode; + osg::Group* mParentNode; + bool mIsEditing; + std::unique_ptr mTerrainShapeSelection; + int mTotalDiffY; + std::vector mAlteredCells; + osg::Vec3d mEditingPos; + int mShapeEditTool; + int mShapeEditToolStrength; + int mTargetHeight; + + const int cellSize {ESM::Land::REAL_SIZE}; + const int landSize {ESM::Land::LAND_SIZE}; + const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; + + PagedWorldspaceWidget& getPagedWorldspaceWidget(); + + signals: + void passBrushTexture(std::string brushTexture); + + public slots: + //void handleDropEvent(QDropEvent *event); + void setBrushSize(int brushSize); + void setBrushShape(int brushShape); + void setShapeEditTool(int shapeEditTool); + void setShapeEditToolStrength(int shapeEditToolStrength); + }; +} + + +#endif diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index e0edae7743..774c204edf 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -1,15 +1,25 @@ #include "terrainstorage.hpp" +#include +#include + #include "../../model/world/land.hpp" #include "../../model/world/landtexture.hpp" +#include +#include +#include +#include + namespace CSVRender { + const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; TerrainStorage::TerrainStorage(const CSMWorld::Data &data) : ESMTerrain::Storage(data.getResourceSystem()->getVFS()) , mData(data) { + resetHeights(); } osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) @@ -33,6 +43,209 @@ namespace CSVRender return &mData.getLandTextures().getRecord(row).get(); } + void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height) + { + mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] = height - fmod(height, 8); //Limit to divisible by 8 to avoid cell seam breakage + } + + void TerrainStorage::resetHeights() + { + for (int x = 0; x < ESM::Land::LAND_SIZE; ++x) + { + for (int y = 0; y < ESM::Land::LAND_SIZE; ++y) + { + mAlteredHeight[y*ESM::Land::LAND_SIZE + x] = 0; + } + } + } + + float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) + { + float height = 0.f; + osg::ref_ptr land = getLand (cellX, cellY); + if (land) + { + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; + if (data) height = getVertexHeight(data, inCellX, inCellY); + } + else return height; + return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height; + + } + + float* TerrainStorage::getAlteredHeights() + { + return mAlteredHeight; + } + + float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY) + { + return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX]; + } + + void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, + osg::ref_ptr positions, + osg::ref_ptr normals, + osg::ref_ptr colours) + { + // LOD level n means every 2^n-th vertex is kept + size_t increment = static_cast(1) << lodLevel; + + osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); + + int startCellX = static_cast(std::floor(origin.x())); + int startCellY = static_cast(std::floor(origin.y())); + + size_t numVerts = static_cast(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); + + positions->resize(numVerts*numVerts); + normals->resize(numVerts*numVerts); + colours->resize(numVerts*numVerts); + + osg::Vec3f normal; + osg::Vec4ub color; + + float vertY = 0; + float vertX = 0; + + ESMTerrain::LandCache cache; + + float vertY_ = 0; // of current cell corner + for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) + { + float vertX_ = 0; // of current cell corner + for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) + { + const ESMTerrain::LandObject* land = ESMTerrain::Storage::getLand(cellX, cellY, cache); + const ESM::Land::LandData *heightData = 0; + const ESM::Land::LandData *normalData = 0; + const ESM::Land::LandData *colourData = 0; + if (land) + { + heightData = land->getData(ESM::Land::DATA_VHGT); + normalData = land->getData(ESM::Land::DATA_VNML); + colourData = land->getData(ESM::Land::DATA_VCLR); + } + + int rowStart = 0; + int colStart = 0; + // Skip the first row / column unless we're at a chunk edge, + // since this row / column is already contained in a previous cell + // This is only relevant if we're creating a chunk spanning multiple cells + if (vertY_ != 0) + colStart += increment; + if (vertX_ != 0) + rowStart += increment; + + // Only relevant for chunks smaller than (contained in) one cell + rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; + colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; + int rowEnd = std::min(static_cast(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast(ESM::Land::LAND_SIZE)); + int colEnd = std::min(static_cast(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast(ESM::Land::LAND_SIZE)); + + vertY = vertY_; + for (int col=colStart; col= 0 && row < ESM::Land::LAND_SIZE); + assert(col >= 0 && col < ESM::Land::LAND_SIZE); + + assert (vertX < numVerts); + assert (vertY < numVerts); + + float height = defaultHeight; + if (heightData) + height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; + + (*positions)[static_cast(vertX*numVerts + vertY)] + = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, + (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, + height + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)]); + + if (normalData) + { + for (int i=0; i<3; ++i) + normal[i] = normalData->mNormals[srcArrayIndex+i]; + + normal.normalize(); + } + else + normal = osg::Vec3f(0,0,1); + + // Normals apparently don't connect seamlessly between cells + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixNormal(normal, cellX, cellY, col, row, cache); + + // some corner normals appear to be complete garbage (z < 0) + if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) + averageNormal(normal, cellX, cellY, col, row, cache); + + assert(normal.z() > 0); + + (*normals)[static_cast(vertX*numVerts + vertY)] = normal; + + if (colourData) + { + for (int i=0; i<3; ++i) + color[i] = colourData->mColours[srcArrayIndex+i]; + } + else + { + color.r() = 255; + color.g() = 255; + color.b() = 255; + } + + // Highlight broken height changes + if ( ((col > 0 && row > 0) && + ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || + abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 )) || + ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && + ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || + abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 ))) + { + color.r() = 255; + color.g() = 0; + color.b() = 0; + } + + // Unlike normals, colors mostly connect seamlessly between cells, but not always... + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixColour(color, cellX, cellY, col, row, cache); + + color.a() = 255; + + (*colours)[static_cast(vertX*numVerts + vertY)] = color; + + ++vertX; + } + ++vertY; + } + vertX_ = vertX; + } + vertY_ = vertY; + + assert(vertX_ == numVerts); // Ensure we covered whole area + } + assert(vertY_ == numVerts); // Ensure we covered whole area*/ + } + void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) { // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 6c3151e8d3..4740f13bba 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -7,6 +7,7 @@ namespace CSVRender { + class LandCache; /** * @brief A bridge between the terrain component and OpenCS's terrain data storage. @@ -15,12 +16,25 @@ namespace CSVRender { public: TerrainStorage(const CSMWorld::Data& data); + float mAlteredHeight[ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE]; + void setAlteredHeight(int inCellX, int inCellY, float heightMap); + void resetHeights(); + float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); + float* getAlteredHeights(); + float* getAlteredHeight(int inCellX, int inCellY); + private: const CSMWorld::Data& mData; virtual osg::ref_ptr getLand (int cellX, int cellY) override; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; + /// Draws temporarily altered land (transient change support) + void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, + osg::ref_ptr positions, + osg::ref_ptr normals, + osg::ref_ptr colours) override; + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; }; diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index a3f0636be2..b1088aa60e 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -150,6 +150,11 @@ CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& p return mCell.get(); } +CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const +{ + return mCell.get(); +} + std::vector > CSVRender::UnpagedWorldspaceWidget::getSelection ( unsigned int elementMask) const { diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index 5274639903..d169220f95 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -17,6 +17,7 @@ namespace CSMDoc namespace CSMWorld { class IdTable; + class CellCoordinates; } namespace CSVRender @@ -63,6 +64,8 @@ namespace CSVRender virtual Cell* getCell(const osg::Vec3d& point) const; + virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const; + virtual std::vector > getSelection (unsigned int elementMask) const; diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 06c182b0c5..a80032b82b 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -17,6 +17,7 @@ namespace CSMPrefs namespace CSMWorld { + class CellCoordinates; class UniversalId; } @@ -170,6 +171,8 @@ namespace CSVRender /// \note Returns the cell if it exists, otherwise a null pointer virtual Cell* getCell(const osg::Vec3d& point) const = 0; + virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const = 0; + virtual std::vector > getSelection (unsigned int elementMask) const = 0; diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp new file mode 100644 index 0000000000..a9f494bf38 --- /dev/null +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -0,0 +1,265 @@ +#include "scenetoolshapebrush.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scenetool.hpp" + +#include "../../model/doc/document.hpp" +#include "../../model/prefs/state.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idcollection.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/landtexture.hpp" +#include "../../model/world/universalid.hpp" + + +CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent) + : QGroupBox(title, parent) +{ + mBrushSizeSlider = new QSlider(Qt::Horizontal); + mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); + mBrushSizeSlider->setTickInterval(10); + mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mBrushSizeSlider->setSingleStep(1); + + mBrushSizeSpinBox = new QSpinBox; + mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mBrushSizeSpinBox->setSingleStep(1); + + mLayoutSliderSize = new QHBoxLayout; + mLayoutSliderSize->addWidget(mBrushSizeSlider); + mLayoutSliderSize->addWidget(mBrushSizeSpinBox); + + connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int))); + connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int))); + + setLayout(mLayoutSliderSize); +} + +CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent) + : QFrame(parent, Qt::Popup), + mBrushShape(0), + mBrushSize(0), + mDocument(document) +{ + mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this); + mButtonSquare = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-square")), "", this); + mButtonCircle = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-circle")), "", this); + mButtonCustom = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-custom")), "", this); + + mSizeSliders = new ShapeBrushSizeControls("Brush size", this); + + QVBoxLayout *layoutMain = new QVBoxLayout; + layoutMain->setSpacing(0); + layoutMain->setContentsMargins(4,0,4,4); + + QHBoxLayout *layoutHorizontal = new QHBoxLayout; + layoutHorizontal->setSpacing(0); + layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0)); + + configureButtonInitialSettings(mButtonPoint); + configureButtonInitialSettings(mButtonSquare); + configureButtonInitialSettings(mButtonCircle); + configureButtonInitialSettings(mButtonCustom); + + mButtonPoint->setToolTip (toolTipPoint); + mButtonSquare->setToolTip (toolTipSquare); + mButtonCircle->setToolTip (toolTipCircle); + mButtonCustom->setToolTip (toolTipCustom); + + QButtonGroup* brushButtonGroup = new QButtonGroup(this); + brushButtonGroup->addButton(mButtonPoint); + brushButtonGroup->addButton(mButtonSquare); + brushButtonGroup->addButton(mButtonCircle); + brushButtonGroup->addButton(mButtonCustom); + + brushButtonGroup->setExclusive(true); + + layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop); + + mHorizontalGroupBox = new QGroupBox(tr("")); + mHorizontalGroupBox->setLayout(layoutHorizontal); + + mToolSelector = new QComboBox(this); + mToolSelector->addItem(tr("Height (drag)")); + mToolSelector->addItem(tr("Height, raise (paint)")); + mToolSelector->addItem(tr("Height, lower (paint)")); + mToolSelector->addItem(tr("Smooth (paint)")); + mToolSelector->addItem(tr("Flatten (paint)")); + + QLabel *brushStrengthLabel = new QLabel(this); + brushStrengthLabel->setText("Brush strength:"); + + mToolStrengthSlider = new QSlider(Qt::Horizontal); + mToolStrengthSlider->setTickPosition(QSlider::TicksBothSides); + mToolStrengthSlider->setTickInterval(8); + mToolStrengthSlider->setRange(8, 128); + mToolStrengthSlider->setSingleStep(8); + + layoutMain->addWidget(mHorizontalGroupBox); + layoutMain->addWidget(mSizeSliders); + layoutMain->addWidget(mToolSelector); + layoutMain->addWidget(brushStrengthLabel); + layoutMain->addWidget(mToolStrengthSlider); + + setLayout(layoutMain); + + connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape())); + connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape())); + connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape())); + connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape())); +} + +void CSVWidget::ShapeBrushWindow::configureButtonInitialSettings(QPushButton *button) +{ + button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + button->setContentsMargins (QMargins (0, 0, 0, 0)); + button->setIconSize (QSize (48-6, 48-6)); + button->setFixedSize (48, 48); + button->setCheckable(true); +} + +void CSVWidget::ShapeBrushWindow::setBrushSize(int brushSize) +{ + mBrushSize = brushSize; + emit passBrushSize(mBrushSize); +} + +void CSVWidget::ShapeBrushWindow::setBrushShape() +{ + if(mButtonPoint->isChecked()) mBrushShape = 0; + if(mButtonSquare->isChecked()) mBrushShape = 1; + if(mButtonCircle->isChecked()) mBrushShape = 2; + if(mButtonCustom->isChecked()) mBrushShape = 3; + emit passBrushShape(mBrushShape); +} + +void CSVWidget::SceneToolShapeBrush::adjustToolTips() +{ +} + +CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document) +: SceneTool (parent, Type_TopAction), + mToolTip (toolTip), + mDocument (document), + mShapeBrushWindow(new ShapeBrushWindow(document, this)) +{ + setAcceptDrops(true); + connect(mShapeBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int))); + setButtonIcon(mShapeBrushWindow->mBrushShape); + + mPanel = new QFrame (this, Qt::Popup); + + QHBoxLayout *layout = new QHBoxLayout (mPanel); + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + mTable = new QTableWidget (0, 2, this); + + mTable->setShowGrid (true); + mTable->verticalHeader()->hide(); + mTable->horizontalHeader()->hide(); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); + mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); +#else + mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); + mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); +#endif + mTable->setSelectionMode (QAbstractItemView::NoSelection); + + layout->addWidget (mTable); + + connect (mTable, SIGNAL (clicked (const QModelIndex&)), + this, SLOT (clicked (const QModelIndex&))); + +} + +void CSVWidget::SceneToolShapeBrush::setButtonIcon (int brushShape) +{ + QString tooltip = "Change brush settings

Currently selected: "; + + switch (brushShape) + { + case 0: + + setIcon (QIcon (QPixmap (":scenetoolbar/brush-point"))); + tooltip += mShapeBrushWindow->toolTipPoint; + break; + + case 1: + + setIcon (QIcon (QPixmap (":scenetoolbar/brush-square"))); + tooltip += mShapeBrushWindow->toolTipSquare; + break; + + case 2: + + setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle"))); + tooltip += mShapeBrushWindow->toolTipCircle; + break; + + case 3: + + setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom"))); + tooltip += mShapeBrushWindow->toolTipCustom; + break; + } + + setToolTip (tooltip); +} + +void CSVWidget::SceneToolShapeBrush::showPanel (const QPoint& position) +{ +} + +void CSVWidget::SceneToolShapeBrush::updatePanel () +{ +} + +void CSVWidget::SceneToolShapeBrush::clicked (const QModelIndex& index) +{ +} + +void CSVWidget::SceneToolShapeBrush::activate () +{ + QPoint position = QCursor::pos(); + mShapeBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mShapeBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mShapeBrushWindow->move (position); + mShapeBrushWindow->show(); +} + +void CSVWidget::SceneToolShapeBrush::dragEnterEvent (QDragEnterEvent *event) +{ + emit passEvent(event); + event->accept(); +} +void CSVWidget::SceneToolShapeBrush::dropEvent (QDropEvent *event) +{ + emit passEvent(event); + event->accept(); +} diff --git a/apps/opencs/view/widget/scenetoolshapebrush.hpp b/apps/opencs/view/widget/scenetoolshapebrush.hpp new file mode 100644 index 0000000000..9048fa5364 --- /dev/null +++ b/apps/opencs/view/widget/scenetoolshapebrush.hpp @@ -0,0 +1,130 @@ +#ifndef CSV_WIDGET_SCENETOOLSHAPEBRUSH_H +#define CSV_WIDGET_SCENETOOLSHAPEBRUSH_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_MOC_RUN +#include "scenetool.hpp" + +#include "../../model/doc/document.hpp" +#endif + +class QTableWidget; + +namespace CSVRender +{ + class TerrainShapeMode; +} + +namespace CSVWidget +{ + class SceneToolShapeBrush; + + /// \brief Layout-box for some brush button settings + class ShapeBrushSizeControls : public QGroupBox + { + Q_OBJECT + + public: + ShapeBrushSizeControls(const QString &title, QWidget *parent); + + private: + QHBoxLayout *mLayoutSliderSize; + QSlider *mBrushSizeSlider; + QSpinBox *mBrushSizeSpinBox; + + friend class SceneToolShapeBrush; + friend class CSVRender::TerrainShapeMode; + }; + + class SceneToolShapeBrush; + + /// \brief Brush settings window + class ShapeBrushWindow : public QFrame + { + Q_OBJECT + + public: + ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent = 0); + void configureButtonInitialSettings(QPushButton *button); + + const QString toolTipPoint = "Paint single point"; + const QString toolTipSquare = "Paint with square brush"; + const QString toolTipCircle = "Paint with circle brush"; + const QString toolTipCustom = "Paint custom selection (not implemented yet)"; + + private: + int mBrushShape; + int mBrushSize; + CSMDoc::Document& mDocument; + QGroupBox *mHorizontalGroupBox; + QComboBox *mToolSelector; + QSlider *mToolStrengthSlider; + QPushButton *mButtonPoint; + QPushButton *mButtonSquare; + QPushButton *mButtonCircle; + QPushButton *mButtonCustom; + ShapeBrushSizeControls* mSizeSliders; + + friend class SceneToolShapeBrush; + friend class CSVRender::TerrainShapeMode; + + public slots: + void setBrushShape(); + void setBrushSize(int brushSize); + + signals: + void passBrushSize (int brushSize); + void passBrushShape(int brushShape); + }; + + class SceneToolShapeBrush : public SceneTool + { + Q_OBJECT + + QString mToolTip; + CSMDoc::Document& mDocument; + QFrame *mPanel; + QTableWidget *mTable; + ShapeBrushWindow *mShapeBrushWindow; + + private: + + void adjustToolTips(); + + public: + + SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document); + + virtual void showPanel (const QPoint& position); + void updatePanel (); + + void dropEvent (QDropEvent *event); + void dragEnterEvent (QDragEnterEvent *event); + + friend class CSVRender::TerrainShapeMode; + + public slots: + void setButtonIcon(int brushShape); + void clicked (const QModelIndex& index); + virtual void activate(); + + signals: + void passEvent(QDropEvent *event); + void passEvent(QDragEnterEvent *event); + }; +} + +#endif diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index c8d31c41c6..151f0ff91b 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,13 +16,6 @@ namespace ESMTerrain { - class LandCache - { - public: - typedef std::map, osg::ref_ptr > Map; - Map mMap; - }; - LandObject::LandObject() : mLand(nullptr) , mLoadFlags(0) @@ -98,82 +91,6 @@ namespace ESMTerrain return false; } - void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) - { - while (col >= ESM::Land::LAND_SIZE-1) - { - ++cellY; - col -= ESM::Land::LAND_SIZE-1; - } - while (row >= ESM::Land::LAND_SIZE-1) - { - ++cellX; - row -= ESM::Land::LAND_SIZE-1; - } - while (col < 0) - { - --cellY; - col += ESM::Land::LAND_SIZE-1; - } - while (row < 0) - { - --cellX; - row += ESM::Land::LAND_SIZE-1; - } - - const LandObject* land = getLand(cellX, cellY, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; - if (data) - { - normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalize(); - } - else - normal = osg::Vec3f(0,0,1); - } - - void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row, LandCache& cache) - { - osg::Vec3f n1,n2,n3,n4; - fixNormal(n1, cellX, cellY, col+1, row, cache); - fixNormal(n2, cellX, cellY, col-1, row, cache); - fixNormal(n3, cellX, cellY, col, row+1, cache); - fixNormal(n4, cellX, cellY, col, row-1, cache); - normal = (n1+n2+n3+n4); - normal.normalize(); - } - - void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) - { - if (col == ESM::Land::LAND_SIZE-1) - { - ++cellY; - col = 0; - } - if (row == ESM::Land::LAND_SIZE-1) - { - ++cellX; - row = 0; - } - - const LandObject* land = getLand(cellX, cellY, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; - if (data) - { - color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; - color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; - color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; - } - else - { - color.r() = 255; - color.g() = 255; - color.b() = 255; - } - } - void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, @@ -521,25 +438,6 @@ namespace ESMTerrain } - float Storage::getVertexHeight(const ESM::Land::LandData* data, int x, int y) - { - assert(x < ESM::Land::LAND_SIZE); - assert(y < ESM::Land::LAND_SIZE); - return data->mHeights[y * ESM::Land::LAND_SIZE + x]; - } - - const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) - { - LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); - if (found != cache.mMap.end()) - return found->second; - else - { - found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; - return found->second; - } - } - Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { OpenThreads::ScopedLock lock(mLayerInfoMutex); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 613d2e2185..79c75471fe 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -1,8 +1,13 @@ #ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H #define COMPONENTS_ESM_TERRAIN_STORAGE_H +#include + #include +#include +#include + #include #include @@ -13,11 +18,14 @@ namespace VFS class Manager; } +namespace CSVRender +{ + class TerrainStorage; +} + namespace ESMTerrain { - class LandCache; - /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use class LandObject : public osg::Object { @@ -48,6 +56,13 @@ namespace ESMTerrain ESM::Land::LandData mData; }; + class LandCache + { + public: + typedef std::map, osg::ref_ptr > Map; + Map mMap; + }; + /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) /// into the terrain component, converting it on the fly as needed. class Storage : public Terrain::Storage @@ -110,14 +125,6 @@ namespace ESMTerrain private: const VFS::Manager* mVFS; - inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); - inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); - inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); - - inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y); - - inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); - // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. // pair @@ -137,6 +144,103 @@ namespace ESMTerrain bool mAutoUseSpecularMaps; Terrain::LayerInfo getLayerInfo(const std::string& texture); + + protected: + + inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) + { + while (col >= ESM::Land::LAND_SIZE-1) + { + ++cellY; + col -= ESM::Land::LAND_SIZE-1; + } + while (row >= ESM::Land::LAND_SIZE-1) + { + ++cellX; + row -= ESM::Land::LAND_SIZE-1; + } + while (col < 0) + { + --cellY; + col += ESM::Land::LAND_SIZE-1; + } + while (row < 0) + { + --cellX; + row += ESM::Land::LAND_SIZE-1; + } + + const LandObject* land = getLand(cellX, cellY, cache); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; + if (data) + { + normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalize(); + } + else + normal = osg::Vec3f(0,0,1); + }; + + inline void fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) + { + if (col == ESM::Land::LAND_SIZE-1) + { + ++cellY; + col = 0; + } + if (row == ESM::Land::LAND_SIZE-1) + { + ++cellX; + row = 0; + } + + const LandObject* land = getLand(cellX, cellY, cache); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; + if (data) + { + color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; + color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; + color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; + } + else + { + color.r() = 255; + color.g() = 255; + color.b() = 255; + } + }; + + inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) + { + osg::Vec3f n1,n2,n3,n4; + fixNormal(n1, cellX, cellY, col+1, row, cache); + fixNormal(n2, cellX, cellY, col-1, row, cache); + fixNormal(n3, cellX, cellY, col, row+1, cache); + fixNormal(n4, cellX, cellY, col, row-1, cache); + normal = (n1+n2+n3+n4); + normal.normalize(); + }; + + inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y) + { + assert(x < ESM::Land::LAND_SIZE); + assert(y < ESM::Land::LAND_SIZE); + return data->mHeights[y * ESM::Land::LAND_SIZE + x]; + }; + + inline const LandObject* getLand(int cellX, int cellY, LandCache& cache) + { + LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); + if (found != cache.mMap.end()) + return found->second; + else + { + found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; + return found->second; + } + }; }; } From 4a2d8aaf9741dc72ff0a14104f2e282e146e4199 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 12 Sep 2019 02:24:13 +0300 Subject: [PATCH 02/46] Handle mBrushSize 1. --- apps/opencs/view/render/terrainshapemode.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index aa91256962..62aef6570f 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -352,6 +352,7 @@ void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor) void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair& vertexCoords, bool dragOperation) { int r = mBrushSize / 2; + if (r == 0) r = 1; // Prevent division by zero later, which might happen when mBrushSize == 1 std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; @@ -409,7 +410,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair float smoothedByDistance = 0.0f; if (mShapeEditTool == 0) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); if (mShapeEditTool == 1 || mShapeEditTool == 2) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); - if (distance < r) + if (distance <= r) { if (mShapeEditTool >= 0 && mShapeEditTool < 3) alterHeight(cellCoords, x, y, smoothedByDistance); if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); @@ -978,7 +979,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& int distanceX = abs(i - vertexCoords.first); int distanceY = abs(j - vertexCoords.second); int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); - if (distance < r) selections.emplace_back(std::make_pair(i, j)); + if (distance <= r) selections.emplace_back(std::make_pair(i, j)); } } } From c2428bc5fa8d16806af1f6d41230ecdef00106ab Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 18 Sep 2019 12:04:03 +0300 Subject: [PATCH 03/46] Remove unneeded forward declaration --- components/esmterrain/storage.hpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 79c75471fe..019bf0babb 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -18,11 +18,6 @@ namespace VFS class Manager; } -namespace CSVRender -{ - class TerrainStorage; -} - namespace ESMTerrain { @@ -62,7 +57,7 @@ namespace ESMTerrain typedef std::map, osg::ref_ptr > Map; Map mMap; }; - + /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) /// into the terrain component, converting it on the fly as needed. class Storage : public Terrain::Storage From 54e13954e8840c5db089be8074b19353ff9ad01b Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 19 Sep 2019 12:38:15 +0300 Subject: [PATCH 04/46] Improve land steepness limiting code --- apps/opencs/view/render/terrainshapemode.cpp | 244 ++++++++++++------- apps/opencs/view/render/terrainshapemode.hpp | 8 +- 2 files changed, 157 insertions(+), 95 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 62aef6570f..015c190352 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -238,6 +238,16 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) undoStack.beginMacro ("Edit shape and normal records"); + 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) + { + limitAlteredHeights(cellCoordinates, true); + } + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); @@ -689,7 +699,8 @@ void CSVRender::TerrainShapeMode::flattenHeight(const CSMWorld::CellCoordinates& } void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight, - float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight) + float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight, float* rightHeight, + float* rightAlteredHeight, float* downHeight, float* downAlteredHeight) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMWorld::IdTable& landTable = dynamic_cast ( @@ -699,13 +710,18 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); - std::string cellUpLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() - 1); + std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); + std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); bool noCell = document.getData().getCells().searchId (cellId) == -1; bool noLand = document.getData().getLand().searchId (cellId) == -1; bool noLeftCell = document.getData().getCells().searchId (cellLeftId) == -1; bool noLeftLand = document.getData().getLand().searchId (cellLeftId) == -1; bool noUpCell = document.getData().getCells().searchId (cellUpId) == -1; bool noUpLand = document.getData().getLand().searchId (cellUpId) == -1; + bool noRightCell = document.getData().getCells().searchId (cellRightId) == -1; + bool noRightLand = document.getData().getLand().searchId (cellRightId) == -1; + bool noDownCell = document.getData().getCells().searchId (cellDownId) == -1; + bool noDownLand = document.getData().getLand().searchId (cellDownId) == -1; *thisHeight = 0.0f; // real + altered height *thisAlteredHeight = 0.0f; // only altered height @@ -713,6 +729,10 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor *leftAlteredHeight = 0.0f; *upHeight = 0.0f; *upAlteredHeight = 0.0f; + *rightHeight = 0.0f; + *rightAlteredHeight = 0.0f; + *downHeight = 0.0f; + *downAlteredHeight = 0.0f; if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) @@ -726,11 +746,14 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor *thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); *thisHeight = landShapePointer[inCellY * landSize + inCellX] + *thisAlteredHeight; - // In case of cell edge, and touching cell/land is not found, assume that left and up -heights would be the same - // This is to prevent unnecessary action at limitHeightChange() + // Default to the same value as thisHeight, which happens in the case of cell edge where next cell/land is not found, + // which is to prevent unnecessary action at limitHeightChange(). *leftHeight = *thisHeight; *upHeight = *thisHeight; + *rightHeight = *thisHeight; + *downHeight = *thisHeight; + //If at edge, get values from neighboring cell if (inCellX == 0) { if(!noLeftCell && !noLeftLand) @@ -760,11 +783,43 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor } } } + if (inCellX == landSize - 1) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); + if(!noRightCell && !noRightLand) + { + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = + landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); + *rightHeight = landRightShapePointer[inCellY * landSize + 1]; + if (paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY)) + { + *rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(1, 0), 1, inCellY); + *rightHeight += *rightAlteredHeight; + } + } + } + if (inCellY == landSize - 1) + { + cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); + if(!noDownCell && !noDownLand) + { + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = + landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); + *downHeight = landDownShapePointer[landSize + 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) { *leftHeight = landShapePointer[inCellY * landSize + inCellX - 1]; if (paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY)) - *leftAlteredHeight += *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY); + *leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY); *leftHeight += *leftAlteredHeight; } if (inCellY != 0) @@ -774,6 +829,21 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1); *upHeight += *upAlteredHeight; } + if (inCellX != landSize - 1) + { + *rightHeight = landShapePointer[inCellY * landSize + inCellX + 1]; + if (paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY)) + *rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY); + *rightHeight += *rightAlteredHeight; + } + if (inCellY != landSize - 1) + { + *downHeight = landShapePointer[(inCellY + 1) * landSize + inCellX]; + if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1)) + *downAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1); + *downHeight += *downAlteredHeight; + } + } } } @@ -808,7 +878,7 @@ void CSVRender::TerrainShapeMode::fixEdges(const CSMWorld::CellCoordinates& cell } } -void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords) +void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMWorld::IdTable& landTable = dynamic_cast ( @@ -817,133 +887,125 @@ void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); - int limitHeightChange = 1024.0f; // Limited by save format + int limitHeightChange = 1016.0f; // Limited by save format bool noCell = document.getData().getCells().searchId (cellId) == -1; bool noLand = document.getData().getLand().searchId (cellId) == -1; - bool dataAlteredAtThisCell = false; - int maxPasses = 5; //Multiple passes are needed if there are consecutive height differences over the limit if (!noCell && !noLand) { const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - for (int passes = 0; passes < maxPasses; ++passes) + if (reverseMode == false) { - for(int inCellX = 0; inCellX < landSize; ++inCellX) + for(int inCellY = 0; inCellY < landSize; ++inCellY) { - for(int inCellY = 0; inCellY < landSize; ++inCellY) + for(int inCellX = 0; inCellX < landSize; ++inCellX) { - // ### Variable naming key ### - // Variables here aim to hold the final value, which is (real_value + altered_value) - // this = this Cell - // left = x - 1, up = y - 1 - // Altered = transient edit (in current edited) - // Binding = the last row or column, the one that binds cell land together - float thisHeight = 0.0f; float thisAlteredHeight = 0.0f; float leftHeight = 0.0f; float leftAlteredHeight = 0.0f; float upHeight = 0.0f; float upAlteredHeight = 0.0f; + float rightHeight = 0.0f; + float rightAlteredHeight = 0.0f; + float downHeight = 0.0f; + float downAlteredHeight = 0.0f; + float* limitedAlteredHeightXAxis = nullptr; + float* limitedAlteredHeightYAxis = nullptr; updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight, - &upHeight, &upAlteredHeight); + &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, &downAlteredHeight); - bool doChange = false; + // Check for height limits on x-axis + if (leftHeight - thisHeight > limitHeightChange) + limitedAlteredHeightXAxis = new float(leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + else if (leftHeight - thisHeight < -limitHeightChange) + limitedAlteredHeightXAxis = new float(leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); - // Check for height limits, prioritize left over up, except in left-right -cell edges - if (thisHeight - upHeight >= limitHeightChange) - { - thisAlteredHeight = upHeight + (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } - if (thisHeight - upHeight <= -limitHeightChange) - { - thisAlteredHeight = upHeight - (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } - if (thisHeight - leftHeight >= limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) - { - thisAlteredHeight = leftHeight + (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } - if (thisHeight - leftHeight <= -limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) - { - thisAlteredHeight = leftHeight - (limitHeightChange / 2) - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } + // Check for height limits on y-axis + if (upHeight - thisHeight > limitHeightChange) + limitedAlteredHeightYAxis = new float(upHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + else if (upHeight - thisHeight < -limitHeightChange) + limitedAlteredHeightYAxis = new float(upHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); - // Apply limits - if (doChange == true) + // Limit altered height value based on x or y, whichever is the smallest + if (limitedAlteredHeightXAxis) { - alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight, false); - dataAlteredAtThisCell = true; + if (limitedAlteredHeightYAxis) + { + if(std::abs(*limitedAlteredHeightXAxis) >= std::abs(*limitedAlteredHeightYAxis)) + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); + else + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); + } + else + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); } + else if (limitedAlteredHeightYAxis) + { + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); + } + delete limitedAlteredHeightXAxis; + delete limitedAlteredHeightYAxis; } } + } - //Skip unneeded extra passes - if (dataAlteredAtThisCell == false) + if (reverseMode == true) + { + for(int inCellY = landSize - 1; inCellY >= 0; --inCellY) { - passes = maxPasses; - continue; - } - - //Inverse check (implement properly after default check works) - for(int inCellX = landSize - 1; inCellX >= 0; --inCellX) - { - for(int inCellY = landSize - 1; inCellY >= 0; --inCellY) + for(int inCellX = landSize - 1; inCellX >= 0; --inCellX) { - // ### Variable naming key ### - // Variables here aim to hold the final value, which is (real_value + altered_value) - // this = this Cell - // left = x - 1, up = y - 1 - // Altered = transient edit (in current edited) - // Binding = the last row or column, the one that binds cell land together - float thisHeight = 0.0f; float thisAlteredHeight = 0.0f; float leftHeight = 0.0f; float leftAlteredHeight = 0.0f; float upHeight = 0.0f; float upAlteredHeight = 0.0f; + float rightHeight = 0.0f; + float rightAlteredHeight = 0.0f; + float downHeight = 0.0f; + float downAlteredHeight = 0.0f; + float* limitedAlteredHeightXAxis = nullptr; + float* limitedAlteredHeightYAxis = nullptr; updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight, - &upHeight, &upAlteredHeight); + &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, &downAlteredHeight); - bool doChange = false; + // Check for height limits on x-axis + if (rightHeight - thisHeight > limitHeightChange) + limitedAlteredHeightXAxis = new float(rightHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + else if (rightHeight - thisHeight < -limitHeightChange) + limitedAlteredHeightXAxis = new float(rightHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); - // Check for height limits, prioritize left over up, except in left-right -cell edges - if (thisHeight - upHeight >= limitHeightChange) - { - thisAlteredHeight = upHeight + limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } - if (thisHeight - upHeight <= -limitHeightChange) - { - thisAlteredHeight = upHeight - limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } - if (thisHeight - leftHeight >= limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) - { - thisAlteredHeight = leftHeight + limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } - if (thisHeight - leftHeight <= -limitHeightChange && !(doChange == true && (inCellX == 0 || inCellX == landSize - 1))) - { - thisAlteredHeight = leftHeight - limitHeightChange - landShapePointer[inCellY * landSize + inCellX]; - doChange = true; - } + // Check for height limits on y-axis + if (downHeight - thisHeight > limitHeightChange) + limitedAlteredHeightYAxis = new float(downHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + else if (downHeight - thisHeight < -limitHeightChange) + limitedAlteredHeightYAxis = new float(downHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); - // Apply limits - if (doChange == true) + // Limit altered height value based on x or y, whichever is the smallest + if (limitedAlteredHeightXAxis) { - alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight, false); - dataAlteredAtThisCell = true; + if (limitedAlteredHeightYAxis) + { + if(std::abs(*limitedAlteredHeightXAxis) >= std::abs(*limitedAlteredHeightYAxis)) + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); + else + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); + } + else + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); } - } + else if (limitedAlteredHeightYAxis) + { + alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); + } + delete limitedAlteredHeightXAxis; + delete limitedAlteredHeightYAxis; } } } } diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index f910b9ecdd..43978eea30 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -93,15 +93,16 @@ namespace CSVRender /// Do a single flattening height alteration for transient shape edit map void flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight); - /// Update the key values used in height calculations + /// Get altered height values around one vertex void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight, - float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight); + float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight, + float* rightHeight, float* rightAlteredHeight, float* downHeight, float* downAlteredHeight); /// Bind edge vertices to next cells void fixEdges(const CSMWorld::CellCoordinates& cellCoords); /// Check that the edit doesn't break save format limits, fix if necessary - void limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords); + void limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false); /// Handle brush mechanics for terrain shape selection void selectTerrainShapes (const std::pair& vertexCoords, unsigned char selectMode, bool dragOperation); @@ -145,7 +146,6 @@ namespace CSVRender void passBrushTexture(std::string brushTexture); public slots: - //void handleDropEvent(QDropEvent *event); void setBrushSize(int brushSize); void setBrushShape(int brushShape); void setShapeEditTool(int shapeEditTool); From 60c0a25004777f21c30400cd1a0c5f5e0478d632 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 19 Sep 2019 14:18:36 +0300 Subject: [PATCH 05/46] Fix smooth tool and flatten tool. Default tool strength to 8. --- apps/opencs/view/render/terrainshapemode.cpp | 13 ++++++------- apps/opencs/view/widget/scenetoolshapebrush.cpp | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 015c190352..cc20339a91 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -50,7 +50,7 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidge mIsEditing(false), mTotalDiffY(0), mShapeEditTool(0), - mShapeEditToolStrength(0), + mShapeEditToolStrength(8), mTargetHeight(0) { } @@ -656,10 +656,9 @@ void CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& float averageHeight = (upHeight + downHeight + rightHeight + leftHeight + upAlteredHeight + downAlteredHeight + rightAlteredHeight + leftAlteredHeight) / 4; if ((thisHeight + thisAlteredHeight) != averageHeight) mAlteredCells.emplace_back(cellCoords); - if (toolStrength > abs(thisHeight + thisAlteredHeight - averageHeight) && toolStrength > 8.0f) toolStrength = - abs(thisHeight + thisAlteredHeight - averageHeight); //Cut down excessive changes - if (thisHeight + thisAlteredHeight > averageHeight) alterHeight(cellCoords, inCellX, inCellY, -toolStrength); - if (thisHeight + thisAlteredHeight < averageHeight) alterHeight(cellCoords, inCellX, inCellY, +toolStrength); + if (toolStrength > abs(thisHeight + thisAlteredHeight - averageHeight)) toolStrength = abs(thisHeight + thisAlteredHeight - averageHeight); + if (thisHeight + thisAlteredHeight > averageHeight) alterHeight(cellCoords, inCellX, inCellY, - toolStrength); + if (thisHeight + thisAlteredHeight < averageHeight) alterHeight(cellCoords, inCellX, inCellY, + toolStrength); } } } @@ -694,8 +693,8 @@ void CSVRender::TerrainShapeMode::flattenHeight(const CSMWorld::CellCoordinates& if (toolStrength > abs(thisHeight - targetHeight) && toolStrength > 8.0f) toolStrength = abs(thisHeight - targetHeight); //Cut down excessive changes - if (thisHeight + thisAlteredHeight > targetHeight) alterHeight(cellCoords, inCellX, inCellY, -toolStrength); - if (thisHeight + thisAlteredHeight < targetHeight) alterHeight(cellCoords, inCellX, inCellY, +toolStrength); + if (thisHeight + thisAlteredHeight > targetHeight) alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight - toolStrength); + if (thisHeight + thisAlteredHeight < targetHeight) alterHeight(cellCoords, inCellX, inCellY, thisAlteredHeight + toolStrength); } void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight, diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index a9f494bf38..3e11dad97d 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -118,6 +118,7 @@ CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidge mToolStrengthSlider->setTickInterval(8); mToolStrengthSlider->setRange(8, 128); mToolStrengthSlider->setSingleStep(8); + mToolStrengthSlider->setValue(8); layoutMain->addWidget(mHorizontalGroupBox); layoutMain->addWidget(mSizeSliders); From ff18595a86ef60fdcc2c4ec8ea355797b143a445 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 21 Sep 2019 13:15:42 +0300 Subject: [PATCH 06/46] Reduce code duplification, add bool value to limiting --- apps/opencs/view/render/terrainshapemode.cpp | 104 +++++++++---------- apps/opencs/view/render/terrainshapemode.hpp | 8 +- 2 files changed, 53 insertions(+), 59 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index cc20339a91..7c4a68ae50 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -877,7 +877,37 @@ void CSVRender::TerrainShapeMode::fixEdges(const CSMWorld::CellCoordinates& cell } } -void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode) +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) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMWorld::IdTable& landTable = dynamic_cast ( @@ -889,31 +919,32 @@ void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi int limitHeightChange = 1016.0f; // Limited by save format bool noCell = document.getData().getCells().searchId (cellId) == -1; bool noLand = document.getData().getLand().searchId (cellId) == -1; + bool steepnessIsWithinLimits = true; if (!noCell && !noLand) { const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + float thisHeight = 0.0f; + float thisAlteredHeight = 0.0f; + float leftHeight = 0.0f; + float leftAlteredHeight = 0.0f; + float upHeight = 0.0f; + float upAlteredHeight = 0.0f; + float rightHeight = 0.0f; + float rightAlteredHeight = 0.0f; + float downHeight = 0.0f; + float downAlteredHeight = 0.0f; + if (reverseMode == false) { for(int inCellY = 0; inCellY < landSize; ++inCellY) { for(int inCellX = 0; inCellX < landSize; ++inCellX) { - float thisHeight = 0.0f; - float thisAlteredHeight = 0.0f; - float leftHeight = 0.0f; - float leftAlteredHeight = 0.0f; - float upHeight = 0.0f; - float upAlteredHeight = 0.0f; - float rightHeight = 0.0f; - float rightAlteredHeight = 0.0f; - float downHeight = 0.0f; - float downAlteredHeight = 0.0f; float* limitedAlteredHeightXAxis = nullptr; float* limitedAlteredHeightYAxis = nullptr; - updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight, &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, &downAlteredHeight); @@ -930,25 +961,9 @@ void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi limitedAlteredHeightYAxis = new float(upHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); // Limit altered height value based on x or y, whichever is the smallest - if (limitedAlteredHeightXAxis) - { - if (limitedAlteredHeightYAxis) - { - if(std::abs(*limitedAlteredHeightXAxis) >= std::abs(*limitedAlteredHeightYAxis)) - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); - else - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); - } - else - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); - } - else if (limitedAlteredHeightYAxis) - { - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); - } + compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis, limitedAlteredHeightYAxis, &steepnessIsWithinLimits); delete limitedAlteredHeightXAxis; - delete limitedAlteredHeightYAxis; - } + delete limitedAlteredHeightYAxis; } } } @@ -958,19 +973,8 @@ void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi { for(int inCellX = landSize - 1; inCellX >= 0; --inCellX) { - float thisHeight = 0.0f; - float thisAlteredHeight = 0.0f; - float leftHeight = 0.0f; - float leftAlteredHeight = 0.0f; - float upHeight = 0.0f; - float upAlteredHeight = 0.0f; - float rightHeight = 0.0f; - float rightAlteredHeight = 0.0f; - float downHeight = 0.0f; - float downAlteredHeight = 0.0f; float* limitedAlteredHeightXAxis = nullptr; float* limitedAlteredHeightYAxis = nullptr; - updateKeyHeightValues(cellCoords, inCellX, inCellY, &thisHeight, &thisAlteredHeight, &leftHeight, &leftAlteredHeight, &upHeight, &upAlteredHeight, &rightHeight, &rightAlteredHeight, &downHeight, &downAlteredHeight); @@ -987,27 +991,13 @@ void CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi limitedAlteredHeightYAxis = new float(downHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); // Limit altered height value based on x or y, whichever is the smallest - if (limitedAlteredHeightXAxis) - { - if (limitedAlteredHeightYAxis) - { - if(std::abs(*limitedAlteredHeightXAxis) >= std::abs(*limitedAlteredHeightYAxis)) - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); - else - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); - } - else - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightXAxis, false); - } - else if (limitedAlteredHeightYAxis) - { - alterHeight(cellCoords, inCellX, inCellY, *limitedAlteredHeightYAxis, false); - } + compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis, limitedAlteredHeightYAxis, &steepnessIsWithinLimits); delete limitedAlteredHeightXAxis; delete limitedAlteredHeightYAxis; } } } } + return steepnessIsWithinLimits; } void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& vertexCoords, unsigned char selectMode, bool dragOperation) diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 43978eea30..b5b642b462 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -101,8 +101,12 @@ namespace CSVRender /// Bind edge vertices to next cells void fixEdges(const CSMWorld::CellCoordinates& cellCoords); - /// Check that the edit doesn't break save format limits, fix if necessary - void limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false); + ///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); /// Handle brush mechanics for terrain shape selection void selectTerrainShapes (const std::pair& vertexCoords, unsigned char selectMode, bool dragOperation); From 1a3fc435b91d39c78cfd7c416b5db8ece5f318e6 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 21 Sep 2019 14:48:16 +0300 Subject: [PATCH 07/46] Fix alterHeight bugs, don't let broken land edit pass. --- apps/opencs/view/render/terrainshapemode.cpp | 111 +++++++++++++------ 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 7c4a68ae50..bcf7b2fc56 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "../widget/modebutton.hpp" #include "../widget/scenetoolbar.hpp" @@ -236,18 +237,37 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) QUndoStack& undoStack = document.getUndoStack(); - undoStack.beginMacro ("Edit shape and normal records"); + bool passing = false; + int passes = 0; - for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) + while (passing == false) // Multiple passes are needed when steepness problems arise for both x and y axis simultaneously { - 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) - { - limitAlteredHeights(cellCoordinates, true); + 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."; + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + paged->resetAllAlteredHeights(); + mAlteredCells.clear(); + return; + } + } } + undoStack.beginMacro ("Edit shape and normal records"); + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); @@ -335,12 +355,8 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) undoStack.endMacro(); mAlteredCells.clear(); - if (CSVRender::PagedWorldspaceWidget *paged = - dynamic_cast (&getWorldspaceWidget())) - { + if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) paged->resetAllAlteredHeights(); - mTotalDiffY = 0; - } } } @@ -464,12 +480,12 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if(allowLandShapeEditing(cellId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords); if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) { if (useTool) { + mAlteredCells.emplace_back(cellCoords); if (mShapeEditTool == 0) { // Get distance from modified land, alter land change based on zoom @@ -490,32 +506,44 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c { if(allowLandShapeEditing(cellUpLeftId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(-1, -1)); - paged->setCellAlteredHeight(cellCoords.move(-1, -1), landSize - 1, landSize - 1, alteredHeight); + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1); + if (useTool) mAlteredCells.emplace_back(cornerCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredHeight(cornerCellCoords, landSize - 1, landSize - 1, alteredHeight); } } else if (inCellX == 0 && inCellY == landSize - 1) { if(allowLandShapeEditing(cellDownLeftId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(-1, 1)); - paged->setCellAlteredHeight(cellCoords.move(-1, 1), landSize - 1, 0, alteredHeight); + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1); + if (useTool) mAlteredCells.emplace_back(cornerCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredHeight(cornerCellCoords, landSize - 1, 0, alteredHeight); } } else if (inCellX == landSize - 1 && inCellY == 0) { if(allowLandShapeEditing(cellUpRightId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(1, -1)); - paged->setCellAlteredHeight(cellCoords.move(1, -1), 0, landSize - 1, alteredHeight); + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1); + if (useTool) mAlteredCells.emplace_back(cornerCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredHeight(cornerCellCoords, 0, landSize - 1, alteredHeight); } } else if (inCellX == landSize - 1 && inCellY == landSize -1) { if(allowLandShapeEditing(cellDownRightId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(1, 1)); - paged->setCellAlteredHeight(cellCoords.move(1, 1), 0, 0, alteredHeight); + CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1); + if (useTool) mAlteredCells.emplace_back(cornerCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(cornerCellCoords); + paged->setCellAlteredHeight(cornerCellCoords, 0, 0, alteredHeight); } } @@ -524,16 +552,22 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c { if(allowLandShapeEditing(cellLeftId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(-1, 0)); - paged->setCellAlteredHeight(cellCoords.move(-1, 0), landSize - 1, inCellY, alteredHeight); + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0); + if (useTool) mAlteredCells.emplace_back(edgeCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(edgeCellCoords, landSize - 1, inCellY, alteredHeight); } } if (inCellY == 0) { if(allowLandShapeEditing(cellUpId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(0, -1)); - paged->setCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 1, alteredHeight); + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1); + if (useTool) mAlteredCells.emplace_back(edgeCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(edgeCellCoords, inCellX, landSize - 1, alteredHeight); } } @@ -541,16 +575,22 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c { if(allowLandShapeEditing(cellRightId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(1, 0)); - paged->setCellAlteredHeight(cellCoords.move(1, 0), 0, inCellY, alteredHeight); + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0); + if (useTool) mAlteredCells.emplace_back(edgeCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(edgeCellCoords, 0, inCellY, alteredHeight); } } if (inCellY == landSize - 1) { - if(allowLandShapeEditing(cellUpId)==true) + if(allowLandShapeEditing(cellDownId)==true) { - if (useTool) mAlteredCells.emplace_back(cellCoords.move(0, 1)); - paged->setCellAlteredHeight(cellCoords.move(0, 1), inCellX, 0, alteredHeight); + CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1); + if (useTool) mAlteredCells.emplace_back(edgeCellCoords); + else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) != mAlteredCells.end())) + mAlteredCells.emplace_back(edgeCellCoords); + paged->setCellAlteredHeight(edgeCellCoords, inCellX, 0, alteredHeight); } } @@ -910,8 +950,7 @@ void CSVRender::TerrainShapeMode::compareAndLimit(const CSMWorld::CellCoordinate bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - CSMWorld::IdTable& landTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& landTable = dynamic_cast (*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land)); int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); @@ -963,7 +1002,8 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi // Limit altered height value based on x or y, whichever is the smallest compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis, limitedAlteredHeightYAxis, &steepnessIsWithinLimits); delete limitedAlteredHeightXAxis; - delete limitedAlteredHeightYAxis; } + delete limitedAlteredHeightYAxis; + } } } @@ -993,7 +1033,8 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi // Limit altered height value based on x or y, whichever is the smallest compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis, limitedAlteredHeightYAxis, &steepnessIsWithinLimits); delete limitedAlteredHeightXAxis; - delete limitedAlteredHeightYAxis; } + delete limitedAlteredHeightYAxis; + } } } } From 8f3c22ccc3507b086ee2bbc2f4ab78169b52c101 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 21 Sep 2019 14:51:04 +0300 Subject: [PATCH 08/46] Remove TerrainShapeMode::fixEdges --- apps/opencs/view/render/terrainshapemode.cpp | 31 -------------------- apps/opencs/view/render/terrainshapemode.hpp | 3 -- 2 files changed, 34 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index bcf7b2fc56..c9638faf74 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -887,36 +887,6 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor } } -void CSVRender::TerrainShapeMode::fixEdges(const CSMWorld::CellCoordinates& cellCoords) -{ - CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - CSMWorld::IdTable& landTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); - int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); - std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); - std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); - std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); - std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); - std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); - if (allowLandShapeEditing(cellId) == true) - { - const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); - const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); - const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); - const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); - CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); - for(int i = 0; i < landSize; ++i) - { - if(document.getData().getCells().searchId (cellLeftId) != -1 && document.getData().getLand().searchId (cellLeftId) != -1 && landShapePointer[i * landSize] != landLeftShapePointer[i * landSize + landSize - 1]) landShapeNew[i * landSize] = landLeftShapePointer[i * landSize + landSize - 1]; - if(document.getData().getCells().searchId (cellRightId) != -1 && document.getData().getLand().searchId (cellRightId) != -1 && landShapePointer[i * landSize + landSize - 1] != landRightShapePointer[i * landSize]) landShapeNew[i * landSize + landSize - 1] = landRightShapePointer[i * landSize]; - if(document.getData().getCells().searchId (cellUpId) != -1 && document.getData().getLand().searchId (cellUpId) != -1 && landShapePointer[i] != landUpShapePointer[(landSize - 1) * landSize + i]) landShapeNew[i] = landUpShapePointer[(landSize - 1) * landSize + i]; - if(document.getData().getCells().searchId (cellDownId) != -1 && document.getData().getLand().searchId (cellDownId) != -1 && landShapePointer[(landSize - 1) * landSize + i] != landDownShapePointer[i]) landShapeNew[(landSize - 1) * landSize + i] = landDownShapePointer[i]; - } - pushEditToCommand(landShapeNew, document, landTable, cellId); - } -} - void CSVRender::TerrainShapeMode::compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* limitedAlteredHeightXAxis, float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits) { if (limitedAlteredHeightXAxis) @@ -1185,7 +1155,6 @@ bool CSVRender::TerrainShapeMode::allowLandShapeEditing(std::string cellId) if (mode=="Create cell and land, then edit") { document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId)); - fixEdges(CSMWorld::CellCoordinates::fromId (cellId).first); } } diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index b5b642b462..642c9052b0 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -98,9 +98,6 @@ namespace CSVRender float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight, float* rightHeight, float* rightAlteredHeight, float* downHeight, float* downAlteredHeight); - /// Bind edge vertices to next cells - void fixEdges(const CSMWorld::CellCoordinates& cellCoords); - ///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); From 23e7c71a5a9d3f43c32f0b29082aab6eac5f6f5c Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 21 Sep 2019 17:49:32 +0300 Subject: [PATCH 09/46] Tighter corner checking, clean-up, landSize to ESM::Land::LAND_SIZE. --- apps/opencs/view/render/terrainshapemode.cpp | 149 ++++++++++--------- apps/opencs/view/render/terrainshapemode.hpp | 4 - 2 files changed, 77 insertions(+), 76 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index c9638faf74..e5daf7a9f6 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -150,7 +150,7 @@ bool CSVRender::TerrainShapeMode::primaryEditStartDrag (const QPoint& pos) const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - mTargetHeight = landShapePointer[inCellY * landSize + inCellX]; + mTargetHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX]; } } @@ -274,17 +274,17 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); - for(int i = 0; i < landSize; ++i) + for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) { - for(int j = 0; j < landSize; ++j) + for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) { if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) { if (paged->getCellAlteredHeight(cellCoordinates, i, j)) - landShapeNew[j * landSize + i] = landShapePointer[j * landSize + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); + landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); else - landShapeNew[j * landSize + i] = 0; + landShapeNew[j * ESM::Land::LAND_SIZE + i] = 0; } } } @@ -300,9 +300,9 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value(); CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer); - for(int i = 0; i < landSize; ++i) + for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) { - for(int j = 0; j < landSize; ++j) + for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) { float v1[3]; float v2[3]; @@ -311,26 +311,26 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) v1[0] = 128; v1[1] = 0; - if (i < landSize - 1) v1[2] = landShapePointer[j*landSize+i+1] - landShapePointer[j*landSize+i]; + if (i < ESM::Land::LAND_SIZE - 1) v1[2] = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; if (!noLand && !noCell) - v1[2] = landRightShapePointer[j*landSize+1] - landShapePointer[j*landSize+i]; + v1[2] = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else v1[2] = 0; } v2[0] = 0; v2[1] = 128; - if (j < landSize - 1) v2[2] = landShapePointer[(j+1)*landSize+i] - landShapePointer[j*landSize+i]; + if (j < ESM::Land::LAND_SIZE - 1) v2[2] = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; if (!noLand && !noCell) - v2[2] = landDownShapePointer[landSize+i] - landShapePointer[j*landSize+i]; + v2[2] = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else v2[2] = 0; } @@ -345,9 +345,9 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) normal[1] /= hyp; normal[2] /= hyp; - landNormalsNew[(j*landSize+i)*3+0] = normal[0]; - landNormalsNew[(j*landSize+i)*3+1] = normal[1]; - landNormalsNew[(j*landSize+i)*3+2] = normal[2]; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = normal[0]; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = normal[1]; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal[2]; } } if (allowLandShapeEditing(cellId) == true) pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); @@ -499,52 +499,53 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if (mShapeEditTool == 3) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; } - paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + if (inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1) + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); // Change values of cornering cells if (inCellX == 0 && inCellY == 0) { - if(allowLandShapeEditing(cellUpLeftId)==true) + if(allowLandShapeEditing(cellUpLeftId) && allowLandShapeEditing(cellLeftId) && allowLandShapeEditing(cellUpId)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1); if (useTool) mAlteredCells.emplace_back(cornerCellCoords); else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) mAlteredCells.emplace_back(cornerCellCoords); - paged->setCellAlteredHeight(cornerCellCoords, landSize - 1, landSize - 1, alteredHeight); - } + paged->setCellAlteredHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE - 1, alteredHeight); + } else return; } - else if (inCellX == 0 && inCellY == landSize - 1) + else if (inCellX == 0 && inCellY == ESM::Land::LAND_SIZE - 1) { - if(allowLandShapeEditing(cellDownLeftId)==true) + if(allowLandShapeEditing(cellDownLeftId) && allowLandShapeEditing(cellLeftId) && allowLandShapeEditing(cellDownId)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1); if (useTool) mAlteredCells.emplace_back(cornerCellCoords); else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) mAlteredCells.emplace_back(cornerCellCoords); - paged->setCellAlteredHeight(cornerCellCoords, landSize - 1, 0, alteredHeight); - } + paged->setCellAlteredHeight(cornerCellCoords, ESM::Land::LAND_SIZE - 1, 0, alteredHeight); + } else return; } - else if (inCellX == landSize - 1 && inCellY == 0) + else if (inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == 0) { - if(allowLandShapeEditing(cellUpRightId)==true) + if(allowLandShapeEditing(cellUpRightId) && allowLandShapeEditing(cellRightId) && allowLandShapeEditing(cellUpId)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1); if (useTool) mAlteredCells.emplace_back(cornerCellCoords); else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) mAlteredCells.emplace_back(cornerCellCoords); - paged->setCellAlteredHeight(cornerCellCoords, 0, landSize - 1, alteredHeight); - } + paged->setCellAlteredHeight(cornerCellCoords, 0, ESM::Land::LAND_SIZE - 1, alteredHeight); + } else return; } - else if (inCellX == landSize - 1 && inCellY == landSize -1) + else if (inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == ESM::Land::LAND_SIZE - 1) { - if(allowLandShapeEditing(cellDownRightId)==true) + if(allowLandShapeEditing(cellDownRightId) && allowLandShapeEditing(cellRightId) && allowLandShapeEditing(cellDownId)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1); if (useTool) mAlteredCells.emplace_back(cornerCellCoords); else if (!(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 @@ -556,7 +557,8 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if (useTool) mAlteredCells.emplace_back(edgeCellCoords); else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) != mAlteredCells.end())) mAlteredCells.emplace_back(edgeCellCoords); - paged->setCellAlteredHeight(edgeCellCoords, landSize - 1, inCellY, alteredHeight); + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + paged->setCellAlteredHeight(edgeCellCoords, ESM::Land::LAND_SIZE - 1, inCellY, alteredHeight); } } if (inCellY == 0) @@ -567,11 +569,12 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if (useTool) mAlteredCells.emplace_back(edgeCellCoords); else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) != mAlteredCells.end())) mAlteredCells.emplace_back(edgeCellCoords); - paged->setCellAlteredHeight(edgeCellCoords, inCellX, landSize - 1, alteredHeight); + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + paged->setCellAlteredHeight(edgeCellCoords, inCellX, ESM::Land::LAND_SIZE - 1, alteredHeight); } } - if (inCellX == landSize - 1) + if (inCellX == ESM::Land::LAND_SIZE - 1) { if(allowLandShapeEditing(cellRightId)==true) { @@ -579,10 +582,11 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if (useTool) mAlteredCells.emplace_back(edgeCellCoords); else if (!(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 == landSize - 1) + if (inCellY == ESM::Land::LAND_SIZE - 1) { if(allowLandShapeEditing(cellDownId)==true) { @@ -590,6 +594,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if (useTool) mAlteredCells.emplace_back(edgeCellCoords); else if (!(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); } } @@ -619,7 +624,7 @@ void CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& float thisAlteredHeight = 0.0f; if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) != nullptr) thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); - float thisHeight = landShapePointer[inCellY * landSize + inCellX]; + float thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX]; float leftHeight = 0.0f; float leftAlteredHeight = 0.0f; float upAlteredHeight = 0.0f; @@ -636,59 +641,59 @@ void CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& { cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - leftHeight = landLeftShapePointer[inCellY * landSize + (landSize - 2)]; - if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), inCellX, landSize - 2)) - leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), landSize - 2, inCellY); + leftHeight = landLeftShapePointer[inCellY * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE - 2)]; + if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), inCellX, ESM::Land::LAND_SIZE - 2)) + leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY); } if (inCellY == 0) { cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - upHeight = landUpShapePointer[(landSize - 2) * landSize + inCellX]; - if (paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 2)) - upAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, landSize - 2); + 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); } if (inCellX > 0) { - leftHeight = landShapePointer[inCellY * landSize + inCellX - 1]; + leftHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX - 1]; leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY); } if (inCellY > 0) { - upHeight = landShapePointer[(inCellY - 1) * landSize + inCellX]; + upHeight = landShapePointer[(inCellY - 1) * ESM::Land::LAND_SIZE + inCellX]; upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1); } - if (inCellX == landSize - 1) + if (inCellX == ESM::Land::LAND_SIZE - 1) { cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - rightHeight = landRightShapePointer[inCellY * landSize + 1]; + 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); } } - if (inCellY == landSize - 1) + if (inCellY == ESM::Land::LAND_SIZE - 1) { cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - downHeight = landDownShapePointer[1 * landSize + inCellX]; + downHeight = landDownShapePointer[1 * ESM::Land::LAND_SIZE + inCellX]; if (paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1)) { downAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, 1), inCellX, 1); } } - if (inCellX < landSize - 1) + if (inCellX < ESM::Land::LAND_SIZE - 1) { - rightHeight = landShapePointer[inCellY * landSize + inCellX + 1]; + rightHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX + 1]; if(paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY)) rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY); } - if (inCellY < landSize - 1) + if (inCellY < ESM::Land::LAND_SIZE - 1) { - downHeight = landShapePointer[(inCellY + 1) * landSize + inCellX]; + downHeight = landShapePointer[(inCellY + 1) * ESM::Land::LAND_SIZE + inCellX]; if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1)) downAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1); } @@ -727,7 +732,7 @@ void CSVRender::TerrainShapeMode::flattenHeight(const CSMWorld::CellCoordinates& if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY)) thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); - thisHeight = landShapePointer[inCellY * landSize + inCellX]; + thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX]; } } @@ -783,7 +788,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor if(paged->getCellAlteredHeight(cellCoords, inCellX, inCellY)) *thisAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY); - *thisHeight = landShapePointer[inCellY * landSize + inCellX] + *thisAlteredHeight; + *thisHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX] + *thisAlteredHeight; // Default to the same value as thisHeight, which happens in the case of cell edge where next cell/land is not found, // which is to prevent unnecessary action at limitHeightChange(). @@ -799,10 +804,10 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor { const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); - *leftHeight = landLeftShapePointer[inCellY * landSize + (landSize - 2)]; - if (paged->getCellAlteredHeight(cellCoords.move(-1, 0), landSize - 2, inCellY)) + *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), landSize - 2, inCellY); + *leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(-1, 0), ESM::Land::LAND_SIZE - 2, inCellY); *leftHeight += *leftAlteredHeight; } } @@ -814,22 +819,22 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor { const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); - *upHeight = landUpShapePointer[(landSize - 2) * landSize + inCellX]; - if (paged->getCellAlteredHeight(cellCoords.move(0,-1), inCellX, landSize - 2)) + *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, landSize - 2); + *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords.move(0, -1), inCellX, ESM::Land::LAND_SIZE - 2); *upHeight += *upAlteredHeight; } } } - if (inCellX == landSize - 1) + if (inCellX == ESM::Land::LAND_SIZE - 1) { cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); if(!noRightCell && !noRightLand) { const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); - *rightHeight = landRightShapePointer[inCellY * landSize + 1]; + *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); @@ -837,14 +842,14 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor } } } - if (inCellY == landSize - 1) + if (inCellY == ESM::Land::LAND_SIZE - 1) { cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); if(!noDownCell && !noDownLand) { const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); - *downHeight = landDownShapePointer[landSize + inCellX]; + *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); @@ -856,28 +861,28 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor //If not at edge, get values from the same cell if (inCellX != 0) { - *leftHeight = landShapePointer[inCellY * landSize + inCellX - 1]; + *leftHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX - 1]; if (paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY)) *leftAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX - 1, inCellY); *leftHeight += *leftAlteredHeight; } if (inCellY != 0) { - *upHeight = landShapePointer[(inCellY - 1) * landSize + inCellX]; + *upHeight = landShapePointer[(inCellY - 1) * ESM::Land::LAND_SIZE + inCellX]; if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1)) *upAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY - 1); *upHeight += *upAlteredHeight; } - if (inCellX != landSize - 1) + if (inCellX != ESM::Land::LAND_SIZE - 1) { - *rightHeight = landShapePointer[inCellY * landSize + inCellX + 1]; + *rightHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX + 1]; if (paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY)) *rightAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX + 1, inCellY); *rightHeight += *rightAlteredHeight; } - if (inCellY != landSize - 1) + if (inCellY != ESM::Land::LAND_SIZE - 1) { - *downHeight = landShapePointer[(inCellY + 1) * landSize + inCellX]; + *downHeight = landShapePointer[(inCellY + 1) * ESM::Land::LAND_SIZE + inCellX]; if (paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1)) *downAlteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY + 1); *downHeight += *downAlteredHeight; @@ -948,9 +953,9 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi if (reverseMode == false) { - for(int inCellY = 0; inCellY < landSize; ++inCellY) + for(int inCellY = 0; inCellY < ESM::Land::LAND_SIZE; ++inCellY) { - for(int inCellX = 0; inCellX < landSize; ++inCellX) + for(int inCellX = 0; inCellX < ESM::Land::LAND_SIZE; ++inCellX) { float* limitedAlteredHeightXAxis = nullptr; float* limitedAlteredHeightYAxis = nullptr; @@ -979,9 +984,9 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi if (reverseMode == true) { - for(int inCellY = landSize - 1; inCellY >= 0; --inCellY) + for(int inCellY = ESM::Land::LAND_SIZE - 1; inCellY >= 0; --inCellY) { - for(int inCellX = landSize - 1; inCellX >= 0; --inCellX) + for(int inCellX = ESM::Land::LAND_SIZE - 1; inCellX >= 0; --inCellX) { float* limitedAlteredHeightXAxis = nullptr; float* limitedAlteredHeightYAxis = nullptr; diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 642c9052b0..8efe22709d 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -137,10 +137,6 @@ namespace CSVRender int mShapeEditToolStrength; int mTargetHeight; - const int cellSize {ESM::Land::REAL_SIZE}; - const int landSize {ESM::Land::LAND_SIZE}; - const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; - PagedWorldspaceWidget& getPagedWorldspaceWidget(); signals: From 62d50a1f47f6ebe443a4fb3e6af61b13cc596784 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 21 Sep 2019 17:52:07 +0300 Subject: [PATCH 10/46] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad8c814ad9..34f37451ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -185,7 +185,6 @@ Feature #4812: Support NiSwitchNode Feature #4836: Daytime node switch Feature #4840: Editor: Transient terrain change support - Feature #????: Editor: Land shape editing, land selection Feature #4859: Make water reflections more configurable Feature #4882: Support for NiPalette node Feature #4887: Add openmw command option to set initial random seed @@ -212,6 +211,7 @@ Feature #5132: Unique animations for different weapon types Feature #5146: Safe Dispose corpse Feature #5147: Show spell magicka cost in spell buying window + Feature #5170: Editor: Land shape editing, land selection Feature #5193: Weapon sheathing Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption From 55b3fd441809fe691b0d803b4c300463b0aa81b0 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 25 Sep 2019 13:01:52 +0300 Subject: [PATCH 11/46] Use float calculations for circle brush, keep tool options in memory. --- apps/opencs/view/render/terrainshapemode.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index e5daf7a9f6..3420149a50 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -80,12 +80,6 @@ void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) void CSVRender::TerrainShapeMode::deactivate(CSVWidget::SceneToolbar* toolbar) { - if(mShapeBrushScenetool) - { - toolbar->removeTool (mShapeBrushScenetool); - delete mShapeBrushScenetool; - mShapeBrushScenetool = 0; - } EditMode::deactivate(toolbar); } @@ -429,7 +423,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int distanceX = abs(i - vertexCoords.first); int distanceY = abs(j - vertexCoords.second); - int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2)); int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); float distancePerRadius = 1.0f * distance / r; From 1046d5719076711ef150a254fe53b7ffeb44665d Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 25 Sep 2019 13:02:05 +0300 Subject: [PATCH 12/46] Remove unneeded forward declarations. --- apps/opencs/view/widget/scenetoolshapebrush.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/opencs/view/widget/scenetoolshapebrush.hpp b/apps/opencs/view/widget/scenetoolshapebrush.hpp index 9048fa5364..bdb6b1309f 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.hpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.hpp @@ -30,8 +30,6 @@ namespace CSVRender namespace CSVWidget { - class SceneToolShapeBrush; - /// \brief Layout-box for some brush button settings class ShapeBrushSizeControls : public QGroupBox { @@ -49,8 +47,6 @@ namespace CSVWidget friend class CSVRender::TerrainShapeMode; }; - class SceneToolShapeBrush; - /// \brief Brush settings window class ShapeBrushWindow : public QFrame { From dc7dc93320ffe86f260ec09c5059f465b7ef7c73 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 2 Oct 2019 14:04:15 +0300 Subject: [PATCH 13/46] Remove unused functions, improve formatting, handle terrain edit click --- apps/opencs/view/render/cell.cpp | 5 - apps/opencs/view/render/cell.hpp | 2 - .../view/render/pagedworldspacewidget.cpp | 15 +- .../view/render/pagedworldspacewidget.hpp | 2 - apps/opencs/view/render/terrainshapemode.cpp | 322 ++++++++++-------- apps/opencs/view/render/terrainshapemode.hpp | 6 + apps/opencs/view/render/terrainstorage.cpp | 5 - apps/opencs/view/render/terrainstorage.hpp | 5 +- 8 files changed, 189 insertions(+), 173 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 3915ee4fbd..94d7bfa51b 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -360,11 +360,6 @@ float CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int in return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY); } -float* CSVRender::Cell::getAlteredHeights() -{ - return mTerrainStorage->getAlteredHeights(); -} - 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 36ac7ff811..281ac67356 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -124,8 +124,6 @@ namespace CSVRender float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); - float* getAlteredHeights(); - 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 21624740e7..63c7df9093 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -796,26 +796,21 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const CSMWorld::CellC if (searchResult != mCells.end()) return searchResult->second; else - return 0; + return nullptr; } void CSVRender::PagedWorldspaceWidget::setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height) { std::map::iterator searchResult = mCells.find(coords); - if (searchResult != mCells.end()) searchResult->second->setAlteredHeight(inCellX, inCellY, height); -} - -float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeights(const CSMWorld::CellCoordinates& coords) -{ - std::map::iterator searchResult = mCells.find(coords); - if (searchResult != mCells.end()) return searchResult->second->getAlteredHeights(); - return nullptr; + if (searchResult != mCells.end()) + searchResult->second->setAlteredHeight(inCellX, inCellY, height); } float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY) { std::map::iterator searchResult = mCells.find(coords); - if (searchResult != mCells.end()) return searchResult->second->getAlteredHeight(inCellX, inCellY); + if (searchResult != mCells.end()) + return searchResult->second->getAlteredHeight(inCellX, inCellY); return nullptr; } diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index db93e8435b..fcc55fe7d2 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -128,8 +128,6 @@ namespace CSVRender void setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height); - float* getCellAlteredHeights(const CSMWorld::CellCoordinates& coords); - float* getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY); void resetAllAlteredHeights(); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 3420149a50..e5a92a5de6 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -93,6 +93,24 @@ void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult& if (hit.hit && hit.tag == 0) { + if (mShapeEditTool == 4) + setFlattenToolTargetHeight(hit); + if (mDragMode == InteractionType_PrimaryEdit && mShapeEditTool > 0) + { + std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); + if (mShapeEditTool > 0) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + applyTerrainEditChanges(); + } + + if (mDragMode == InteractionType_PrimarySelect) + { + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true); + } + + if (mDragMode == InteractionType_SecondarySelect) + { + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true); + } } if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) @@ -131,21 +149,7 @@ bool CSVRender::TerrainShapeMode::primaryEditStartDrag (const QPoint& pos) mEditingPos = hit.worldPos; mIsEditing = true; if (mShapeEditTool == 4) - { - std::pair vertexCoords = CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos); - std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); - int inCellX = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); - int inCellY = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); - - CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - CSMWorld::IdTable& landTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); - int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); - const CSMWorld::LandHeightsColumn::DataType landShapePointer = - landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - - mTargetHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX]; - } + setFlattenToolTargetHeight(hit); } return true; @@ -216,138 +220,7 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) mIsEditing = false; } - std::sort(mAlteredCells.begin(), mAlteredCells.end()); - std::set removeDuplicates(mAlteredCells.begin(), mAlteredCells.end()); - mAlteredCells.assign(removeDuplicates.begin(), removeDuplicates.end()); - - CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - CSMWorld::IdTable& landTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); - CSMWorld::IdTable& ltexTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); - - int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); - int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); - - QUndoStack& undoStack = document.getUndoStack(); - - bool passing = false; - int passes = 0; - - while (passing == false) // 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."; - if (CSVRender::PagedWorldspaceWidget *paged = - dynamic_cast (&getWorldspaceWidget())) - { - paged->resetAllAlteredHeights(); - mAlteredCells.clear(); - return; - } - } - } - - undoStack.beginMacro ("Edit shape and normal records"); - - for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) - { - std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); - undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); - const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); - for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) - { - for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) - { - if (CSVRender::PagedWorldspaceWidget *paged = - dynamic_cast (&getWorldspaceWidget())) - { - if (paged->getCellAlteredHeight(cellCoordinates, i, j)) - landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); - else - landShapeNew[j * ESM::Land::LAND_SIZE + i] = 0; - } - } - } - if (allowLandShapeEditing(cellId) == true) pushEditToCommand(landShapeNew, document, landTable, cellId); - } - - for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) - { - std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); - const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY()), landshapeColumn)).value(); - const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1), landshapeColumn)).value(); - const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value(); - CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer); - - for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) - { - for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) - { - float v1[3]; - float v2[3]; - float normal[3]; - float hyp; - - v1[0] = 128; - v1[1] = 0; - if (i < ESM::Land::LAND_SIZE - 1) v1[2] = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; - else - { - bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; - bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; - if (!noLand && !noCell) - v1[2] = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; - else - v1[2] = 0; - } - - v2[0] = 0; - v2[1] = 128; - if (j < ESM::Land::LAND_SIZE - 1) v2[2] = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; - else - { - bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; - bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; - if (!noLand && !noCell) - v2[2] = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; - else - v2[2] = 0; - } - - normal[1] = v1[2]*v2[0] - v1[0]*v2[2]; - normal[0] = v1[1]*v2[2] - v1[2]*v2[1]; - normal[2] = v1[0]*v2[1] - v1[1]*v2[0]; - - hyp = sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]) / 127.0f; - - normal[0] /= hyp; - normal[1] /= hyp; - normal[2] /= hyp; - - landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = normal[0]; - landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = normal[1]; - landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal[2]; - } - } - if (allowLandShapeEditing(cellId) == true) pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); - } - undoStack.endMacro(); - mAlteredCells.clear(); + applyTerrainEditChanges(); if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) paged->resetAllAlteredHeights(); @@ -369,6 +242,143 @@ void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor) { } + +void CSVRender::TerrainShapeMode::applyTerrainEditChanges() +{ + std::sort(mAlteredCells.begin(), mAlteredCells.end()); + std::set removeDuplicates(mAlteredCells.begin(), mAlteredCells.end()); + mAlteredCells.assign(removeDuplicates.begin(), removeDuplicates.end()); + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); + + QUndoStack& undoStack = document.getUndoStack(); + + bool passing = false; + int passes = 0; + + while (passing == false) // 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."; + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + paged->resetAllAlteredHeights(); + mAlteredCells.clear(); + return; + } + } + } + + undoStack.beginMacro ("Edit shape and normal records"); + + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) + { + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); + undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); + for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) + { + for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) + { + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (paged->getCellAlteredHeight(cellCoordinates, i, j)) + landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); + else + landShapeNew[j * ESM::Land::LAND_SIZE + i] = 0; + } + } + } + if (allowLandShapeEditing(cellId) == true) pushEditToCommand(landShapeNew, document, landTable, cellId); + } + + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) + { + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY()), landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1), landshapeColumn)).value(); + const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value(); + CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer); + + for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) + { + for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) + { + float v1[3]; + float v2[3]; + float normal[3]; + float hyp; + + v1[0] = 128; + v1[1] = 0; + if (i < ESM::Land::LAND_SIZE - 1) v1[2] = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + else + { + bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; + bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; + if (!noLand && !noCell) + v1[2] = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + else + v1[2] = 0; + } + + v2[0] = 0; + v2[1] = 128; + if (j < ESM::Land::LAND_SIZE - 1) v2[2] = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + else + { + bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; + bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; + if (!noLand && !noCell) + v2[2] = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + else + v2[2] = 0; + } + + normal[1] = v1[2]*v2[0] - v1[0]*v2[2]; + normal[0] = v1[1]*v2[2] - v1[2]*v2[1]; + normal[2] = v1[0]*v2[1] - v1[1]*v2[0]; + + hyp = sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]) / 127.0f; + + normal[0] /= hyp; + normal[1] /= hyp; + normal[2] /= hyp; + + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = normal[0]; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = normal[1]; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal[2]; + } + } + if (allowLandShapeEditing(cellId) == true) pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); + } + undoStack.endMacro(); + mAlteredCells.clear(); +} + void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair& vertexCoords, bool dragOperation) { int r = mBrushSize / 2; @@ -460,6 +470,24 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair } } +void CSVRender::TerrainShapeMode::setFlattenToolTargetHeight(const WorldspaceHitResult& hit) +{ + std::pair vertexCoords = CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos); + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); + int inCellX = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); + int inCellY = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + const CSMWorld::LandHeightsColumn::DataType landShapePointer = + landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + + mTargetHeight = landShapePointer[inCellY * ESM::Land::LAND_SIZE + inCellX]; +} + + void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool) { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 8efe22709d..63e14de31c 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -81,9 +81,15 @@ namespace CSVRender virtual void dragWheel (int diff, double speedFactor); virtual void dragMoveEvent (QDragMoveEvent *event); + /// Move pending alteredHeights changes to omwgame/omeaddon -data + void applyTerrainEditChanges(); + /// Handle brush mechanics for shape editing void editTerrainShapeGrid (const std::pair& vertexCoords, bool dragOperation); + /// set the target height for flatten tool + void setFlattenToolTargetHeight(const WorldspaceHitResult& hit); + /// 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); diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 774c204edf..0eb1f9ab3b 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -73,11 +73,6 @@ namespace CSVRender } - float* TerrainStorage::getAlteredHeights() - { - return mAlteredHeight; - } - float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY) { return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX]; diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 4740f13bba..5f808d8434 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -1,6 +1,8 @@ #ifndef OPENCS_RENDER_TERRAINSTORAGE_H #define OPENCS_RENDER_TERRAINSTORAGE_H +#include + #include #include "../../model/world/data.hpp" @@ -16,11 +18,10 @@ namespace CSVRender { public: TerrainStorage(const CSMWorld::Data& data); - float mAlteredHeight[ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE]; + std::array mAlteredHeight; void setAlteredHeight(int inCellX, int inCellY, float heightMap); void resetHeights(); float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); - float* getAlteredHeights(); float* getAlteredHeight(int inCellX, int inCellY); private: From 3599e804e100ec4604669142212c5b3dd7606046 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 2 Oct 2019 14:36:30 +0300 Subject: [PATCH 14/46] Fix the default value of mBrushSize --- apps/opencs/view/widget/scenetoolshapebrush.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index 3e11dad97d..3e2f4e99db 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -59,7 +59,7 @@ CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent) : QFrame(parent, Qt::Popup), mBrushShape(0), - mBrushSize(0), + mBrushSize(1), mDocument(document) { mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this); From 69083369f869c1bae0ea8b71aab81b078cc80502 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 2 Oct 2019 15:00:12 +0300 Subject: [PATCH 15/46] use std::fill instead of for --- apps/opencs/view/render/terrainstorage.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 0eb1f9ab3b..5e4ca1365e 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -50,13 +50,7 @@ namespace CSVRender void TerrainStorage::resetHeights() { - for (int x = 0; x < ESM::Land::LAND_SIZE; ++x) - { - for (int y = 0; y < ESM::Land::LAND_SIZE; ++y) - { - mAlteredHeight[y*ESM::Land::LAND_SIZE + x] = 0; - } - } + std::fill(std::begin(mAlteredHeight), std::end(mAlteredHeight), 0); } float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) From 0ce971c1bb71e1c3a7b0f03263267819dff1967d Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 3 Oct 2019 12:54:37 +0300 Subject: [PATCH 16/46] Revert back to less aggressive component-level changes. --- apps/opencs/view/render/terrainstorage.cpp | 195 ++++----------------- apps/opencs/view/render/terrainstorage.hpp | 11 +- components/esmterrain/storage.cpp | 108 +++++++++++- components/esmterrain/storage.hpp | 125 ++----------- 4 files changed, 160 insertions(+), 279 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 5e4ca1365e..3b314935d8 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -72,173 +72,42 @@ namespace CSVRender return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX]; } - void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, - osg::ref_ptr positions, - osg::ref_ptr normals, - osg::ref_ptr colours) - { - // LOD level n means every 2^n-th vertex is kept - size_t increment = static_cast(1) << lodLevel; - - osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); - - int startCellX = static_cast(std::floor(origin.x())); - int startCellY = static_cast(std::floor(origin.y())); - - size_t numVerts = static_cast(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); - - positions->resize(numVerts*numVerts); - normals->resize(numVerts*numVerts); - colours->resize(numVerts*numVerts); - - osg::Vec3f normal; - osg::Vec4ub color; - - float vertY = 0; - float vertX = 0; - - ESMTerrain::LandCache cache; - - float vertY_ = 0; // of current cell corner - for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) - { - float vertX_ = 0; // of current cell corner - for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) - { - const ESMTerrain::LandObject* land = ESMTerrain::Storage::getLand(cellX, cellY, cache); - const ESM::Land::LandData *heightData = 0; - const ESM::Land::LandData *normalData = 0; - const ESM::Land::LandData *colourData = 0; - if (land) - { - heightData = land->getData(ESM::Land::DATA_VHGT); - normalData = land->getData(ESM::Land::DATA_VNML); - colourData = land->getData(ESM::Land::DATA_VCLR); - } - - int rowStart = 0; - int colStart = 0; - // Skip the first row / column unless we're at a chunk edge, - // since this row / column is already contained in a previous cell - // This is only relevant if we're creating a chunk spanning multiple cells - if (vertY_ != 0) - colStart += increment; - if (vertX_ != 0) - rowStart += increment; - - // Only relevant for chunks smaller than (contained in) one cell - rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; - colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; - int rowEnd = std::min(static_cast(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast(ESM::Land::LAND_SIZE)); - int colEnd = std::min(static_cast(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast(ESM::Land::LAND_SIZE)); - - vertY = vertY_; - for (int col=colStart; col= 0 && row < ESM::Land::LAND_SIZE); - assert(col >= 0 && col < ESM::Land::LAND_SIZE); - - assert (vertX < numVerts); - assert (vertY < numVerts); - - float height = defaultHeight; - if (heightData) - height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; - - (*positions)[static_cast(vertX*numVerts + vertY)] - = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - height + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)]); - - if (normalData) - { - for (int i=0; i<3; ++i) - normal[i] = normalData->mNormals[srcArrayIndex+i]; - - normal.normalize(); - } - else - normal = osg::Vec3f(0,0,1); - - // Normals apparently don't connect seamlessly between cells - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixNormal(normal, cellX, cellY, col, row, cache); - - // some corner normals appear to be complete garbage (z < 0) - if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) - averageNormal(normal, cellX, cellY, col, row, cache); - - assert(normal.z() > 0); - - (*normals)[static_cast(vertX*numVerts + vertY)] = normal; - - if (colourData) - { - for (int i=0; i<3; ++i) - color[i] = colourData->mColours[srcArrayIndex+i]; - } - else - { - color.r() = 255; - color.g() = 255; - color.b() = 255; - } - - // Highlight broken height changes - if ( ((col > 0 && row > 0) && - ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + - mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || - abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 )) || - ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && - ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + - mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || - abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 ))) - { - color.r() = 255; - color.g() = 0; - color.b() = 0; - } - - // Unlike normals, colors mostly connect seamlessly between cells, but not always... - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixColour(color, cellX, cellY, col, row, cache); - - color.a() = 255; - - (*colours)[static_cast(vertX*numVerts + vertY)] = color; - - ++vertX; - } - ++vertY; - } - vertX_ = vertX; - } - vertY_ = vertY; - - assert(vertX_ == numVerts); // Ensure we covered whole area - } - assert(vertY_ == numVerts); // Ensure we covered whole area*/ - } - void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) { // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells throw std::runtime_error("getBounds not implemented"); } + void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const + { + // Highlight broken height changes + if ( ((col > 0 && row > 0) && + ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || + abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 )) || + ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && + ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || + abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - + (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 ))) + { + color.r() = 255; + color.g() = 0; + color.b() = 0; + } + } + + float TerrainStorage::getAlteredHeight(int col, int row) const + { + return mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)]; + } } diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 5f808d8434..fe2d94b4a7 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -9,8 +9,6 @@ namespace CSVRender { - class LandCache; - /** * @brief A bridge between the terrain component and OpenCS's terrain data storage. */ @@ -30,13 +28,10 @@ namespace CSVRender virtual osg::ref_ptr getLand (int cellX, int cellY) override; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; - /// Draws temporarily altered land (transient change support) - void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, - osg::ref_ptr positions, - osg::ref_ptr normals, - osg::ref_ptr colours) override; - virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; + + void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override; + float getAlteredHeight(int col, int row) const override; }; } diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 151f0ff91b..52af530f5b 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,6 +16,13 @@ namespace ESMTerrain { + class LandCache + { + public: + typedef std::map, osg::ref_ptr > Map; + Map mMap; + }; + LandObject::LandObject() : mLand(nullptr) , mLoadFlags(0) @@ -91,6 +98,82 @@ namespace ESMTerrain return false; } + void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) + { + while (col >= ESM::Land::LAND_SIZE-1) + { + ++cellY; + col -= ESM::Land::LAND_SIZE-1; + } + while (row >= ESM::Land::LAND_SIZE-1) + { + ++cellX; + row -= ESM::Land::LAND_SIZE-1; + } + while (col < 0) + { + --cellY; + col += ESM::Land::LAND_SIZE-1; + } + while (row < 0) + { + --cellX; + row += ESM::Land::LAND_SIZE-1; + } + + const LandObject* land = getLand(cellX, cellY, cache); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; + if (data) + { + normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalize(); + } + else + normal = osg::Vec3f(0,0,1); + } + + void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row, LandCache& cache) + { + osg::Vec3f n1,n2,n3,n4; + fixNormal(n1, cellX, cellY, col+1, row, cache); + fixNormal(n2, cellX, cellY, col-1, row, cache); + fixNormal(n3, cellX, cellY, col, row+1, cache); + fixNormal(n4, cellX, cellY, col, row-1, cache); + normal = (n1+n2+n3+n4); + normal.normalize(); + } + + void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) + { + if (col == ESM::Land::LAND_SIZE-1) + { + ++cellY; + col = 0; + } + if (row == ESM::Land::LAND_SIZE-1) + { + ++cellX; + row = 0; + } + + const LandObject* land = getLand(cellX, cellY, cache); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; + if (data) + { + color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; + color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; + color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; + } + else + { + color.r() = 255; + color.g() = 255; + color.b() = 255; + } + } + void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, @@ -172,7 +255,7 @@ namespace ESMTerrain (*positions)[static_cast(vertX*numVerts + vertY)] = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, - height); + height + getAlteredHeight(col, row)); if (normalData) { @@ -208,6 +291,8 @@ namespace ESMTerrain color.b() = 255; } + adjustColor(col, row, heightData, color); //Does nothing by default, override in OpenMW-CS + // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixColour(color, cellX, cellY, col, row, cache); @@ -438,6 +523,27 @@ namespace ESMTerrain } + const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) + { + LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); + if (found != cache.mMap.end()) + return found->second; + else + { + found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; + return found->second; + } + } + + void Storage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const + { + } + + float Storage::getAlteredHeight(int col, int row) const + { + return 0; + } + Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { OpenThreads::ScopedLock lock(mLayerInfoMutex); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 019bf0babb..65e531e5c5 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -5,9 +5,6 @@ #include -#include -#include - #include #include @@ -21,6 +18,8 @@ namespace VFS namespace ESMTerrain { + class LandCache; + /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use class LandObject : public osg::Object { @@ -51,13 +50,6 @@ namespace ESMTerrain ESM::Land::LandData mData; }; - class LandCache - { - public: - typedef std::map, osg::ref_ptr > Map; - Map mMap; - }; - /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) /// into the terrain component, converting it on the fly as needed. class Storage : public Terrain::Storage @@ -117,9 +109,25 @@ namespace ESMTerrain virtual int getBlendmapScale(float chunkSize); + float getVertexHeight (const ESM::Land::LandData* data, int x, int y) + { + assert(x < ESM::Land::LAND_SIZE); + assert(y < ESM::Land::LAND_SIZE); + return data->mHeights[y * ESM::Land::LAND_SIZE + x]; + } + private: const VFS::Manager* mVFS; + inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); + inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + + inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); + + virtual void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const; + virtual float getAlteredHeight(int col, int row) const; + // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. // pair @@ -139,103 +147,6 @@ namespace ESMTerrain bool mAutoUseSpecularMaps; Terrain::LayerInfo getLayerInfo(const std::string& texture); - - protected: - - inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) - { - while (col >= ESM::Land::LAND_SIZE-1) - { - ++cellY; - col -= ESM::Land::LAND_SIZE-1; - } - while (row >= ESM::Land::LAND_SIZE-1) - { - ++cellX; - row -= ESM::Land::LAND_SIZE-1; - } - while (col < 0) - { - --cellY; - col += ESM::Land::LAND_SIZE-1; - } - while (row < 0) - { - --cellX; - row += ESM::Land::LAND_SIZE-1; - } - - const LandObject* land = getLand(cellX, cellY, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; - if (data) - { - normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalize(); - } - else - normal = osg::Vec3f(0,0,1); - }; - - inline void fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) - { - if (col == ESM::Land::LAND_SIZE-1) - { - ++cellY; - col = 0; - } - if (row == ESM::Land::LAND_SIZE-1) - { - ++cellX; - row = 0; - } - - const LandObject* land = getLand(cellX, cellY, cache); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; - if (data) - { - color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; - color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; - color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; - } - else - { - color.r() = 255; - color.g() = 255; - color.b() = 255; - } - }; - - inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) - { - osg::Vec3f n1,n2,n3,n4; - fixNormal(n1, cellX, cellY, col+1, row, cache); - fixNormal(n2, cellX, cellY, col-1, row, cache); - fixNormal(n3, cellX, cellY, col, row+1, cache); - fixNormal(n4, cellX, cellY, col, row-1, cache); - normal = (n1+n2+n3+n4); - normal.normalize(); - }; - - inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y) - { - assert(x < ESM::Land::LAND_SIZE); - assert(y < ESM::Land::LAND_SIZE); - return data->mHeights[y * ESM::Land::LAND_SIZE + x]; - }; - - inline const LandObject* getLand(int cellX, int cellY, LandCache& cache) - { - LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); - if (found != cache.mMap.end()) - return found->second; - else - { - found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; - return found->second; - } - }; }; } From 45b0f034c3a415180be2625c1fd27e5929d556b1 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 3 Oct 2019 23:22:19 +0300 Subject: [PATCH 17/46] Split complex if-logic into multiple reasonably named functions --- apps/opencs/view/render/terrainstorage.cpp | 83 +++++++++++++++++----- apps/opencs/view/render/terrainstorage.hpp | 12 ++++ 2 files changed, 77 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 3b314935d8..8938866e36 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -78,27 +78,74 @@ namespace CSVRender throw std::runtime_error("getBounds not implemented"); } + int TerrainStorage::getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const + { + return heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)]; + } + + int TerrainStorage::getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const + { + return heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + + mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)]; + } + + int TerrainStorage::getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const + { + return heightData->mHeights[col*ESM::Land::LAND_SIZE + row + 1] + + mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row + 1)]; + } + + int TerrainStorage::getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const + { + return heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)]; + } + + int TerrainStorage::getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const + { + return heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + + mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)]; + } + + int TerrainStorage::getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const + { + return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData)); + } + + int TerrainStorage::getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const + { + return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData)); + } + + int TerrainStorage::getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const + { + return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData)); + } + + int TerrainStorage::getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const + { + return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData)); + } + + bool TerrainStorage::LeftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const + { + return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit || + getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit; + } + + bool TerrainStorage::RightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const + { + return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit || + getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit; + } + void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const { // Highlight broken height changes - if ( ((col > 0 && row > 0) && - ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + - mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || - abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast((col - 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 )) || - ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && - ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + - mAlteredHeight[static_cast((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || - abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast(col*ESM::Land::LAND_SIZE + row)] - - (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + - mAlteredHeight[static_cast((col + 1)*ESM::Land::LAND_SIZE + row)])) >= 1024 ))) + int heightWarningLimit = 1024; + if (((col > 0 && row > 0) && LeftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) || + ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && RightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData))) { color.r() = 255; color.g() = 0; diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index fe2d94b4a7..703cd9be2d 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -30,6 +30,18 @@ namespace CSVRender virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; + int getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const; + int getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const; + int getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const; + int getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const; + int getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const; + int getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const; + int getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const; + int getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const; + int getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const; + bool LeftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; + bool RightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; + void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override; float getAlteredHeight(int col, int row) const override; }; From bccf36fdbc9eaa3f1e246961d52cc50e742a7820 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 00:30:58 +0300 Subject: [PATCH 18/46] Convert normals calculations mostly to osg::Vec3f --- apps/opencs/view/render/terrainshapemode.cpp | 45 ++++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index e5a92a5de6..0ce009a365 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -327,50 +328,48 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) { - float v1[3]; - float v2[3]; - float normal[3]; + osg::Vec3f v1; + osg::Vec3f v2; + osg::Vec3f normal; float hyp; - v1[0] = 128; - v1[1] = 0; - if (i < ESM::Land::LAND_SIZE - 1) v1[2] = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + v1.x() = 128; + v1.y() = 0; + if (i < ESM::Land::LAND_SIZE - 1) v1.z() = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; if (!noLand && !noCell) - v1[2] = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + v1.z() = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else - v1[2] = 0; + v1.z() = 0; } - v2[0] = 0; - v2[1] = 128; - if (j < ESM::Land::LAND_SIZE - 1) v2[2] = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + v2.x() = 0; + v2.y() = 128; + if (j < ESM::Land::LAND_SIZE - 1) v2.z() = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; if (!noLand && !noCell) - v2[2] = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; + v2.z() = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else - v2[2] = 0; + v2.z() = 0; } - normal[1] = v1[2]*v2[0] - v1[0]*v2[2]; - normal[0] = v1[1]*v2[2] - v1[2]*v2[1]; - normal[2] = v1[0]*v2[1] - v1[1]*v2[0]; + normal.y() = v1.z()*v2.x() - v1.x()*v2.z(); + normal.x() = v1.y()*v2.z() - v1.z()*v2.y(); + normal.z() = v1.x()*v2.y() - v1.y()*v2.x(); - hyp = sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]) / 127.0f; + hyp = normal.length() / 127.0f; - normal[0] /= hyp; - normal[1] /= hyp; - normal[2] /= hyp; + normal /= hyp; - landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = normal[0]; - landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = normal[1]; - landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal[2]; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = normal.x(); + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = normal.y(); + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal.z(); } } if (allowLandShapeEditing(cellId) == true) pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); From 6a44cae5724b654a8b5bdec673cfa71ebca9dba6 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 00:34:07 +0300 Subject: [PATCH 19/46] white iterator loop to C++11 range-based for loop. --- apps/opencs/view/render/pagedworldspacewidget.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 63c7df9093..b5d9234e42 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -816,13 +816,8 @@ float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeight(const CSMWorld::Ce void CSVRender::PagedWorldspaceWidget::resetAllAlteredHeights() { - std::map::iterator iter (mCells.begin()); - - while (iter!=mCells.end()) - { - iter->second->resetAlteredHeights(); - ++iter; - } + for (const auto& cell : mCells) + cell.second->resetAlteredHeights(); } std::vector > CSVRender::PagedWorldspaceWidget::getSelection ( From da4abcd7c1997806ae6718cf13a4aac42ebe3893 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 00:42:16 +0300 Subject: [PATCH 20/46] std::set to std::unique and erase. --- apps/opencs/view/render/terrainshapemode.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 0ce009a365..bb01247ddb 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -247,8 +247,7 @@ void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor) void CSVRender::TerrainShapeMode::applyTerrainEditChanges() { std::sort(mAlteredCells.begin(), mAlteredCells.end()); - std::set removeDuplicates(mAlteredCells.begin(), mAlteredCells.end()); - mAlteredCells.assign(removeDuplicates.begin(), removeDuplicates.end()); + mAlteredCells.erase(std::unique(mAlteredCells.begin(), mAlteredCells.end()), mAlteredCells.end()); CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMWorld::IdTable& landTable = dynamic_cast ( From 388edfd8ccbebdb8a5d445bd40fb5180c2d17572 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 01:22:47 +0300 Subject: [PATCH 21/46] change manual memory management to std::unique_ptr --- apps/opencs/view/render/terrainshapemode.cpp | 32 +++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index bb01247ddb..052a5b67e3 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -977,27 +977,25 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi { for(int inCellX = 0; inCellX < ESM::Land::LAND_SIZE; ++inCellX) { - float* limitedAlteredHeightXAxis = nullptr; - float* limitedAlteredHeightYAxis = nullptr; + 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 = new float(leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightXAxis.reset(new float(leftHeight - limitHeightChange - (thisHeight - thisAlteredHeight))); else if (leftHeight - thisHeight < -limitHeightChange) - limitedAlteredHeightXAxis = new float(leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightXAxis.reset(new float(leftHeight + limitHeightChange - (thisHeight - thisAlteredHeight))); // Check for height limits on y-axis if (upHeight - thisHeight > limitHeightChange) - limitedAlteredHeightYAxis = new float(upHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightYAxis.reset(new float(upHeight - limitHeightChange - (thisHeight - thisAlteredHeight))); else if (upHeight - thisHeight < -limitHeightChange) - limitedAlteredHeightYAxis = new float(upHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightYAxis.reset(new float(upHeight + limitHeightChange - (thisHeight - thisAlteredHeight))); // Limit altered height value based on x or y, whichever is the smallest - compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis, limitedAlteredHeightYAxis, &steepnessIsWithinLimits); - delete limitedAlteredHeightXAxis; - delete limitedAlteredHeightYAxis; + compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis.get(), limitedAlteredHeightYAxis.get(), &steepnessIsWithinLimits); } } } @@ -1008,27 +1006,25 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi { for(int inCellX = ESM::Land::LAND_SIZE - 1; inCellX >= 0; --inCellX) { - float* limitedAlteredHeightXAxis = nullptr; - float* limitedAlteredHeightYAxis = nullptr; + 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) - limitedAlteredHeightXAxis = new float(rightHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightXAxis.reset(new float(rightHeight - limitHeightChange - (thisHeight - thisAlteredHeight))); else if (rightHeight - thisHeight < -limitHeightChange) - limitedAlteredHeightXAxis = new float(rightHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightXAxis.reset(new float(rightHeight + limitHeightChange - (thisHeight - thisAlteredHeight))); // Check for height limits on y-axis if (downHeight - thisHeight > limitHeightChange) - limitedAlteredHeightYAxis = new float(downHeight - limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightYAxis.reset(new float(downHeight - limitHeightChange - (thisHeight - thisAlteredHeight))); else if (downHeight - thisHeight < -limitHeightChange) - limitedAlteredHeightYAxis = new float(downHeight + limitHeightChange - (thisHeight - thisAlteredHeight)); + limitedAlteredHeightYAxis.reset(new float(downHeight + limitHeightChange - (thisHeight - thisAlteredHeight))); // Limit altered height value based on x or y, whichever is the smallest - compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis, limitedAlteredHeightYAxis, &steepnessIsWithinLimits); - delete limitedAlteredHeightXAxis; - delete limitedAlteredHeightYAxis; + compareAndLimit(cellCoords, inCellX, inCellY, limitedAlteredHeightXAxis.get(), limitedAlteredHeightYAxis.get(), &steepnessIsWithinLimits); } } } From c031543420c50966045850c070750f2a7acec41e Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 12:07:47 +0300 Subject: [PATCH 22/46] use enum for brush shapes --- apps/opencs/view/render/terrainshapemode.cpp | 18 +++++++++--------- apps/opencs/view/render/terrainshapemode.hpp | 8 ++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 052a5b67e3..2590a10c54 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -393,7 +393,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair if(allowLandShapeEditing(cellId)==true) { - if (mBrushShape == 0) + if (mBrushShape == BrushShape_Point) { int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); @@ -403,7 +403,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } - if (mBrushShape == 1) + if (mBrushShape == BrushShape_Square) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -421,7 +421,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair } } - if (mBrushShape == 2) + if (mBrushShape == BrushShape_Circle) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -447,7 +447,7 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair } } } - if (mBrushShape == 3) + if (mBrushShape == BrushShape_Custom) { if(!mCustomBrushShape.empty()) { @@ -1037,12 +1037,12 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& int r = mBrushSize / 2; std::vector> selections; - if (mBrushShape == 0) + if (mBrushShape == BrushShape_Point) { selections.emplace_back(vertexCoords); } - if (mBrushShape == 1) + if (mBrushShape == BrushShape_Square) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -1053,7 +1053,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } } - if (mBrushShape == 2) + if (mBrushShape == BrushShape_Circle) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -1067,7 +1067,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } } - if (mBrushShape == 3) + if (mBrushShape == BrushShape_Custom) { if(!mCustomBrushShape.empty()) { @@ -1196,7 +1196,7 @@ void CSVRender::TerrainShapeMode::setBrushShape(int brushShape) mBrushShape = brushShape; //Set custom brush shape - if (mBrushShape == 3 && !mTerrainShapeSelection->getTerrainSelection().empty()) + if (mBrushShape == BrushShape_Custom && !mTerrainShapeSelection->getTerrainSelection().empty()) { auto terrainSelection = mTerrainShapeSelection->getTerrainSelection(); int selectionCenterX = 0; diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 63e14de31c..cec40b1367 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -46,6 +46,14 @@ namespace CSVRender InteractionType_None }; + enum BrushShape + { + BrushShape_Point = 1, + BrushShape_Square = 2, + BrushShape_Circle = 3, + BrushShape_Custom = 4 + }; + /// Editmode for terrain shape grid TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); From bae8636ec068944ab8b9424eb453c0e5c225cfb7 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 12:43:30 +0300 Subject: [PATCH 23/46] Fix brusshape enum values --- apps/opencs/view/render/terrainshapemode.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index cec40b1367..b6d2ffde1d 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -48,10 +48,10 @@ namespace CSVRender enum BrushShape { - BrushShape_Point = 1, - BrushShape_Square = 2, - BrushShape_Circle = 3, - BrushShape_Custom = 4 + BrushShape_Point = 0, + BrushShape_Square = 1, + BrushShape_Circle = 2, + BrushShape_Custom = 3 }; /// Editmode for terrain shape grid From 4f9ec24e4154258acf0b43f8b9034b17179a69a6 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 12:51:57 +0300 Subject: [PATCH 24/46] use enum for brushshape at scenetoolshapebrush --- apps/opencs/view/widget/scenetoolshapebrush.cpp | 10 +++++----- apps/opencs/view/widget/scenetoolshapebrush.hpp | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index 3e2f4e99db..bc7b04c734 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -58,7 +58,7 @@ CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent) : QFrame(parent, Qt::Popup), - mBrushShape(0), + mBrushShape(BrushShape_Point), mBrushSize(1), mDocument(document) { @@ -151,10 +151,10 @@ void CSVWidget::ShapeBrushWindow::setBrushSize(int brushSize) void CSVWidget::ShapeBrushWindow::setBrushShape() { - if(mButtonPoint->isChecked()) mBrushShape = 0; - if(mButtonSquare->isChecked()) mBrushShape = 1; - if(mButtonCircle->isChecked()) mBrushShape = 2; - if(mButtonCustom->isChecked()) mBrushShape = 3; + if(mButtonPoint->isChecked()) mBrushShape = BrushShape_Point; + if(mButtonSquare->isChecked()) mBrushShape = BrushShape_Square; + if(mButtonCircle->isChecked()) mBrushShape = BrushShape_Circle; + if(mButtonCustom->isChecked()) mBrushShape = BrushShape_Custom; emit passBrushShape(mBrushShape); } diff --git a/apps/opencs/view/widget/scenetoolshapebrush.hpp b/apps/opencs/view/widget/scenetoolshapebrush.hpp index bdb6b1309f..44fd66df10 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.hpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.hpp @@ -53,6 +53,15 @@ namespace CSVWidget Q_OBJECT public: + + enum BrushShape + { + BrushShape_Point = 0, + BrushShape_Square = 1, + BrushShape_Circle = 2, + BrushShape_Custom = 3 + }; + ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent = 0); void configureButtonInitialSettings(QPushButton *button); From 5b9debc5545d7c85a955c3449ca2c63c58bcc042 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 4 Oct 2019 12:53:54 +0300 Subject: [PATCH 25/46] use enum for mbrushshape initialization --- apps/opencs/view/render/terrainshapemode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 2590a10c54..382bf186c3 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -45,7 +45,7 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), mBrushSize(0), - mBrushShape(0), + mBrushShape(BrushShape_Point), mShapeBrushScenetool(0), mDragMode(InteractionType_None), mParentNode(parentNode), From 8acfa2600f30d04f948fd6607711ca5babad3c91 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Mon, 7 Oct 2019 02:57:09 +0300 Subject: [PATCH 26/46] Terrain shape editing related fixes --- CHANGELOG_PR.md | 2 + apps/opencs/model/world/columnimp.cpp | 4 +- apps/opencs/view/render/terrainshapemode.cpp | 121 +++++++++++------- apps/opencs/view/render/terrainshapemode.hpp | 27 ++-- apps/opencs/view/render/terrainstorage.cpp | 8 +- apps/opencs/view/render/terrainstorage.hpp | 4 +- apps/opencs/view/widget/brushshapes.hpp | 14 ++ .../view/widget/scenetoolshapebrush.cpp | 15 ++- .../view/widget/scenetoolshapebrush.hpp | 17 +-- 9 files changed, 126 insertions(+), 86 deletions(-) create mode 100644 apps/opencs/view/widget/brushshapes.hpp diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 8862a8448c..71677af501 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -40,6 +40,8 @@ New Features: New Editor Features: - "Faction Ranks" table for "Faction" records (#4209) +- Changes to height editing can be cancelled without changes to data (press esc to cancel) (#4840) +- Land heightmap/shape editing and vertex selection (#5170) Bug Fixes: - Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index b202a97d93..e610ef2235 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -89,10 +89,12 @@ namespace CSMWorld DataType values(Size, 0); - if (land.isDataLoaded(Land::DATA_WNAM)) + if (land.mDataTypes & Land::DATA_WNAM) { for (int i = 0; i < Size; ++i) + { values[i] = land.mWnam[i]; + } } QVariant variant; diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 382bf186c3..171ad5a27b 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -18,6 +18,7 @@ #include #include +#include "../widget/brushshapes.hpp" #include "../widget/modebutton.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolshapebrush.hpp" @@ -44,14 +45,14 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), - mBrushSize(0), - mBrushShape(BrushShape_Point), + mBrushSize(1), + mBrushShape(CSVWidget::BrushShape_Point), mShapeBrushScenetool(0), mDragMode(InteractionType_None), mParentNode(parentNode), mIsEditing(false), mTotalDiffY(0), - mShapeEditTool(0), + mShapeEditTool(ShapeEditTool_Drag), mShapeEditToolStrength(8), mTargetHeight(0) { @@ -69,7 +70,7 @@ void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) mShapeBrushScenetool = new CSVWidget::SceneToolShapeBrush (toolbar, "scenetoolshapebrush", getWorldspaceWidget().getDocument()); connect(mShapeBrushScenetool, SIGNAL (clicked()), mShapeBrushScenetool, SLOT (activate())); connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int))); - connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setBrushShape(int))); + connect(mShapeBrushScenetool->mShapeBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setBrushShape(CSVWidget::BrushShape))); connect(mShapeBrushScenetool->mShapeBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int))); connect(mShapeBrushScenetool->mShapeBrushWindow->mToolSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(setShapeEditTool(int))); connect(mShapeBrushScenetool->mShapeBrushWindow->mToolStrengthSlider, SIGNAL(valueChanged(int)), this, SLOT(setShapeEditToolStrength(int))); @@ -94,12 +95,12 @@ void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult& if (hit.hit && hit.tag == 0) { - if (mShapeEditTool == 4) + if (mShapeEditTool == ShapeEditTool_Flatten) setFlattenToolTargetHeight(hit); - if (mDragMode == InteractionType_PrimaryEdit && mShapeEditTool > 0) + if (mDragMode == InteractionType_PrimaryEdit && mShapeEditTool != ShapeEditTool_Drag) { std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); - if (mShapeEditTool > 0) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); applyTerrainEditChanges(); } @@ -149,7 +150,7 @@ bool CSVRender::TerrainShapeMode::primaryEditStartDrag (const QPoint& pos) { mEditingPos = hit.worldPos; mIsEditing = true; - if (mShapeEditTool == 4) + if (mShapeEditTool == ShapeEditTool_Flatten) setFlattenToolTargetHeight(hit); } @@ -194,8 +195,8 @@ void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); mTotalDiffY += diffY; - if (mIsEditing == true && mShapeEditTool == 0) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true); - if (mIsEditing == true && mShapeEditTool > 0) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + if (mIsEditing == true && mShapeEditTool == ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true); + if (mIsEditing == true && mShapeEditTool != ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); } if (mDragMode == InteractionType_PrimarySelect) @@ -256,6 +257,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + int landMapLodColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandMapLodIndex); int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); QUndoStack& undoStack = document.getUndoStack(); @@ -296,7 +298,9 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + const CSMWorld::LandMapLodColumn::DataType landMapLodPointer = landTable.data(landTable.getModelIndex(cellId, landMapLodColumn)).value(); CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); + CSMWorld::LandMapLodColumn::DataType mapLodShapeNew(landMapLodPointer); for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) @@ -311,7 +315,15 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } } } - if (allowLandShapeEditing(cellId) == true) pushEditToCommand(landShapeNew, document, landTable, cellId); + for(int i = 0; i < ESM::Land::LAND_GLOBAL_MAP_LOD_SIZE; ++i) + { + mapLodShapeNew[i] = landMapLodPointer[i]; //TO-DO: generate a new mWnam based on new heights + } + if (allowLandShapeEditing(cellId) == true) + { + pushEditToCommand(landShapeNew, document, landTable, cellId); + pushLodToCommand(mapLodShapeNew, document, landTable, cellId); + } } for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) @@ -358,9 +370,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() v2.z() = 0; } - normal.y() = v1.z()*v2.x() - v1.x()*v2.z(); - normal.x() = v1.y()*v2.z() - v1.z()*v2.y(); - normal.z() = v1.x()*v2.y() - v1.y()*v2.x(); + normal = v1 ^ v2; hyp = normal.length() / 127.0f; @@ -388,22 +398,22 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) { - if (mShapeEditTool == 0) paged->resetAllAlteredHeights(); + if (mShapeEditTool == ShapeEditTool_Drag) paged->resetAllAlteredHeights(); } if(allowLandShapeEditing(cellId)==true) { - if (mBrushShape == BrushShape_Point) + if (mBrushShape == CSVWidget::BrushShape_Point) { int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); - if (mShapeEditTool == 0) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == 1 || mShapeEditTool == 2) alterHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } - if (mBrushShape == BrushShape_Square) + if (mBrushShape == CSVWidget::BrushShape_Square) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -413,15 +423,15 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); - if (mShapeEditTool == 0) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == 1 || mShapeEditTool == 2) alterHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } } } - if (mBrushShape == BrushShape_Circle) + if (mBrushShape == CSVWidget::BrushShape_Circle) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -436,18 +446,19 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); float distancePerRadius = 1.0f * distance / r; float smoothedByDistance = 0.0f; - if (mShapeEditTool == 0) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); - if (mShapeEditTool == 1 || mShapeEditTool == 2) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); if (distance <= r) { - if (mShapeEditTool >= 0 && mShapeEditTool < 3) alterHeight(cellCoords, x, y, smoothedByDistance); - if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + if (mShapeEditTool == ShapeEditTool_Drag || mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) + alterHeight(cellCoords, x, y, smoothedByDistance); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } } } } - if (mBrushShape == BrushShape_Custom) + if (mBrushShape == CSVWidget::BrushShape_Custom) { if(!mCustomBrushShape.empty()) { @@ -457,10 +468,10 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second); - if (mShapeEditTool == 0) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == 1 || mShapeEditTool == 2) alterHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 3) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == 4) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } } } @@ -506,7 +517,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if (useTool) { mAlteredCells.emplace_back(cellCoords); - if (mShapeEditTool == 0) + if (mShapeEditTool == ShapeEditTool_Drag) { // Get distance from modified land, alter land change based on zoom osg::Vec3d eye, center, up; @@ -514,9 +525,9 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c osg::Vec3d distance = eye - mEditingPos; alteredHeight = alteredHeight * (distance.length() / 500); } - if (mShapeEditTool == 1) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; - if (mShapeEditTool == 2) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) - alteredHeight; - if (mShapeEditTool == 3) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; + if (mShapeEditTool == ShapeEditTool_PaintToRaise) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; + if (mShapeEditTool == ShapeEditTool_PaintToLower) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) - alteredHeight; + if (mShapeEditTool == ShapeEditTool_Smooth) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; } if (inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1) @@ -1037,12 +1048,12 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& int r = mBrushSize / 2; std::vector> selections; - if (mBrushShape == BrushShape_Point) + if (mBrushShape == CSVWidget::BrushShape_Point) { selections.emplace_back(vertexCoords); } - if (mBrushShape == BrushShape_Square) + if (mBrushShape == CSVWidget::BrushShape_Square) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -1053,7 +1064,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } } - if (mBrushShape == BrushShape_Circle) + if (mBrushShape == CSVWidget::BrushShape_Circle) { for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { @@ -1067,7 +1078,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } } - if (mBrushShape == BrushShape_Custom) + if (mBrushShape == CSVWidget::BrushShape_Custom) { if(!mCustomBrushShape.empty()) { @@ -1084,7 +1095,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, - CSMWorld::IdTable& landTable, std::string cellId) + CSMWorld::IdTable& landTable, const std::string& cellId) { QVariant changedLand; changedLand.setValue(newLandGrid); @@ -1096,7 +1107,7 @@ void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsC } void CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document, - CSMWorld::IdTable& landTable, std::string cellId) + CSMWorld::IdTable& landTable, const std::string& cellId) { QVariant changedLand; changedLand.setValue(newLandGrid); @@ -1107,7 +1118,19 @@ void CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandN undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); } -bool CSVRender::TerrainShapeMode::allowLandShapeEditing(std::string cellId) +void CSVRender::TerrainShapeMode::pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, const std::string& cellId) +{ + QVariant changedLod; + changedLod.setValue(newLandMapLod); + + QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandMapLodIndex))); + + QUndoStack& undoStack = document.getUndoStack(); + undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLod)); +} + +bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellId) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMWorld::IdTable& landTable = dynamic_cast ( @@ -1191,12 +1214,12 @@ void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) mBrushSize = brushSize; } -void CSVRender::TerrainShapeMode::setBrushShape(int brushShape) +void CSVRender::TerrainShapeMode::setBrushShape(CSVWidget::BrushShape brushShape) { mBrushShape = brushShape; //Set custom brush shape - if (mBrushShape == BrushShape_Custom && !mTerrainShapeSelection->getTerrainSelection().empty()) + if (mBrushShape == CSVWidget::BrushShape_Custom && !mTerrainShapeSelection->getTerrainSelection().empty()) { auto terrainSelection = mTerrainShapeSelection->getTerrainSelection(); int selectionCenterX = 0; diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index b6d2ffde1d..912aec2ce6 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -12,11 +12,11 @@ #ifndef Q_MOC_RUN #include "../../model/world/data.hpp" #include "../../model/world/land.hpp" - #include "../../model/doc/document.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/landtexture.hpp" +#include "../widget/brushshapes.hpp" #endif #include "terrainselection.hpp" @@ -46,12 +46,13 @@ namespace CSVRender InteractionType_None }; - enum BrushShape + enum ShapeEditTool { - BrushShape_Point = 0, - BrushShape_Square = 1, - BrushShape_Circle = 2, - BrushShape_Custom = 3 + ShapeEditTool_Drag = 0, + ShapeEditTool_PaintToRaise = 1, + ShapeEditTool_PaintToLower = 2, + ShapeEditTool_Smooth = 3, + ShapeEditTool_Flatten = 4 }; /// Editmode for terrain shape grid @@ -124,20 +125,24 @@ namespace CSVRender /// Push terrain shape edits to command macro void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, - CSMWorld::IdTable& landTable, std::string cellId); + CSMWorld::IdTable& landTable, const std::string& cellId); /// Push land normals edits to command macro void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document, - CSMWorld::IdTable& landTable, std::string cellId); + CSMWorld::IdTable& landTable, const std::string& cellId); + + /// Generate new land map LOD + void pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, const std::string& cellId); /// Create new cell and land if needed - bool allowLandShapeEditing(std::string textureFileName); + bool allowLandShapeEditing(const std::string& textureFileName); private: std::string mCellId; std::string mBrushTexture; int mBrushSize; - int mBrushShape; + CSVWidget::BrushShape mBrushShape; std::vector> mCustomBrushShape; CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool; int mDragMode; @@ -158,7 +163,7 @@ namespace CSVRender public slots: void setBrushSize(int brushSize); - void setBrushShape(int brushShape); + void setBrushShape(CSVWidget::BrushShape brushShape); void setShapeEditTool(int shapeEditTool); void setShapeEditToolStrength(int shapeEditToolStrength); }; diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 8938866e36..7f5dc1208a 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -128,13 +128,13 @@ namespace CSVRender return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData)); } - bool TerrainStorage::LeftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const + bool TerrainStorage::leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const { return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit || getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit; } - bool TerrainStorage::RightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const + bool TerrainStorage::rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const { return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit || getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit; @@ -144,8 +144,8 @@ namespace CSVRender { // Highlight broken height changes int heightWarningLimit = 1024; - if (((col > 0 && row > 0) && LeftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) || - ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && RightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData))) + if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) || + ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData))) { color.r() = 255; color.g() = 0; diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 703cd9be2d..9e5226d5f7 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -39,8 +39,8 @@ namespace CSVRender int getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const; int getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const; int getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const; - bool LeftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; - bool RightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; + bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; + bool rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override; float getAlteredHeight(int col, int row) const override; diff --git a/apps/opencs/view/widget/brushshapes.hpp b/apps/opencs/view/widget/brushshapes.hpp new file mode 100644 index 0000000000..2e931157c8 --- /dev/null +++ b/apps/opencs/view/widget/brushshapes.hpp @@ -0,0 +1,14 @@ +#ifndef CSV_WIDGET_BRUSHSHAPES_H +#define CSV_WIDGET_BRUSHSHAPES_H + +namespace CSVWidget +{ + enum BrushShape + { + BrushShape_Point, + BrushShape_Square, + BrushShape_Circle, + BrushShape_Custom + }; +} +#endif diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index bc7b04c734..3b9d908ac1 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -21,6 +21,7 @@ #include #include +#include "brushshapes.hpp" #include "scenetool.hpp" #include "../../model/doc/document.hpp" @@ -58,7 +59,7 @@ CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent) : QFrame(parent, Qt::Popup), - mBrushShape(BrushShape_Point), + mBrushShape(CSVWidget::BrushShape_Point), mBrushSize(1), mDocument(document) { @@ -169,7 +170,7 @@ CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const mShapeBrushWindow(new ShapeBrushWindow(document, this)) { setAcceptDrops(true); - connect(mShapeBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int))); + connect(mShapeBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape))); setButtonIcon(mShapeBrushWindow->mBrushShape); mPanel = new QFrame (this, Qt::Popup); @@ -199,31 +200,31 @@ CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const } -void CSVWidget::SceneToolShapeBrush::setButtonIcon (int brushShape) +void CSVWidget::SceneToolShapeBrush::setButtonIcon (CSVWidget::BrushShape brushShape) { QString tooltip = "Change brush settings

Currently selected: "; switch (brushShape) { - case 0: + case BrushShape_Point: setIcon (QIcon (QPixmap (":scenetoolbar/brush-point"))); tooltip += mShapeBrushWindow->toolTipPoint; break; - case 1: + case BrushShape_Square: setIcon (QIcon (QPixmap (":scenetoolbar/brush-square"))); tooltip += mShapeBrushWindow->toolTipSquare; break; - case 2: + case BrushShape_Circle: setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle"))); tooltip += mShapeBrushWindow->toolTipCircle; break; - case 3: + case BrushShape_Custom: setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom"))); tooltip += mShapeBrushWindow->toolTipCustom; diff --git a/apps/opencs/view/widget/scenetoolshapebrush.hpp b/apps/opencs/view/widget/scenetoolshapebrush.hpp index 44fd66df10..19d24111df 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.hpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.hpp @@ -16,6 +16,7 @@ #include #ifndef Q_MOC_RUN +#include "brushshapes.hpp" #include "scenetool.hpp" #include "../../model/doc/document.hpp" @@ -54,24 +55,16 @@ namespace CSVWidget public: - enum BrushShape - { - BrushShape_Point = 0, - BrushShape_Square = 1, - BrushShape_Circle = 2, - BrushShape_Custom = 3 - }; - ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent = 0); void configureButtonInitialSettings(QPushButton *button); const QString toolTipPoint = "Paint single point"; const QString toolTipSquare = "Paint with square brush"; const QString toolTipCircle = "Paint with circle brush"; - const QString toolTipCustom = "Paint custom selection (not implemented yet)"; + const QString toolTipCustom = "Paint with custom brush, defined by terrain selection"; private: - int mBrushShape; + CSVWidget::BrushShape mBrushShape; int mBrushSize; CSMDoc::Document& mDocument; QGroupBox *mHorizontalGroupBox; @@ -92,7 +85,7 @@ namespace CSVWidget signals: void passBrushSize (int brushSize); - void passBrushShape(int brushShape); + void passBrushShape(CSVWidget::BrushShape brushShape); }; class SceneToolShapeBrush : public SceneTool @@ -122,7 +115,7 @@ namespace CSVWidget friend class CSVRender::TerrainShapeMode; public slots: - void setButtonIcon(int brushShape); + void setButtonIcon(CSVWidget::BrushShape brushShape); void clicked (const QModelIndex& index); virtual void activate(); From 96be82a04719478217789628fcf19292cf8720c2 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Mon, 7 Oct 2019 22:32:50 +0300 Subject: [PATCH 27/46] Remove terrain vertex selection and brush button when exiting editMode --- apps/opencs/view/render/terrainshapemode.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 171ad5a27b..8da50ada3c 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -82,6 +82,16 @@ void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) void CSVRender::TerrainShapeMode::deactivate(CSVWidget::SceneToolbar* toolbar) { + if(mShapeBrushScenetool) + { + toolbar->removeTool (mShapeBrushScenetool); + } + + if (mTerrainShapeSelection) + { + mTerrainShapeSelection.reset(); + } + EditMode::deactivate(toolbar); } From 8a9ca0b3ec1793a81773bbc74ef16570c6ec15b7 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Mon, 7 Oct 2019 23:12:21 +0300 Subject: [PATCH 28/46] Generate new WNAM record based on new land heights, add comments --- apps/opencs/view/render/terrainshapemode.cpp | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 8da50ada3c..419d222c0a 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -311,6 +311,8 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() const CSMWorld::LandMapLodColumn::DataType landMapLodPointer = landTable.data(landTable.getModelIndex(cellId, landMapLodColumn)).value(); CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); CSMWorld::LandMapLodColumn::DataType mapLodShapeNew(landMapLodPointer); + + // Generate land height record for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) @@ -325,9 +327,24 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } } } - for(int i = 0; i < ESM::Land::LAND_GLOBAL_MAP_LOD_SIZE; ++i) + + // Generate WNAM record + int sqrtLandGlobalMapLodSize = sqrt(ESM::Land::LAND_GLOBAL_MAP_LOD_SIZE); + for(int i = 0; i < sqrtLandGlobalMapLodSize; ++i) { - mapLodShapeNew[i] = landMapLodPointer[i]; //TO-DO: generate a new mWnam based on new heights + for(int j = 0; j < sqrtLandGlobalMapLodSize; ++j) + { + int col = (static_cast(j) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1); + int row = (static_cast(i) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1); + signed char lodHeight = 0; + float floatLodHeight = 0; + if (landShapeNew[col * ESM::Land::LAND_SIZE + row] > 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 128; + if (landShapeNew[col * ESM::Land::LAND_SIZE + row] <= 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 16; + if (floatLodHeight > std::numeric_limits::max()) lodHeight = std::numeric_limits::max(); + else if (floatLodHeight < std::numeric_limits::min()) lodHeight = std::numeric_limits::min(); + else lodHeight = static_cast(floatLodHeight); + mapLodShapeNew[j * sqrtLandGlobalMapLodSize + i] = lodHeight; + } } if (allowLandShapeEditing(cellId) == true) { @@ -345,6 +362,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value(); CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer); + // Generate land normals record for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) From 6a3070e68073b9a44e57f35fc9b00d7a842e48eb Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Mon, 7 Oct 2019 23:48:02 +0300 Subject: [PATCH 29/46] Fix shape brush setting description --- apps/opencs/model/prefs/state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 35a8ef2df2..b672855137 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -248,7 +248,7 @@ void CSMPrefs::State::declare() addValues (landeditOutsideVisibleCell); declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). setMin (1); - declareInt ("shapebrush-maximumsize", "Maximum texture brush size", 100). + declareInt ("shapebrush-maximumsize", "Maximum shape brush size", 100). setMin (1); declareBool ("open-list-view", "Open displays list view", false). setTooltip ("When opening a reference from the scene view, it will open the" From c2de645c8cb8aa4790317ff61f5ee4f714072bdd Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 9 Oct 2019 01:31:13 +0300 Subject: [PATCH 30/46] override, virtual -> final, remove unused variables, public to private. --- apps/opencs/view/render/terrainshapemode.cpp | 2 +- apps/opencs/view/render/terrainshapemode.hpp | 20 ++++++++++---------- apps/opencs/view/render/terrainstorage.cpp | 2 -- apps/opencs/view/render/terrainstorage.hpp | 12 ++++++------ 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 419d222c0a..934388acfc 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -47,7 +47,7 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidge : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), mBrushSize(1), mBrushShape(CSVWidget::BrushShape_Point), - mShapeBrushScenetool(0), + mShapeBrushScenetool(nullptr), mDragMode(InteractionType_None), mParentNode(parentNode), mIsEditing(false), diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 912aec2ce6..8af81dff9b 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -72,24 +72,25 @@ namespace CSVRender void deactivate(CSVWidget::SceneToolbar*); /// Start shape editing command macro - virtual bool primaryEditStartDrag (const QPoint& pos); + bool primaryEditStartDrag (const QPoint& pos) final; - virtual bool secondaryEditStartDrag (const QPoint& pos); - virtual bool primarySelectStartDrag (const QPoint& pos); - virtual bool secondarySelectStartDrag (const QPoint& pos); + bool secondaryEditStartDrag (const QPoint& pos) final; + bool primarySelectStartDrag (const QPoint& pos) final; + bool secondarySelectStartDrag (const QPoint& pos) final; /// Handle shape edit behavior during dragging - virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); + void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final; /// End shape editing command macro - virtual void dragCompleted(const QPoint& pos); + void dragCompleted(const QPoint& pos) final; /// Cancel shape editing, and reset all pending changes - virtual void dragAborted(); + void dragAborted() final; - virtual void dragWheel (int diff, double speedFactor); - virtual void dragMoveEvent (QDragMoveEvent *event); + void dragWheel (int diff, double speedFactor) final; + void dragMoveEvent (QDragMoveEvent *event) final; + private: /// Move pending alteredHeights changes to omwgame/omeaddon -data void applyTerrainEditChanges(); @@ -138,7 +139,6 @@ namespace CSVRender /// Create new cell and land if needed bool allowLandShapeEditing(const std::string& textureFileName); - private: std::string mCellId; std::string mBrushTexture; int mBrushSize; diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 7f5dc1208a..65c89ae5c0 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -13,8 +13,6 @@ namespace CSVRender { - const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; - TerrainStorage::TerrainStorage(const CSMWorld::Data &data) : ESMTerrain::Storage(data.getResourceSystem()->getVFS()) , mData(data) diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 9e5226d5f7..032261ad47 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -16,7 +16,6 @@ namespace CSVRender { public: TerrainStorage(const CSMWorld::Data& data); - std::array mAlteredHeight; void setAlteredHeight(int inCellX, int inCellY, float heightMap); void resetHeights(); float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); @@ -24,11 +23,12 @@ namespace CSVRender private: const CSMWorld::Data& mData; + std::array mAlteredHeight; - virtual osg::ref_ptr getLand (int cellX, int cellY) override; - virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; + osg::ref_ptr getLand (int cellX, int cellY) final; + const ESM::LandTexture* getLandTexture(int index, short plugin) final; - virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; + void getBounds(float& minX, float& maxX, float& minY, float& maxY) final; int getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const; int getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const; @@ -42,8 +42,8 @@ namespace CSVRender bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; bool rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; - void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override; - float getAlteredHeight(int col, int row) const override; + void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const final; + float getAlteredHeight(int col, int row) const final; }; } From 16ba32ffd5ee17ef9346180c5b9da990f6880819 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 9 Oct 2019 01:46:28 +0300 Subject: [PATCH 31/46] assignments to member initialization list --- apps/opencs/view/widget/scenetoolshapebrush.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index 3b9d908ac1..fcafc4cf04 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -35,19 +35,19 @@ CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent) - : QGroupBox(title, parent) + : QGroupBox(title, parent), + mLayoutSliderSize(new QHBoxLayout), + mBrushSizeSlider(new QSlider(Qt::Horizontal)), + mBrushSizeSpinBox(new QSpinBox) { - mBrushSizeSlider = new QSlider(Qt::Horizontal); mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); mBrushSizeSlider->setTickInterval(10); mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); mBrushSizeSlider->setSingleStep(1); - mBrushSizeSpinBox = new QSpinBox; mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); mBrushSizeSpinBox->setSingleStep(1); - mLayoutSliderSize = new QHBoxLayout; mLayoutSliderSize->addWidget(mBrushSizeSlider); mLayoutSliderSize->addWidget(mBrushSizeSpinBox); From a1348d94f9185324b890dfd3e29dade0f4352b2d Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 11 Oct 2019 00:15:32 +0300 Subject: [PATCH 32/46] add missing final specifiers --- apps/opencs/view/render/terrainshapemode.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 8af81dff9b..95ffa572ef 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -58,18 +58,18 @@ namespace CSVRender /// Editmode for terrain shape grid TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); - void primaryOpenPressed (const WorldspaceHitResult& hit); + void primaryOpenPressed (const WorldspaceHitResult& hit) final; /// Create single command for one-click shape editing - void primaryEditPressed (const WorldspaceHitResult& hit); + void primaryEditPressed (const WorldspaceHitResult& hit) final; /// Open brush settings window - void primarySelectPressed(const WorldspaceHitResult&); + void primarySelectPressed(const WorldspaceHitResult&) final; - void secondarySelectPressed(const WorldspaceHitResult&); + void secondarySelectPressed(const WorldspaceHitResult&) final; - void activate(CSVWidget::SceneToolbar*); - void deactivate(CSVWidget::SceneToolbar*); + void activate(CSVWidget::SceneToolbar*) final; + void deactivate(CSVWidget::SceneToolbar*) final; /// Start shape editing command macro bool primaryEditStartDrag (const QPoint& pos) final; From 71eff60d22c2465ab048fd52de624e273c276224 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 11 Oct 2019 00:41:33 +0300 Subject: [PATCH 33/46] Do in-class init. if possible, mLayoutSliderSize -> layoutSliderSize --- apps/opencs/view/render/terrainshapemode.cpp | 11 +---------- apps/opencs/view/render/terrainshapemode.hpp | 18 +++++++++--------- .../opencs/view/widget/scenetoolshapebrush.cpp | 14 +++++--------- .../opencs/view/widget/scenetoolshapebrush.hpp | 9 ++++----- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 934388acfc..701e704bbd 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -45,16 +45,7 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), - mBrushSize(1), - mBrushShape(CSVWidget::BrushShape_Point), - mShapeBrushScenetool(nullptr), - mDragMode(InteractionType_None), - mParentNode(parentNode), - mIsEditing(false), - mTotalDiffY(0), - mShapeEditTool(ShapeEditTool_Drag), - mShapeEditToolStrength(8), - mTargetHeight(0) + mParentNode(parentNode) { } diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 95ffa572ef..8ee9625ffc 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -141,20 +141,20 @@ namespace CSVRender std::string mCellId; std::string mBrushTexture; - int mBrushSize; - CSVWidget::BrushShape mBrushShape; + int mBrushSize = 1; + CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; std::vector> mCustomBrushShape; - CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool; - int mDragMode; + CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool = nullptr; + int mDragMode = InteractionType_None; osg::Group* mParentNode; - bool mIsEditing; + bool mIsEditing = false; std::unique_ptr mTerrainShapeSelection; - int mTotalDiffY; + int mTotalDiffY = 0; std::vector mAlteredCells; osg::Vec3d mEditingPos; - int mShapeEditTool; - int mShapeEditToolStrength; - int mTargetHeight; + int mShapeEditTool = ShapeEditTool_Drag; + int mShapeEditToolStrength = 8; + int mTargetHeight = 0; PagedWorldspaceWidget& getPagedWorldspaceWidget(); diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index fcafc4cf04..e4647d600d 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -35,10 +35,7 @@ CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent) - : QGroupBox(title, parent), - mLayoutSliderSize(new QHBoxLayout), - mBrushSizeSlider(new QSlider(Qt::Horizontal)), - mBrushSizeSpinBox(new QSpinBox) + : QGroupBox(title, parent) { mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); mBrushSizeSlider->setTickInterval(10); @@ -48,19 +45,18 @@ CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); mBrushSizeSpinBox->setSingleStep(1); - mLayoutSliderSize->addWidget(mBrushSizeSlider); - mLayoutSliderSize->addWidget(mBrushSizeSpinBox); + QHBoxLayout *layoutSliderSize = new QHBoxLayout; + layoutSliderSize->addWidget(mBrushSizeSlider); + layoutSliderSize->addWidget(mBrushSizeSpinBox); connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int))); connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int))); - setLayout(mLayoutSliderSize); + setLayout(layoutSliderSize); } CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent) : QFrame(parent, Qt::Popup), - mBrushShape(CSVWidget::BrushShape_Point), - mBrushSize(1), mDocument(document) { mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this); diff --git a/apps/opencs/view/widget/scenetoolshapebrush.hpp b/apps/opencs/view/widget/scenetoolshapebrush.hpp index 19d24111df..2c027baf02 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.hpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.hpp @@ -40,9 +40,8 @@ namespace CSVWidget ShapeBrushSizeControls(const QString &title, QWidget *parent); private: - QHBoxLayout *mLayoutSliderSize; - QSlider *mBrushSizeSlider; - QSpinBox *mBrushSizeSpinBox; + QSlider *mBrushSizeSlider = new QSlider(Qt::Horizontal); + QSpinBox *mBrushSizeSpinBox = new QSpinBox; friend class SceneToolShapeBrush; friend class CSVRender::TerrainShapeMode; @@ -64,8 +63,8 @@ namespace CSVWidget const QString toolTipCustom = "Paint with custom brush, defined by terrain selection"; private: - CSVWidget::BrushShape mBrushShape; - int mBrushSize; + CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + int mBrushSize = 1; CSMDoc::Document& mDocument; QGroupBox *mHorizontalGroupBox; QComboBox *mToolSelector; From 2d34b63b0b15b0aa040eed0aaf01eacca8ad26fe Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 12 Oct 2019 15:25:34 +0300 Subject: [PATCH 34/46] Fix logic of land loading when no cell, land or landdata. Fix also draw. --- apps/opencs/view/render/cell.cpp | 1 - apps/opencs/view/render/terrainshapemode.cpp | 475 ++++++++++++------- apps/opencs/view/render/terrainshapemode.hpp | 25 +- 3 files changed, 336 insertions(+), 165 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 94d7bfa51b..056c50e45f 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -149,7 +149,6 @@ void CSVRender::Cell::updateLand() } // No land data - mLandDeleted = true; unloadLand(); } diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 701e704bbd..d43b126e07 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -92,8 +92,6 @@ void CSVRender::TerrainShapeMode::primaryOpenPressed (const WorldspaceHitResult& void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult& hit) { - mCellId = getWorldspaceWidget().getCellId (hit.worldPos); - if (hit.hit && hit.tag == 0) { if (mShapeEditTool == ShapeEditTool_Flatten) @@ -143,8 +141,6 @@ bool CSVRender::TerrainShapeMode::primaryEditStartDrag (const QPoint& pos) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); - mCellId = getWorldspaceWidget().getCellId (hit.worldPos); - mDragMode = InteractionType_PrimaryEdit; if (hit.hit && hit.tag == 0) @@ -245,27 +241,14 @@ void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor) { } - -void CSVRender::TerrainShapeMode::applyTerrainEditChanges() +void CSVRender::TerrainShapeMode::sortAndLimitAlteredCells() { - std::sort(mAlteredCells.begin(), mAlteredCells.end()); - mAlteredCells.erase(std::unique(mAlteredCells.begin(), mAlteredCells.end()), mAlteredCells.end()); - - CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - CSMWorld::IdTable& landTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); - CSMWorld::IdTable& ltexTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); - - int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); - int landMapLodColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandMapLodIndex); - int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); - - QUndoStack& undoStack = document.getUndoStack(); - bool passing = false; int passes = 0; + std::sort(mAlteredCells.begin(), mAlteredCells.end()); + mAlteredCells.erase(std::unique(mAlteredCells.begin(), mAlteredCells.end()), mAlteredCells.end()); + while (passing == false) // Multiple passes are needed when steepness problems arise for both x and y axis simultaneously { passing = true; @@ -291,6 +274,23 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } } } +} + +void CSVRender::TerrainShapeMode::applyTerrainEditChanges() +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + int landMapLodColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandMapLodIndex); + int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); + + QUndoStack& undoStack = document.getUndoStack(); + + sortAndLimitAlteredCells(); undoStack.beginMacro ("Edit shape and normal records"); @@ -337,11 +337,8 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() mapLodShapeNew[j * sqrtLandGlobalMapLodSize + i] = lodHeight; } } - if (allowLandShapeEditing(cellId) == true) - { - pushEditToCommand(landShapeNew, document, landTable, cellId); - pushLodToCommand(mapLodShapeNew, document, landTable, cellId); - } + pushEditToCommand(landShapeNew, document, landTable, cellId); + pushLodToCommand(mapLodShapeNew, document, landTable, cellId); } for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) @@ -368,9 +365,8 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() if (i < ESM::Land::LAND_SIZE - 1) v1.z() = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { - bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; - bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY())) == -1; - if (!noLand && !noCell) + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY()); + if (!noCell(cellId) && !noLand(cellId) && noLandLoaded(cellId)) v1.z() = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else v1.z() = 0; @@ -381,9 +377,8 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() if (j < ESM::Land::LAND_SIZE - 1) v2.z() = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { - bool noCell = document.getData().getCells().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; - bool noLand = document.getData().getLand().searchId (CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1)) == -1; - if (!noLand && !noCell) + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1); + if (!noCell(cellId) && !noLand(cellId) && noLandLoaded(cellId)) v2.z() = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else v2.z() = 0; @@ -400,7 +395,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = normal.z(); } } - if (allowLandShapeEditing(cellId) == true) pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); + pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); } undoStack.endMacro(); mAlteredCells.clear(); @@ -420,81 +415,77 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair if (mShapeEditTool == ShapeEditTool_Drag) paged->resetAllAlteredHeights(); } - if(allowLandShapeEditing(cellId)==true) + if (mBrushShape == CSVWidget::BrushShape_Point) { - if (mBrushShape == CSVWidget::BrushShape_Point) - { - int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); - int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); - if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); - } + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + } - if (mBrushShape == CSVWidget::BrushShape_Square) + if (mBrushShape == CSVWidget::BrushShape_Square) + { + for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) { - for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) { - for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); + cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); + } + } + } + + if (mBrushShape == CSVWidget::BrushShape_Circle) + { + for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + { + for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + { + cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); + cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int distanceX = abs(i - vertexCoords.first); + int distanceY = abs(j - vertexCoords.second); + float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2)); + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); + float distancePerRadius = 1.0f * distance / r; + float smoothedByDistance = 0.0f; + if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (distance <= r) { - cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); - cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; - int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); - int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); - if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Drag || mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) + alterHeight(cellCoords, x, y, smoothedByDistance); if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } } } - - if (mBrushShape == CSVWidget::BrushShape_Circle) + } + if (mBrushShape == CSVWidget::BrushShape_Custom) + { + if(!mCustomBrushShape.empty()) { - for(int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + for(auto const& value: mCustomBrushShape) { - for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) - { - cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); - cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; - int distanceX = abs(i - vertexCoords.first); - int distanceY = abs(j - vertexCoords.second); - float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2)); - int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); - int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); - float distancePerRadius = 1.0f * distance / r; - float smoothedByDistance = 0.0f; - if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); - if (distance <= r) - { - if (mShapeEditTool == ShapeEditTool_Drag || mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) - alterHeight(cellCoords, x, y, smoothedByDistance); - if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); - } - } + cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second)); + cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } } - if (mBrushShape == CSVWidget::BrushShape_Custom) - { - if(!mCustomBrushShape.empty()) - { - for(auto const& value: mCustomBrushShape) - { - cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second)); - cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; - int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first); - int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second); - if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); - if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); - } - } - } - } } @@ -528,7 +519,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c std::string cellDownLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() + 1); std::string cellDownRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() + 1); - if(allowLandShapeEditing(cellId)==true) + if ((allowLandShapeEditing(cellId, useTool)==true) && (useTool || (isLandLoaded(cellId)))) { if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) @@ -553,96 +544,88 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); // Change values of cornering cells - if (inCellX == 0 && inCellY == 0) + if ((inCellX == 0 && inCellY == 0) && (useTool || isLandLoaded(cellUpLeftId))) { - if(allowLandShapeEditing(cellUpLeftId) && allowLandShapeEditing(cellLeftId) && allowLandShapeEditing(cellUpId)) + if(allowLandShapeEditing(cellUpLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) && allowLandShapeEditing(cellUpId, useTool)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, -1); - if (useTool) mAlteredCells.emplace_back(cornerCellCoords); - else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + 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) + else if ((inCellX == 0 && inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownLeftId))) { - if(allowLandShapeEditing(cellDownLeftId) && allowLandShapeEditing(cellLeftId) && allowLandShapeEditing(cellDownId)) + if (allowLandShapeEditing(cellDownLeftId, useTool) && allowLandShapeEditing(cellLeftId, useTool) && allowLandShapeEditing(cellDownId, useTool)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(-1, 1); - if (useTool) mAlteredCells.emplace_back(cornerCellCoords); - else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + 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) + else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == 0) && (useTool || isLandLoaded(cellUpRightId))) { - if(allowLandShapeEditing(cellUpRightId) && allowLandShapeEditing(cellRightId) && allowLandShapeEditing(cellUpId)) + if (allowLandShapeEditing(cellUpRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) && allowLandShapeEditing(cellUpId, useTool)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, -1); - if (useTool) mAlteredCells.emplace_back(cornerCellCoords); - else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + 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) + else if ((inCellX == ESM::Land::LAND_SIZE - 1 && inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownRightId))) { - if(allowLandShapeEditing(cellDownRightId) && allowLandShapeEditing(cellRightId) && allowLandShapeEditing(cellDownId)) + if(allowLandShapeEditing(cellDownRightId, useTool) && allowLandShapeEditing(cellRightId, useTool) && allowLandShapeEditing(cellDownId, useTool)) { CSMWorld::CellCoordinates cornerCellCoords = cellCoords.move(1, 1); - if (useTool) mAlteredCells.emplace_back(cornerCellCoords); - else if (!(std::find(mAlteredCells.begin(), mAlteredCells.end(), cornerCellCoords) != mAlteredCells.end())) + 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) + if ((inCellX == 0) && (useTool || isLandLoaded(cellLeftId))) { - if(allowLandShapeEditing(cellLeftId)==true) + if(allowLandShapeEditing(cellLeftId, useTool)==true) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0); - if (useTool) mAlteredCells.emplace_back(edgeCellCoords); - else if (!(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); } } - if (inCellY == 0) + if ((inCellY == 0) && (useTool || isLandLoaded(cellUpId))) { - if(allowLandShapeEditing(cellUpId)==true) + if(allowLandShapeEditing(cellUpId, useTool)==true) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1); - if (useTool) mAlteredCells.emplace_back(edgeCellCoords); - else if (!(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); } } - if (inCellX == ESM::Land::LAND_SIZE - 1) + if ((inCellX == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellRightId))) { - if(allowLandShapeEditing(cellRightId)==true) + if(allowLandShapeEditing(cellRightId, useTool)==true) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0); - if (useTool) mAlteredCells.emplace_back(edgeCellCoords); - else if (!(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); } } - if (inCellY == ESM::Land::LAND_SIZE - 1) + if ((inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownId))) { - if(allowLandShapeEditing(cellDownId)==true) + if(allowLandShapeEditing(cellDownId, useTool)==true) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1); - if (useTool) mAlteredCells.emplace_back(edgeCellCoords); - else if (!(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); @@ -769,13 +752,11 @@ void CSVRender::TerrainShapeMode::flattenHeight(const CSMWorld::CellCoordinates& float thisAlteredHeight = 0.0f; std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); - bool noCell = document.getData().getCells().searchId (cellId) == -1; - bool noLand = document.getData().getLand().searchId (cellId) == -1; if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) { - if (!noCell && !noLand) + if (!noCell(cellId) && !noLand(cellId)) { const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); @@ -806,16 +787,6 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); - bool noCell = document.getData().getCells().searchId (cellId) == -1; - bool noLand = document.getData().getLand().searchId (cellId) == -1; - bool noLeftCell = document.getData().getCells().searchId (cellLeftId) == -1; - bool noLeftLand = document.getData().getLand().searchId (cellLeftId) == -1; - bool noUpCell = document.getData().getCells().searchId (cellUpId) == -1; - bool noUpLand = document.getData().getLand().searchId (cellUpId) == -1; - bool noRightCell = document.getData().getCells().searchId (cellRightId) == -1; - bool noRightLand = document.getData().getLand().searchId (cellRightId) == -1; - bool noDownCell = document.getData().getCells().searchId (cellDownId) == -1; - bool noDownLand = document.getData().getLand().searchId (cellDownId) == -1; *thisHeight = 0.0f; // real + altered height *thisAlteredHeight = 0.0f; // only altered height @@ -831,7 +802,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) { - if (!noCell && !noLand) + if (!noCell(cellId) && !noLand(cellId)) { const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); @@ -850,7 +821,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor //If at edge, get values from neighboring cell if (inCellX == 0) { - if(!noLeftCell && !noLeftLand) + if(isLandLoaded(cellLeftId)) { const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); @@ -864,8 +835,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor } if (inCellY == 0) { - cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); - if(!noUpCell && !noUpLand) + if(isLandLoaded(cellUpId)) { const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); @@ -879,8 +849,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor } if (inCellX == ESM::Land::LAND_SIZE - 1) { - cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); - if(!noRightCell && !noRightLand) + if(isLandLoaded(cellRightId)) { const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); @@ -894,8 +863,7 @@ void CSVRender::TerrainShapeMode::updateKeyHeightValues(const CSMWorld::CellCoor } if (inCellY == ESM::Land::LAND_SIZE - 1) { - cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); - if(!noDownCell && !noDownLand) + if(isLandLoaded(cellDownId)) { const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); @@ -981,11 +949,9 @@ 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 noCell = document.getData().getCells().searchId (cellId) == -1; - bool noLand = document.getData().getLand().searchId (cellId) == -1; bool steepnessIsWithinLimits = true; - if (!noCell && !noLand) + if (isLandLoaded(cellId)) { const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); @@ -1149,7 +1115,134 @@ void CSVRender::TerrainShapeMode::pushLodToCommand(const CSMWorld::LandMapLodCol undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLod)); } -bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellId) +bool CSVRender::TerrainShapeMode::noCell(const std::string& cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + const CSMWorld::IdCollection& cellCollection = document.getData().getCells(); + return cellCollection.searchId (cellId) == -1; +} + +bool CSVRender::TerrainShapeMode::noLand(const std::string& cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + const CSMWorld::IdCollection& landCollection = document.getData().getLand(); + return landCollection.searchId (cellId) == -1; +} + +bool CSVRender::TerrainShapeMode::noLandLoaded(const std::string& cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + const CSMWorld::IdCollection& landCollection = document.getData().getLand(); + return !landCollection.getRecord(cellId).get().isDataLoaded(ESM::Land::DATA_VNML); +} + +bool CSVRender::TerrainShapeMode::isLandLoaded(const std::string& cellId) +{ + if (!noCell(cellId) && !noLand(cellId) && !noLandLoaded(cellId)) return true; + return false; +} + +void CSVRender::TerrainShapeMode::createNewLandData(const CSMWorld::CellCoordinates& cellCoords) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); + + float defaultHeight = 0.f; + int averageDivider = 0; + CSMWorld::CellCoordinates cellLeftCoords = cellCoords.move(-1, 0); + CSMWorld::CellCoordinates cellRightCoords = cellCoords.move(1, 0); + CSMWorld::CellCoordinates cellUpCoords = cellCoords.move(0, -1); + CSMWorld::CellCoordinates cellDownCoords = cellCoords.move(0, 1); + + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellLeftCoords.getX(), cellLeftCoords.getY()); + std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellRightCoords.getX(), cellRightCoords.getY()); + std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellUpCoords.getX(), cellUpCoords.getY()); + std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellDownCoords.getX(), cellDownCoords.getY()); + + float leftCellSampleHeight = 0.0f; + float rightCellSampleHeight = 0.0f; + float upCellSampleHeight = 0.0f; + float downCellSampleHeight = 0.0f; + + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + const CSMWorld::LandNormalsColumn::DataType landNormalsPointer = landTable.data(landTable.getModelIndex(cellId, landnormalsColumn)).value(); + CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); + CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer); + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + if (isLandLoaded(cellLeftId)) + { + const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = + landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); + + ++averageDivider; + leftCellSampleHeight = landLeftShapePointer[(ESM::Land::LAND_SIZE / 2) * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1]; + if(paged->getCellAlteredHeight(cellLeftCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE / 2)) + leftCellSampleHeight += *paged->getCellAlteredHeight(cellLeftCoords, ESM::Land::LAND_SIZE - 1, ESM::Land::LAND_SIZE / 2); + } + if (isLandLoaded(cellRightId)) + { + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = + landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); + + ++averageDivider; + rightCellSampleHeight = landRightShapePointer[(ESM::Land::LAND_SIZE / 2) * ESM::Land::LAND_SIZE]; + if(paged->getCellAlteredHeight(cellRightCoords, 0, ESM::Land::LAND_SIZE / 2)) + rightCellSampleHeight += *paged->getCellAlteredHeight(cellRightCoords, 0, ESM::Land::LAND_SIZE / 2); + } + if (isLandLoaded(cellUpId)) + { + const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = + landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); + + ++averageDivider; + upCellSampleHeight = landUpShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + (ESM::Land::LAND_SIZE / 2)]; + if(paged->getCellAlteredHeight(cellUpCoords, ESM::Land::LAND_SIZE / 2, ESM::Land::LAND_SIZE - 1)) + upCellSampleHeight += *paged->getCellAlteredHeight(cellUpCoords, ESM::Land::LAND_SIZE / 2, ESM::Land::LAND_SIZE - 1); + } + if (isLandLoaded(cellDownId)) + { + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = + landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); + + ++averageDivider; + downCellSampleHeight = landDownShapePointer[ESM::Land::LAND_SIZE / 2]; + if(paged->getCellAlteredHeight(cellLeftCoords, ESM::Land::LAND_SIZE / 2, 0)) + downCellSampleHeight += *paged->getCellAlteredHeight(cellDownCoords, ESM::Land::LAND_SIZE / 2, 0); + } + } + if (averageDivider > 0) defaultHeight = (leftCellSampleHeight + rightCellSampleHeight + upCellSampleHeight + downCellSampleHeight) / averageDivider; + + for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) + { + for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) + { + landShapeNew[j * ESM::Land::LAND_SIZE + i] = defaultHeight; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 0] = 0; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 1] = 0; + landNormalsNew[(j * ESM::Land::LAND_SIZE + i) * 3 + 2] = 127; + } + } + QVariant changedShape; + changedShape.setValue(landShapeNew); + QVariant changedNormals; + changedNormals.setValue(landNormalsNew); + QModelIndex indexShape(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandHeightsIndex))); + QModelIndex indexNormal(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandNormalsIndex))); + document.getUndoStack().push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); + document.getUndoStack().push (new CSMWorld::ModifyCommand(landTable, indexShape, changedShape)); + document.getUndoStack().push (new CSMWorld::ModifyCommand(landTable, indexNormal, changedNormals)); +} + +bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellId, bool useTool) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMWorld::IdTable& landTable = dynamic_cast ( @@ -1157,10 +1250,7 @@ bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellI CSMWorld::IdTree& cellTable = dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); - bool noCell = document.getData().getCells().searchId (cellId)==-1; - bool noLand = document.getData().getLand().searchId (cellId)==-1; - - if (noCell) + if (noCell(cellId)) { std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); @@ -1168,7 +1258,7 @@ bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellI if (mode=="Discard") return false; - if (mode=="Create cell and land, then edit") + if (mode=="Create cell and land, then edit" && useTool) { std::unique_ptr createCommand ( new CSMWorld::CreateCommand (cellTable, cellId)); @@ -1199,7 +1289,7 @@ bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellI if (mode=="Discard") return false; - if (mode=="Show cell and edit") + if (mode=="Show cell and edit" && useTool) { selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); paged->setCellSelection (selection); @@ -1207,7 +1297,7 @@ bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellI } } - if (noLand) + if (noLand(cellId)) { std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); @@ -1215,15 +1305,80 @@ bool CSVRender::TerrainShapeMode::allowLandShapeEditing(const std::string& cellI if (mode=="Discard") return false; - if (mode=="Create cell and land, then edit") + if (mode=="Create cell and land, then edit" && useTool) { document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId)); + createNewLandData(CSMWorld::CellCoordinates::fromId(cellId).first); + fixEdges(CSMWorld::CellCoordinates::fromId(cellId).first); + sortAndLimitAlteredCells(); + } + } + else if (noLandLoaded(cellId)) + { + std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); + + if (mode=="Discard") + return false; + + if (mode=="Create cell and land, then edit" && useTool) + { + createNewLandData(CSMWorld::CellCoordinates::fromId(cellId).first); + fixEdges(CSMWorld::CellCoordinates::fromId(cellId).first); + sortAndLimitAlteredCells(); } } + if (useTool && (noCell(cellId) || noLand(cellId) || noLandLoaded(cellId))) + { + Log(Debug::Warning) << "Land creation failed at cell id: " << cellId; + return false; + } return true; } +void CSVRender::TerrainShapeMode::fixEdges(CSMWorld::CellCoordinates cellCoords) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); + std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); + std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); + std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); + std::string cellDownId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() + 1); + + const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer = landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landRightShapePointer = landTable.data(landTable.getModelIndex(cellRightId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landUpShapePointer = landTable.data(landTable.getModelIndex(cellUpId, landshapeColumn)).value(); + const CSMWorld::LandHeightsColumn::DataType landDownShapePointer = landTable.data(landTable.getModelIndex(cellDownId, landshapeColumn)).value(); + + CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); + for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) + { + if (isLandLoaded(cellLeftId) && + landShapePointer[i * ESM::Land::LAND_SIZE] != landLeftShapePointer[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1]) + landShapeNew[i * ESM::Land::LAND_SIZE] = landLeftShapePointer[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1]; + if (isLandLoaded(cellRightId) && + landShapePointer[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1] != landRightShapePointer[i * ESM::Land::LAND_SIZE]) + landShapeNew[i * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE - 1] = landRightShapePointer[i * ESM::Land::LAND_SIZE]; + if (isLandLoaded(cellUpId) && + landShapePointer[i] != landUpShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i]) + landShapeNew[i] = landUpShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i]; + if (isLandLoaded(cellDownId) && + landShapePointer[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i] != landDownShapePointer[i]) + landShapeNew[(ESM::Land::LAND_SIZE - 1) * ESM::Land::LAND_SIZE + i] = landDownShapePointer[i]; + } + + QVariant changedLand; + changedLand.setValue(landShapeNew); + + QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandHeightsIndex))); + QUndoStack& undoStack = document.getUndoStack(); + undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); +} + void CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event) { } diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 8ee9625ffc..8c07aa0ce2 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -91,7 +91,11 @@ namespace CSVRender void dragMoveEvent (QDragMoveEvent *event) final; private: - /// Move pending alteredHeights changes to omwgame/omeaddon -data + + /// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse + void sortAndLimitAlteredCells(); + + /// Move pending alteredHeights changes to omwgame/omwaddon -data void applyTerrainEditChanges(); /// Handle brush mechanics for shape editing @@ -136,10 +140,23 @@ namespace CSVRender void pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document, CSMWorld::IdTable& landTable, const std::string& cellId); - /// Create new cell and land if needed - bool allowLandShapeEditing(const std::string& textureFileName); + bool noCell(const std::string& cellId); + + bool noLand(const std::string& cellId); + + bool noLandLoaded(const std::string& cellId); + + bool isLandLoaded(const std::string& cellId); + + /// Create new blank height record and new normals, if there are valid adjancent cell, take sample points and set the average height based on that + void createNewLandData(const CSMWorld::CellCoordinates& cellCoords); + + /// Create new cell and land if needed, only user tools may ask for opening new cells (useTool == false is for automated land changes) + bool allowLandShapeEditing(const std::string& textureFileName, bool useTool = true); + + /// Bind the edging vertice to the values of the adjancent cells + void fixEdges(CSMWorld::CellCoordinates cellCoords); - std::string mCellId; std::string mBrushTexture; int mBrushSize = 1; CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; From deb122ffdb121c1e1cf577eecffda2a358d0e262 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 12 Oct 2019 16:20:57 +0300 Subject: [PATCH 35/46] Add optional post-processing (smooth/rough), add/fix tooltips. --- apps/opencs/model/prefs/state.cpp | 16 +++++++++-- apps/opencs/view/render/terrainshapemode.cpp | 30 ++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index b672855137..1bf5752f07 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -242,14 +242,24 @@ void CSMPrefs::State::declare() addValues (insertOutsideCell); declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). addValues (insertOutsideVisibleCell); - declareEnum ("outside-landedit", "Handling land edit outside of cells", createAndLandEdit). + declareEnum ("outside-landedit", "Handling terrain edit outside of cells", createAndLandEdit). + setTooltip("Behavior of terrain editing, if land editing brush reaches an area without cell record."). addValues (landeditOutsideCell); - declareEnum ("outside-visible-landedit", "Handling land edit outside of visible cells", showAndLandEdit). + declareEnum ("outside-visible-landedit", "Handling terrain edit outside of visible cells", showAndLandEdit). + setTooltip("Behavior of terrain editing, if land editing brush reaches an area that is not currently visible."). addValues (landeditOutsideVisibleCell); declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). setMin (1); - declareInt ("shapebrush-maximumsize", "Maximum shape brush size", 100). + declareInt ("shapebrush-maximumsize", "Maximum height edit brush size", 100). + setTooltip("Setting for the slider range of brush size in terrain height editing."). setMin (1); + declareBool ("landedit-post-smoothpainting", "Smooth land after painting height", false). + setTooltip("Raise and lower tools will leave bumpy finish without this option"); + declareDouble ("landedit-post-smoothstrength", "Smoothing strength (post-edit)", 0.25). + setTooltip("If smoothing land after painting height is used, this is the percentage of smooth applied afterwards. " + "Negative values may be used to roughen instead of smooth."). + setMin (-1). + setMax (1); declareBool ("open-list-view", "Open displays list view", false). setTooltip ("When opening a reference from the scene view, it will open the" " instance list view instead of the individual instance record view."); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index d43b126e07..7f3ccf04f4 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -420,7 +420,12 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) + { + alterHeight(cellCoords, x, y, mShapeEditToolStrength); + float smoothMultiplier = static_cast(CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothstrength"].toDouble()); + if (CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothpainting"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier); + } if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } @@ -436,7 +441,12 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) + { + alterHeight(cellCoords, x, y, mShapeEditToolStrength); + float smoothMultiplier = static_cast(CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothstrength"].toDouble()); + if (CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothpainting"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier); + } if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } @@ -462,8 +472,13 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); if (distance <= r) { - if (mShapeEditTool == ShapeEditTool_Drag || mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) - alterHeight(cellCoords, x, y, smoothedByDistance); + if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, smoothedByDistance); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) + { + alterHeight(cellCoords, x, y, smoothedByDistance); + float smoothMultiplier = static_cast(CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothstrength"].toDouble()); + if (CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothpainting"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier); + } if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } @@ -481,7 +496,12 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second); if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) alterHeight(cellCoords, x, y, mShapeEditToolStrength); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) + { + alterHeight(cellCoords, x, y, mShapeEditToolStrength); + float smoothMultiplier = static_cast(CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothstrength"].toDouble()); + if (CSMPrefs::get()["3D Scene Editing"]["landedit-post-smoothpainting"].isTrue()) smoothHeight(cellCoords, x, y, mShapeEditToolStrength * smoothMultiplier); + } if (mShapeEditTool == ShapeEditTool_Smooth) smoothHeight(cellCoords, x, y, mShapeEditToolStrength); if (mShapeEditTool == ShapeEditTool_Flatten) flattenHeight(cellCoords, x, y, mShapeEditToolStrength, mTargetHeight); } From c556885d715e4bfb177ddfd76b8a40b40f178d4f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sun, 13 Oct 2019 23:09:13 +0300 Subject: [PATCH 36/46] remove unused Qt signal --- apps/opencs/view/render/terrainshapemode.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 8c07aa0ce2..0f987c88b6 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -175,9 +175,6 @@ namespace CSVRender PagedWorldspaceWidget& getPagedWorldspaceWidget(); - signals: - void passBrushTexture(std::string brushTexture); - public slots: void setBrushSize(int brushSize); void setBrushShape(CSVWidget::BrushShape brushShape); From 12349f4e48efc7baade19d19b78338c71747c7dc Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sun, 13 Oct 2019 23:20:27 +0300 Subject: [PATCH 37/46] cleanup --- apps/opencs/view/render/terrainshapemode.cpp | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 7f3ccf04f4..e723e18dfa 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -192,8 +192,11 @@ void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); mTotalDiffY += diffY; - if (mIsEditing == true && mShapeEditTool == ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true); - if (mIsEditing == true && mShapeEditTool != ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + if (mIsEditing) + { + if (mShapeEditTool == ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true); + if (mShapeEditTool != ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + } } if (mDragMode == InteractionType_PrimarySelect) @@ -213,7 +216,7 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos) { if (mDragMode == InteractionType_PrimaryEdit) { - if (mIsEditing == true) + if (mIsEditing) { mTotalDiffY = 0; mIsEditing = false; @@ -539,7 +542,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c std::string cellDownLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() + 1); std::string cellDownRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() + 1); - if ((allowLandShapeEditing(cellId, useTool)==true) && (useTool || (isLandLoaded(cellId)))) + if (allowLandShapeEditing(cellId, useTool) && (useTool || (isLandLoaded(cellId)))) { if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) @@ -608,7 +611,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c // Change values of edging cells if ((inCellX == 0) && (useTool || isLandLoaded(cellLeftId))) { - if(allowLandShapeEditing(cellLeftId, useTool)==true) + if(allowLandShapeEditing(cellLeftId, useTool)) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(-1, 0); if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) @@ -619,7 +622,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c } if ((inCellY == 0) && (useTool || isLandLoaded(cellUpId))) { - if(allowLandShapeEditing(cellUpId, useTool)==true) + if(allowLandShapeEditing(cellUpId, useTool)) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, -1); if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) @@ -631,7 +634,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c if ((inCellX == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellRightId))) { - if(allowLandShapeEditing(cellRightId, useTool)==true) + if(allowLandShapeEditing(cellRightId, useTool)) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(1, 0); if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) @@ -642,7 +645,7 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c } if ((inCellY == ESM::Land::LAND_SIZE - 1) && (useTool || isLandLoaded(cellDownId))) { - if(allowLandShapeEditing(cellDownId, useTool)==true) + if(allowLandShapeEditing(cellDownId, useTool)) { CSMWorld::CellCoordinates edgeCellCoords = cellCoords.move(0, 1); if (useTool && std::find(mAlteredCells.begin(), mAlteredCells.end(), edgeCellCoords) == mAlteredCells.end()) @@ -687,7 +690,7 @@ void CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& float downAlteredHeight = 0.0f; float upHeight = 0.0f; - if(allowLandShapeEditing(cellId)==true) + if(allowLandShapeEditing(cellId)) { //Get key values for calculating average, handle cell edges, check for null pointers if (inCellX == 0) @@ -1016,7 +1019,7 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi } } - if (reverseMode == true) + if (reverseMode) { for(int inCellY = ESM::Land::LAND_SIZE - 1; inCellY >= 0; --inCellY) { From 0f6ddfe8e69aba38b9798e104d095215b75075a1 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 16 Oct 2019 03:02:29 +0300 Subject: [PATCH 38/46] change include, cleanup --- apps/opencs/view/render/terrainshapemode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index e723e18dfa..54fddd76b3 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -39,7 +39,7 @@ #include "editmode.hpp" #include "pagedworldspacewidget.hpp" #include "mask.hpp" -#include "object.hpp" // Something small needed regarding pointers from here () +#include "tagbase.hpp" #include "terrainselection.hpp" #include "worldspacewidget.hpp" @@ -252,7 +252,7 @@ void CSVRender::TerrainShapeMode::sortAndLimitAlteredCells() std::sort(mAlteredCells.begin(), mAlteredCells.end()); mAlteredCells.erase(std::unique(mAlteredCells.begin(), mAlteredCells.end()), mAlteredCells.end()); - while (passing == false) // Multiple passes are needed when steepness problems arise for both x and y axis simultaneously + while (!passing) // Multiple passes are needed when steepness problems arise for both x and y axis simultaneously { passing = true; for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) @@ -990,7 +990,7 @@ bool CSVRender::TerrainShapeMode::limitAlteredHeights(const CSMWorld::CellCoordi float downHeight = 0.0f; float downAlteredHeight = 0.0f; - if (reverseMode == false) + if (!reverseMode) { for(int inCellY = 0; inCellY < ESM::Land::LAND_SIZE; ++inCellY) { From 8d3e7f281554d90f839b1f96f53f42a72fb72507 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Mon, 21 Oct 2019 01:49:15 +0300 Subject: [PATCH 39/46] fix normals at cell edges, better coding standards --- apps/opencs/view/render/terrainshapemode.cpp | 247 +++++++++---------- 1 file changed, 117 insertions(+), 130 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 54fddd76b3..174a7beb24 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -98,7 +98,6 @@ void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult& setFlattenToolTargetHeight(hit); if (mDragMode == InteractionType_PrimaryEdit && mShapeEditTool != ShapeEditTool_Drag) { - std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); applyTerrainEditChanges(); } @@ -190,12 +189,11 @@ void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, if (mDragMode == InteractionType_PrimaryEdit) { WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); - std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); mTotalDiffY += diffY; if (mIsEditing) { if (mShapeEditTool == ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(mEditingPos), true); - if (mShapeEditTool != ShapeEditTool_Drag) editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + else editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); } } @@ -305,14 +303,14 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() const CSMWorld::LandMapLodColumn::DataType landMapLodPointer = landTable.data(landTable.getModelIndex(cellId, landMapLodColumn)).value(); CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); CSMWorld::LandMapLodColumn::DataType mapLodShapeNew(landMapLodPointer); + CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget()); // Generate land height record for(int i = 0; i < ESM::Land::LAND_SIZE; ++i) { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) { - if (CSVRender::PagedWorldspaceWidget *paged = - dynamic_cast (&getWorldspaceWidget())) + if (paged) { if (paged->getCellAlteredHeight(cellCoordinates, i, j)) landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); @@ -358,38 +356,27 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) { - osg::Vec3f v1; - osg::Vec3f v2; - osg::Vec3f normal; - float hyp; + osg::Vec3f v1(128, 0, 0); + osg::Vec3f v2(0, 128, 0); - v1.x() = 128; - v1.y() = 0; if (i < ESM::Land::LAND_SIZE - 1) v1.z() = landShapePointer[j * ESM::Land::LAND_SIZE + i + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX() + 1, cellCoordinates.getY()); - if (!noCell(cellId) && !noLand(cellId) && noLandLoaded(cellId)) + if (isLandLoaded(cellId)) v1.z() = landRightShapePointer[j * ESM::Land::LAND_SIZE + 1] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; - else - v1.z() = 0; } - v2.x() = 0; - v2.y() = 128; if (j < ESM::Land::LAND_SIZE - 1) v2.z() = landShapePointer[(j + 1) * ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; else { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY() + 1); - if (!noCell(cellId) && !noLand(cellId) && noLandLoaded(cellId)) + if (isLandLoaded(cellId)) v2.z() = landDownShapePointer[ESM::Land::LAND_SIZE + i] - landShapePointer[j * ESM::Land::LAND_SIZE + i]; - else - v2.z() = 0; } - normal = v1 ^ v2; - - hyp = normal.length() / 127.0f; + osg::Vec3f normal = v1 ^ v2; + const float hyp = normal.length() / 127.0f; normal /= hyp; @@ -533,6 +520,13 @@ void CSVRender::TerrainShapeMode::setFlattenToolTargetHeight(const WorldspaceHit void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool) { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY()); + + if (!(allowLandShapeEditing(cellId, useTool) && (useTool || (isLandLoaded(cellId))))) + return; + CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget()); + if (!paged) + return; + std::string cellLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY()); std::string cellRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY()); std::string cellUpId = CSMWorld::CellCoordinates::generateId(cellCoords.getX(), cellCoords.getY() - 1); @@ -542,121 +536,114 @@ void CSVRender::TerrainShapeMode::alterHeight(const CSMWorld::CellCoordinates& c std::string cellDownLeftId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() - 1, cellCoords.getY() + 1); std::string cellDownRightId = CSMWorld::CellCoordinates::generateId(cellCoords.getX() + 1, cellCoords.getY() + 1); - if (allowLandShapeEditing(cellId, useTool) && (useTool || (isLandLoaded(cellId)))) + if (useTool) { - if (CSVRender::PagedWorldspaceWidget *paged = - dynamic_cast (&getWorldspaceWidget())) + mAlteredCells.emplace_back(cellCoords); + if (mShapeEditTool == ShapeEditTool_Drag) { - if (useTool) - { - mAlteredCells.emplace_back(cellCoords); - if (mShapeEditTool == ShapeEditTool_Drag) - { - // Get distance from modified land, alter land change based on zoom - osg::Vec3d eye, center, up; - paged->getCamera()->getViewMatrixAsLookAt(eye, center, up); - osg::Vec3d distance = eye - mEditingPos; - alteredHeight = alteredHeight * (distance.length() / 500); - } - if (mShapeEditTool == ShapeEditTool_PaintToRaise) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; - if (mShapeEditTool == ShapeEditTool_PaintToLower) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) - alteredHeight; - if (mShapeEditTool == ShapeEditTool_Smooth) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; - } + // Get distance from modified land, alter land change based on zoom + osg::Vec3d eye, center, up; + paged->getCamera()->getViewMatrixAsLookAt(eye, center, up); + osg::Vec3d distance = eye - mEditingPos; + alteredHeight = alteredHeight * (distance.length() / 500); + } + if (mShapeEditTool == ShapeEditTool_PaintToRaise) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; + if (mShapeEditTool == ShapeEditTool_PaintToLower) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) - alteredHeight; + if (mShapeEditTool == ShapeEditTool_Smooth) alteredHeight = *paged->getCellAlteredHeight(cellCoords, inCellX, inCellY) + alteredHeight; + } - if (inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1) - paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, alteredHeight); + if (inCellX != 0 && inCellY != 0 && inCellX != ESM::Land::LAND_SIZE - 1 && inCellY != ESM::Land::LAND_SIZE - 1) + paged->setCellAlteredHeight(cellCoords, inCellX, inCellY, 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->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->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); - } - } + // 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->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); + } + } + } void CSVRender::TerrainShapeMode::smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength) From a13edbdb428b26d9cb79e698a66e71ca262307fd Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 22 Oct 2019 20:58:23 +0300 Subject: [PATCH 40/46] Bump shape calculation to function --- apps/opencs/view/render/terrainshapemode.cpp | 15 ++++++++++----- apps/opencs/view/render/terrainshapemode.hpp | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 174a7beb24..8bdd96ad8f 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -391,6 +391,12 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() mAlteredCells.clear(); } +float CSVRender::TerrainShapeMode::calculateBumpShape(const float& distance, int radius, const float& height) +{ + float distancePerRadius = distance / radius; + return height - height * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); +} + void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair& vertexCoords, bool dragOperation) { int r = mBrushSize / 2; @@ -451,15 +457,14 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair { cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); + int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); int distanceX = abs(i - vertexCoords.first); int distanceY = abs(j - vertexCoords.second); float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2)); - int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); - int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); - float distancePerRadius = 1.0f * distance / r; float smoothedByDistance = 0.0f; - if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = mTotalDiffY - mTotalDiffY * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); - if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = (r + mShapeEditToolStrength) - (r + mShapeEditToolStrength) * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); + if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = calculateBumpShape(distance, r, mTotalDiffY); + if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = calculateBumpShape(distance, r, r + mShapeEditToolStrength); if (distance <= r) { if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, smoothedByDistance); diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 0f987c88b6..6e6589c86b 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -101,6 +101,9 @@ namespace CSVRender /// Handle brush mechanics for shape editing void editTerrainShapeGrid (const std::pair& vertexCoords, bool dragOperation); + /// Calculate height, when aiming for bump-shaped terrain change + float calculateBumpShape(const float& distance, int radius, const float& height); + /// set the target height for flatten tool void setFlattenToolTargetHeight(const WorldspaceHitResult& hit); From fdcc7fa1d7435ca52228ade9049e38b15969c87f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 22 Oct 2019 20:58:34 +0300 Subject: [PATCH 41/46] 0 to nullptr --- apps/opencs/view/render/terrainstorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 65c89ae5c0..847a574045 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -57,7 +57,7 @@ namespace CSVRender osg::ref_ptr land = getLand (cellX, cellY); if (land) { - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; if (data) height = getVertexHeight(data, inCellX, inCellY); } else return height; From 6f9f59dd580f327e024677dff3e396d49b0b6c17 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 22 Oct 2019 21:03:37 +0300 Subject: [PATCH 42/46] declare cellId and cellCoords in smallest possible scope --- apps/opencs/view/render/terrainshapemode.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 8bdd96ad8f..559b8a47c2 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -402,9 +402,6 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair int r = mBrushSize / 2; if (r == 0) r = 1; // Prevent division by zero later, which might happen when mBrushSize == 1 - std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); - CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; - if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget())) { @@ -413,6 +410,8 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair if (mBrushShape == CSVWidget::BrushShape_Point) { + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); + CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); @@ -432,8 +431,8 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair { for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) { - cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); - cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); + CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); @@ -455,8 +454,8 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair { for(int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) { - cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); - cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(i, j)); + CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(i); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(j); int distanceX = abs(i - vertexCoords.first); @@ -486,8 +485,8 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair { for(auto const& value: mCustomBrushShape) { - cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second)); - cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(std::make_pair(vertexCoords.first + value.first, vertexCoords.second + value.second)); + CSMWorld::CellCoordinates cellCoords = CSMWorld::CellCoordinates::fromId(cellId).first; int x = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first + value.first); int y = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second + value.second); if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, mTotalDiffY); From ac5b356e8da64e4a0e60b2e327fd3425d234c57b Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 23 Oct 2019 00:01:23 +0300 Subject: [PATCH 43/46] Always initialize landShapeNew --- apps/opencs/view/render/terrainshapemode.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 559b8a47c2..bc0a2650f8 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -310,13 +310,10 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() { for(int j = 0; j < ESM::Land::LAND_SIZE; ++j) { - if (paged) - { - if (paged->getCellAlteredHeight(cellCoordinates, i, j)) - landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); - else - landShapeNew[j * ESM::Land::LAND_SIZE + i] = 0; - } + if (paged && paged->getCellAlteredHeight(cellCoordinates, i, j)) + landShapeNew[j * ESM::Land::LAND_SIZE + i] = landShapePointer[j * ESM::Land::LAND_SIZE + i] + *paged->getCellAlteredHeight(cellCoordinates, i, j); + else + landShapeNew[j * ESM::Land::LAND_SIZE + i] = 0; } } From 9e5db10288ff1240b38d3be6cce06d3a92b94433 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 23 Oct 2019 00:02:24 +0300 Subject: [PATCH 44/46] const ref float -> float --- apps/opencs/view/render/terrainshapemode.cpp | 2 +- apps/opencs/view/render/terrainshapemode.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index bc0a2650f8..5f5a60da28 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -388,7 +388,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() mAlteredCells.clear(); } -float CSVRender::TerrainShapeMode::calculateBumpShape(const float& distance, int radius, const float& height) +float CSVRender::TerrainShapeMode::calculateBumpShape(float distance, int radius, float height) { float distancePerRadius = distance / radius; return height - height * (3 * distancePerRadius * distancePerRadius - 2 * distancePerRadius * distancePerRadius * distancePerRadius); diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 6e6589c86b..8e2a616c98 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -102,7 +102,7 @@ namespace CSVRender void editTerrainShapeGrid (const std::pair& vertexCoords, bool dragOperation); /// Calculate height, when aiming for bump-shaped terrain change - float calculateBumpShape(const float& distance, int radius, const float& height); + float calculateBumpShape(float distance, int radius, float height); /// set the target height for flatten tool void setFlattenToolTargetHeight(const WorldspaceHitResult& hit); From fdc73b87ba1b01c9599e73fbcfedf49c9ddd2901 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 23 Oct 2019 00:07:41 +0300 Subject: [PATCH 45/46] Remove includes that aren't needed any more --- apps/opencs/view/render/terrainstorage.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 847a574045..d9cc3015e1 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -1,15 +1,9 @@ #include "terrainstorage.hpp" -#include -#include - #include "../../model/world/land.hpp" #include "../../model/world/landtexture.hpp" #include -#include -#include -#include namespace CSVRender { From c957f0544c6aaa6fb9f55f44b4d882abf3043478 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 23 Oct 2019 00:34:54 +0300 Subject: [PATCH 46/46] Remove unneeded brackets --- apps/opencs/model/world/columnimp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index e610ef2235..948174b304 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -92,9 +92,7 @@ namespace CSMWorld if (land.mDataTypes & Land::DATA_WNAM) { for (int i = 0; i < Size; ++i) - { values[i] = land.mWnam[i]; - } } QVariant variant;