From 7a489f3eee02ae2ce37c9fcf87c568b48d585843 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Mon, 15 Apr 2024 03:09:53 +0100 Subject: [PATCH 1/8] Land color painting Signed-off-by: Sam Hellawell --- apps/opencs/CMakeLists.txt | 4 +- .../view/render/pagedworldspacewidget.cpp | 4 +- .../view/render/terrainvertexpaintmode.cpp | 851 ++++++++++++++++++ .../view/render/terrainvertexpaintmode.hpp | 180 ++++ .../view/widget/scenetoolvertexpaintbrush.cpp | 234 +++++ .../view/widget/scenetoolvertexpaintbrush.hpp | 164 ++++ 6 files changed, 1433 insertions(+), 4 deletions(-) create mode 100644 apps/opencs/view/render/terrainvertexpaintmode.cpp create mode 100644 apps/opencs/view/render/terrainvertexpaintmode.hpp create mode 100644 apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp create mode 100644 apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index c2f249171a..45f2d40848 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -80,7 +80,7 @@ opencs_units (view/world ) opencs_units (view/widget - scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton + scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton scenetoolvertexpaintbrush scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit ) @@ -88,7 +88,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands + cellwater terraintexturemode terrainvertexpaintmode actor terrainselection terrainshapemode brushdraw commands ) opencs_units (view/render diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 214618a627..d0167b1e63 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -41,6 +41,7 @@ #include "mask.hpp" #include "terrainshapemode.hpp" #include "terraintexturemode.hpp" +#include "terrainvertexpaintmode.hpp" class QWidget; @@ -170,8 +171,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons(CSVWidget::Sce /// \todo replace EditMode with suitable subclasses tool->addButton(new TerrainShapeMode(this, mRootNode, tool), "terrain-shape"); tool->addButton(new TerrainTextureMode(this, mRootNode, tool), "terrain-texture"); - tool->addButton( - new EditMode(this, QIcon(":placeholder"), Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex"); + tool->addButton(new TerrainVertexPaintMode(this, mRootNode, tool), "terrain-vertex"); tool->addButton(new EditMode(this, QIcon(":placeholder"), Mask_Reference, "Terrain movement"), "terrain-move"); } diff --git a/apps/opencs/view/render/terrainvertexpaintmode.cpp b/apps/opencs/view/render/terrainvertexpaintmode.cpp new file mode 100644 index 0000000000..26b227d2c2 --- /dev/null +++ b/apps/opencs/view/render/terrainvertexpaintmode.cpp @@ -0,0 +1,851 @@ +#include "terrainvertexpaintmode.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../widget/scenetoolbar.hpp" +#include "../widget/scenetoolvertexpaintbrush.hpp" + +#include "../../model/prefs/state.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtree.hpp" + +#include "brushdraw.hpp" +#include "commands.hpp" +#include "editmode.hpp" +#include "mask.hpp" +#include "pagedworldspacewidget.hpp" +#include "terrainselection.hpp" +#include "worldspacewidget.hpp" + +class QPoint; +class QWidget; + +namespace CSMWorld +{ + struct Cell; +} + +namespace osg +{ + class Group; +} + +CSVRender::TerrainVertexPaintMode::TerrainVertexPaintMode( + WorldspaceWidget* worldspaceWidget, osg::Group* parentNode, QWidget* parent) + : EditMode(worldspaceWidget, QIcon{ ":placeholder" }, Mask_Terrain, "Terrain vertex paint editing", parent) + , mParentNode(parentNode) + , mVertexPaintEditToolColor(Qt::white) +{ +} + +void CSVRender::TerrainVertexPaintMode::activate(CSVWidget::SceneToolbar* toolbar) +{ + if (!mTerrainShapeSelection) + { + mTerrainShapeSelection + = std::make_shared(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Shape); + } + + if (!mVertexPaintBrushScenetool) + { + mVertexPaintBrushScenetool = new CSVWidget::SceneToolVertexPaintBrush( + toolbar, "scenetoolvertexpaintbrush", getWorldspaceWidget().getDocument()); + connect(mVertexPaintBrushScenetool->mVertexPaintBrushWindow, &CSVWidget::VertexPaintBrushWindow::passBrushSize, + this, &TerrainVertexPaintMode::setBrushSize); + connect(mVertexPaintBrushScenetool->mVertexPaintBrushWindow, &CSVWidget::VertexPaintBrushWindow::passBrushShape, + this, &TerrainVertexPaintMode::setBrushShape); + connect(mVertexPaintBrushScenetool->mVertexPaintBrushWindow->mSizeSliders->mBrushSizeSlider, + &QSlider::valueChanged, this, &TerrainVertexPaintMode::setBrushSize); + connect(mVertexPaintBrushScenetool->mVertexPaintBrushWindow->mToolSelector, + qOverload(&QComboBox::currentIndexChanged), this, &TerrainVertexPaintMode::setVertexPaintEditTool); + connect(mVertexPaintBrushScenetool->mVertexPaintBrushWindow->mColorButtonWidget, + &CSVWidget::ColorButtonWidget::colorChanged, this, &TerrainVertexPaintMode::setVertexPaintColor); + } + + if (!mBrushDraw) + mBrushDraw = std::make_unique(mParentNode); + + EditMode::activate(toolbar); + toolbar->addTool(mVertexPaintBrushScenetool); +} + +void CSVRender::TerrainVertexPaintMode::deactivate(CSVWidget::SceneToolbar* toolbar) +{ + if (mVertexPaintBrushScenetool) + { + toolbar->removeTool(mVertexPaintBrushScenetool); + } + + if (mTerrainShapeSelection) + { + mTerrainShapeSelection.reset(); + } + + if (mBrushDraw) + mBrushDraw.reset(); + + EditMode::deactivate(toolbar); +} + +void CSVRender::TerrainVertexPaintMode::primaryOpenPressed(const WorldspaceHitResult& hit) // Apply changes here +{ +} + +void CSVRender::TerrainVertexPaintMode::primaryEditPressed(const WorldspaceHitResult& hit) +{ + if (hit.hit && hit.tag == nullptr) + { + if (mDragMode == InteractionType_PrimaryEdit) + { + editVertexColourGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + } + } + endShapeEditing(); +} + +void CSVRender::TerrainVertexPaintMode::primarySelectPressed(const WorldspaceHitResult& hit) +{ + if (hit.hit && hit.tag == nullptr) + { + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0); + mTerrainShapeSelection->clearTemporarySelection(); + } +} + +void CSVRender::TerrainVertexPaintMode::secondarySelectPressed(const WorldspaceHitResult& hit) +{ + if (hit.hit && hit.tag == nullptr) + { + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1); + mTerrainShapeSelection->clearTemporarySelection(); + } +} + +bool CSVRender::TerrainVertexPaintMode::primaryEditStartDrag(const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + + mDragMode = InteractionType_PrimaryEdit; + + if (hit.hit && hit.tag == nullptr) + { + mEditingPos = hit.worldPos; + mIsEditing = true; + } + + return true; +} + +bool CSVRender::TerrainVertexPaintMode::secondaryEditStartDrag(const QPoint& pos) +{ + return false; +} + +bool CSVRender::TerrainVertexPaintMode::primarySelectStartDrag(const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + mDragMode = InteractionType_PrimarySelect; + if (!hit.hit || hit.tag != nullptr) + { + mDragMode = InteractionType_None; + return false; + } + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0); + return true; +} + +bool CSVRender::TerrainVertexPaintMode::secondarySelectStartDrag(const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + mDragMode = InteractionType_SecondarySelect; + if (!hit.hit || hit.tag != nullptr) + { + mDragMode = InteractionType_None; + return false; + } + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1); + return true; +} + +void CSVRender::TerrainVertexPaintMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor) +{ + if (mDragMode == InteractionType_PrimaryEdit) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + mTotalDiffY += diffY; + if (mIsEditing) + editVertexColourGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); + } + + if (mDragMode == InteractionType_PrimarySelect) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + if (hit.hit && hit.tag == nullptr) + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0); + } + + if (mDragMode == InteractionType_SecondarySelect) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + if (hit.hit && hit.tag == nullptr) + selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1); + } +} + +void CSVRender::TerrainVertexPaintMode::dragCompleted(const QPoint& pos) +{ + if (mDragMode == InteractionType_PrimaryEdit) + { + endShapeEditing(); + } + if (mDragMode == InteractionType_PrimarySelect || mDragMode == InteractionType_SecondarySelect) + { + mTerrainShapeSelection->clearTemporarySelection(); + } +} + +void CSVRender::TerrainVertexPaintMode::dragAborted() +{ + endShapeEditing(); + mDragMode = InteractionType_None; +} + +void CSVRender::TerrainVertexPaintMode::dragWheel(int diff, double speedFactor) {} + +void CSVRender::TerrainVertexPaintMode::endShapeEditing() +{ + mIsEditing = false; + mTerrainShapeSelection->update(); +} + +void CSVRender::TerrainVertexPaintMode::editVertexColourGrid( + const std::pair& vertexCoords, bool dragOperation) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable + = dynamic_cast(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land)); + + std::string mCellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); + if (allowLandColourEditing(mCellId)) + { + } + + std::pair cellCoordinates_pair = CSMWorld::CellCoordinates::fromId(mCellId); + + int cellX = cellCoordinates_pair.first.getX(); + int cellY = cellCoordinates_pair.first.getY(); + + int xHitInCell = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.first); + int yHitInCell = CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(vertexCoords.second); + if (xHitInCell < 0) + { + xHitInCell = xHitInCell + ESM::Land::LAND_SIZE; + cellX = cellX - 1; + } + if (yHitInCell > 64) + { + yHitInCell = yHitInCell - ESM::Land::LAND_SIZE; + cellY = cellY + 1; + } + + mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY); + if (allowLandColourEditing(mCellId)) + { + } + + std::string iteratedCellId; + + int colourColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandColoursIndex); + + int r = std::max(static_cast(mBrushSize) / 2.0f, 1.0f); + + if (mBrushShape == CSVWidget::BrushShape_Point) + { + CSMWorld::LandColoursColumn::DataType newTerrain + = landTable.data(landTable.getModelIndex(mCellId, colourColumn)) + .value(); + + if (allowLandColourEditing(mCellId)) + { + alterColour(newTerrain, xHitInCell, yHitInCell, 0.0f); + pushEditToCommand(newTerrain, document, landTable, mCellId); + } + } + + if (mBrushShape == CSVWidget::BrushShape_Square) + { + int upperLeftCellX = cellX - std::floor(r / ESM::Land::LAND_SIZE); + int upperLeftCellY = cellY - std::floor(r / ESM::Land::LAND_SIZE); + if (xHitInCell - (r % ESM::Land::LAND_SIZE) < 0) + upperLeftCellX--; + if (yHitInCell - (r % ESM::Land::LAND_SIZE) < 0) + upperLeftCellY--; + + int lowerrightCellX = cellX + std::floor(r / ESM::Land::LAND_SIZE); + int lowerrightCellY = cellY + std::floor(r / ESM::Land::LAND_SIZE); + if (xHitInCell + (r % ESM::Land::LAND_SIZE) > ESM::Land::LAND_SIZE - 1) + lowerrightCellX++; + if (yHitInCell + (r % ESM::Land::LAND_SIZE) > ESM::Land::LAND_SIZE - 1) + lowerrightCellY++; + + for (int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++) + { + for (int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) + { + iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell); + if (allowLandColourEditing(iteratedCellId)) + { + CSMWorld::LandColoursColumn::DataType newTerrain + = landTable.data(landTable.getModelIndex(iteratedCellId, colourColumn)) + .value(); + for (int i = 0; i < ESM::Land::LAND_SIZE; i++) + { + for (int j = 0; j < ESM::Land::LAND_SIZE; j++) + { + + if (i_cell == cellX && j_cell == cellY && abs(i - xHitInCell) < r + && abs(j - yHitInCell) < r) + { + alterColour(newTerrain, i, j, 0.0f); + } + else + { + int distanceX(0); + int distanceY(0); + if (i_cell < cellX) + distanceX = xHitInCell + ESM::Land::LAND_SIZE * abs(i_cell - cellX) - i; + if (j_cell < cellY) + distanceY = yHitInCell + ESM::Land::LAND_SIZE * abs(j_cell - cellY) - j; + if (i_cell > cellX) + distanceX = -xHitInCell + ESM::Land::LAND_SIZE * abs(i_cell - cellX) + i; + if (j_cell > cellY) + distanceY = -yHitInCell + ESM::Land::LAND_SIZE * abs(j_cell - cellY) + j; + if (i_cell == cellX) + distanceX = abs(i - xHitInCell); + if (j_cell == cellY) + distanceY = abs(j - yHitInCell); + if (distanceX < r && distanceY < r) + alterColour(newTerrain, i, j, 0.0f); + } + } + } + pushEditToCommand(newTerrain, document, landTable, iteratedCellId); + } + } + } + } + + if (mBrushShape == CSVWidget::BrushShape_Circle) + { + int upperLeftCellX = cellX - std::floor(r / ESM::Land::LAND_SIZE); + int upperLeftCellY = cellY - std::floor(r / ESM::Land::LAND_SIZE); + if (xHitInCell - (r % ESM::Land::LAND_SIZE) < 0) + upperLeftCellX--; + if (yHitInCell - (r % ESM::Land::LAND_SIZE) < 0) + upperLeftCellY--; + + int lowerrightCellX = cellX + std::floor(r / ESM::Land::LAND_SIZE); + int lowerrightCellY = cellY + std::floor(r / ESM::Land::LAND_SIZE); + if (xHitInCell + (r % ESM::Land::LAND_SIZE) > ESM::Land::LAND_SIZE - 1) + lowerrightCellX++; + if (yHitInCell + (r % ESM::Land::LAND_SIZE) > ESM::Land::LAND_SIZE - 1) + lowerrightCellY++; + + for (int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++) + { + for (int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) + { + iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell); + if (allowLandColourEditing(iteratedCellId)) + { + CSMWorld::LandColoursColumn::DataType newTerrain + = landTable.data(landTable.getModelIndex(iteratedCellId, colourColumn)) + .value(); + for (int i = 0; i < ESM::Land::LAND_SIZE; i++) + { + for (int j = 0; j < ESM::Land::LAND_SIZE; j++) + { + if (i_cell == cellX && j_cell == cellY && abs(i - xHitInCell) < r + && abs(j - yHitInCell) < r) + { + int distanceX = abs(i - xHitInCell); + int distanceY = abs(j - yHitInCell); + float distance = std::round(sqrt(pow(distanceX, 2) + pow(distanceY, 2))); + if (distance < r) + alterColour(newTerrain, i, j, 0.0f); + } + else + { + int distanceX(0); + int distanceY(0); + if (i_cell < cellX) + distanceX = xHitInCell + ESM::Land::LAND_SIZE * abs(i_cell - cellX) - i; + if (j_cell < cellY) + distanceY = yHitInCell + ESM::Land::LAND_SIZE * abs(j_cell - cellY) - j; + if (i_cell > cellX) + distanceX = -xHitInCell + ESM::Land::LAND_SIZE * abs(i_cell - cellX) + i; + if (j_cell > cellY) + distanceY = -yHitInCell + ESM::Land::LAND_SIZE * abs(j_cell - cellY) + j; + if (i_cell == cellX) + distanceX = abs(i - xHitInCell); + if (j_cell == cellY) + distanceY = abs(j - yHitInCell); + float distance = std::round(sqrt(pow(distanceX, 2) + pow(distanceY, 2))); + if (distance < r) + alterColour(newTerrain, i, j, 0.0f); + } + } + } + pushEditToCommand(newTerrain, document, landTable, iteratedCellId); + } + } + } + } +} + +void CSVRender::TerrainVertexPaintMode::alterColour( + CSMWorld::LandColoursColumn::DataType& landColorsNew, int inCellX, int inCellY, float alteredHeight, bool useTool) +{ + const int red = mVertexPaintEditToolColor.red(); + const int green = mVertexPaintEditToolColor.green(); + const int blue = mVertexPaintEditToolColor.blue(); + + // TODO: handle different smoothing/blend types with different tools, right now this expects Replace + landColorsNew[(inCellY * ESM::Land::LAND_SIZE + inCellX) * 3 + 0] = red; + landColorsNew[(inCellY * ESM::Land::LAND_SIZE + inCellX) * 3 + 1] = green; + landColorsNew[(inCellY * ESM::Land::LAND_SIZE + inCellX) * 3 + 2] = blue; +} + +void CSVRender::TerrainVertexPaintMode::pushEditToCommand(const CSMWorld::LandColoursColumn::DataType& newLandColours, + CSMDoc::Document& document, CSMWorld::IdTable& landTable, const std::string& cellId) +{ + QVariant changedLand; + changedLand.setValue(newLandColours); + + QModelIndex index( + landTable.getModelIndex(cellId, landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandColoursIndex))); + + QUndoStack& undoStack = document.getUndoStack(); + undoStack.push(new CSMWorld::ModifyCommand(landTable, index, changedLand)); +} + +bool CSVRender::TerrainVertexPaintMode::isInCellSelection(int globalSelectionX, int globalSelectionY) +{ + if (CSVRender::PagedWorldspaceWidget* paged + = dynamic_cast(&getWorldspaceWidget())) + { + std::pair vertexCoords = std::make_pair(globalSelectionX, globalSelectionY); + std::string cellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords); + return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first) && isLandLoaded(cellId); + } + return false; +} + +void CSVRender::TerrainVertexPaintMode::handleSelection( + int globalSelectionX, int globalSelectionY, std::vector>* selections) +{ + if (isInCellSelection(globalSelectionX, globalSelectionY)) + selections->emplace_back(globalSelectionX, globalSelectionY); + else + { + int moduloX = globalSelectionX % (ESM::Land::LAND_SIZE - 1); + int moduloY = globalSelectionY % (ESM::Land::LAND_SIZE - 1); + bool xIsAtCellBorder = moduloX == 0; + bool yIsAtCellBorder = moduloY == 0; + if (!xIsAtCellBorder && !yIsAtCellBorder) + return; + int selectionX = globalSelectionX; + int selectionY = globalSelectionY; + + /* + The northern and eastern edges don't belong to the current cell. + If the corresponding adjacent cell is not loaded, some special handling is necessary to select border + vertices. + */ + if (xIsAtCellBorder && yIsAtCellBorder) + { + /* + Handle the NW, NE, and SE corner vertices. + NW corner: (+1, -1) offset to reach current cell. + NE corner: (-1, -1) offset to reach current cell. + SE corner: (-1, +1) offset to reach current cell. + */ + if (isInCellSelection(globalSelectionX - 1, globalSelectionY - 1) + || isInCellSelection(globalSelectionX + 1, globalSelectionY - 1) + || isInCellSelection(globalSelectionX - 1, globalSelectionY + 1)) + { + selections->emplace_back(globalSelectionX, globalSelectionY); + } + } + else if (xIsAtCellBorder) + { + selectionX--; + } + else if (yIsAtCellBorder) + { + selectionY--; + } + + if (isInCellSelection(selectionX, selectionY)) + selections->emplace_back(globalSelectionX, globalSelectionY); + } +} + +void CSVRender::TerrainVertexPaintMode::selectTerrainShapes( + const std::pair& vertexCoords, unsigned char selectMode) +{ + int r = mBrushSize / 2; + std::vector> selections; + + if (mBrushShape == CSVWidget::BrushShape_Point) + { + handleSelection(vertexCoords.first, vertexCoords.second, &selections); + } + + if (mBrushShape == CSVWidget::BrushShape_Square) + { + for (int i = vertexCoords.first - r; i <= vertexCoords.first + r; ++i) + { + for (int j = vertexCoords.second - r; j <= vertexCoords.second + r; ++j) + { + handleSelection(i, j, &selections); + } + } + } + + 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) + { + int distanceX = abs(i - vertexCoords.first); + int distanceY = abs(j - vertexCoords.second); + float distance = sqrt(pow(distanceX, 2) + pow(distanceY, 2)); + + // Using floating-point radius here to prevent selecting too few vertices. + if (distance <= mBrushSize / 2.0f) + handleSelection(i, j, &selections); + } + } + } + + std::string selectAction; + + if (selectMode == 0) + selectAction = CSMPrefs::get()["3D Scene Editing"]["primary-select-action"].toString(); + else + selectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString(); + + if (selectAction == "Select only") + mTerrainShapeSelection->onlySelect(selections); + else if (selectAction == "Add to selection") + mTerrainShapeSelection->addSelect(selections); + else if (selectAction == "Remove from selection") + mTerrainShapeSelection->removeSelect(selections); + else if (selectAction == "Invert selection") + mTerrainShapeSelection->toggleSelect(selections); +} + +bool CSVRender::TerrainVertexPaintMode::noCell(const std::string& cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + const CSMWorld::IdCollection& cellCollection = document.getData().getCells(); + return cellCollection.searchId(ESM::RefId::stringRefId(cellId)) == -1; +} + +bool CSVRender::TerrainVertexPaintMode::noLand(const std::string& cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + const CSMWorld::IdCollection& landCollection = document.getData().getLand(); + return landCollection.searchId(ESM::RefId::stringRefId(cellId)) == -1; +} + +bool CSVRender::TerrainVertexPaintMode::noLandLoaded(const std::string& cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + const CSMWorld::IdCollection& landCollection = document.getData().getLand(); + return !landCollection.getRecord(ESM::RefId::stringRefId(cellId)).get().isDataLoaded(ESM::Land::DATA_VNML); +} + +bool CSVRender::TerrainVertexPaintMode::isLandLoaded(const std::string& cellId) +{ + if (!noCell(cellId) && !noLand(cellId) && !noLandLoaded(cellId)) + return true; + return false; +} + +void CSVRender::TerrainVertexPaintMode::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(cellDownCoords, 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::TerrainVertexPaintMode::allowLandColourEditing(const std::string& cellId, bool useTool) +{ + 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)); + + if (noCell(cellId)) + { + 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" && useTool) + { + auto createCommand = std::make_unique(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" && useTool) + { + selection.add(CSMWorld::CellCoordinates::fromId(cellId).first); + paged->setCellSelection(selection); + } + } + } + + if (noLand(cellId)) + { + 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" && useTool) + { + document.getUndoStack().push(new CSMWorld::CreateCommand(landTable, cellId)); + createNewLandData(CSMWorld::CellCoordinates::fromId(cellId).first); + } + } + 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); + } + } + + if (useTool && (noCell(cellId) || noLand(cellId) || noLandLoaded(cellId))) + { + Log(Debug::Warning) << "Land creation failed at cell id: " << cellId; + return false; + } + return true; +} + +void CSVRender::TerrainVertexPaintMode::dragMoveEvent(QDragMoveEvent* event) {} + +void CSVRender::TerrainVertexPaintMode::mouseMoveEvent(QMouseEvent* event) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); + if (hit.hit && mBrushDraw) + mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape); + if (!hit.hit && mBrushDraw) + mBrushDraw->hide(); +} + +std::shared_ptr CSVRender::TerrainVertexPaintMode::getTerrainSelection() +{ + return mTerrainShapeSelection; +} + +void CSVRender::TerrainVertexPaintMode::setBrushSize(int brushSize) +{ + mBrushSize = brushSize; +} + +void CSVRender::TerrainVertexPaintMode::setBrushShape(CSVWidget::BrushShape brushShape) +{ + mBrushShape = brushShape; +} + +void CSVRender::TerrainVertexPaintMode::setVertexPaintEditTool(int shapeEditTool) +{ + mVertexPaintEditTool = shapeEditTool; +} + +void CSVRender::TerrainVertexPaintMode::setVertexPaintColor(const QColor& color) +{ + mVertexPaintEditToolColor = color; +} + +CSVRender::PagedWorldspaceWidget& CSVRender::TerrainVertexPaintMode::getPagedWorldspaceWidget() +{ + return dynamic_cast(getWorldspaceWidget()); +} diff --git a/apps/opencs/view/render/terrainvertexpaintmode.hpp b/apps/opencs/view/render/terrainvertexpaintmode.hpp new file mode 100644 index 0000000000..8dd8f9f8e1 --- /dev/null +++ b/apps/opencs/view/render/terrainvertexpaintmode.hpp @@ -0,0 +1,180 @@ +#ifndef CSV_RENDER_TERRAINVERTEXPAINTMODE_H +#define CSV_RENDER_TERRAINVERTEXPAINTMODE_H + +#include "editmode.hpp" + +#include +#include +#include +#include + +#include + +#include + +#ifndef Q_MOC_RUN +#include "../../model/world/columnimp.hpp" +#include "../widget/brushshapes.hpp" +#endif + +#include "brushdraw.hpp" + +class QDragMoveEvent; +class QMouseEvent; +class QObject; +class QPoint; +class QWidget; + +namespace osg +{ + class Group; +} + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWidget +{ + class SceneToolVertexPaintBrush; +} + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSVRender +{ + class PagedWorldspaceWidget; + class TerrainSelection; + class WorldspaceWidget; + struct WorldspaceHitResult; + class SceneToolbar; + + /// \brief EditMode for handling the terrain shape editing + class TerrainVertexPaintMode : public EditMode + { + Q_OBJECT + + public: + enum InteractionType + { + InteractionType_PrimaryEdit, + InteractionType_PrimarySelect, + InteractionType_SecondaryEdit, + InteractionType_SecondarySelect, + InteractionType_None + }; + + enum VertexPaintEditTool + { + VertexPaintEditTool_Replace = 0 + }; + + /// Editmode for terrain shape grid + TerrainVertexPaintMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); + + void primaryOpenPressed(const WorldspaceHitResult& hit) override; + + /// Create single command for one-click shape editing + void primaryEditPressed(const WorldspaceHitResult& hit) override; + + /// Open brush settings window + void primarySelectPressed(const WorldspaceHitResult&) override; + + void secondarySelectPressed(const WorldspaceHitResult&) override; + + void activate(CSVWidget::SceneToolbar*) override; + void deactivate(CSVWidget::SceneToolbar*) override; + + /// Start shape editing command macro + bool primaryEditStartDrag(const QPoint& pos) override; + + bool secondaryEditStartDrag(const QPoint& pos) override; + bool primarySelectStartDrag(const QPoint& pos) override; + bool secondarySelectStartDrag(const QPoint& pos) override; + + /// Handle shape edit behavior during dragging + void drag(const QPoint& pos, int diffX, int diffY, double speedFactor) override; + + /// End shape editing command macro + void dragCompleted(const QPoint& pos) override; + + /// Cancel shape editing, and reset all pending changes + void dragAborted() override; + + void dragWheel(int diff, double speedFactor) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + std::shared_ptr getTerrainSelection(); + + private: + /// Reset everything in the current edit + void endShapeEditing(); + + /// Handle brush mechanics for colour editing + void editVertexColourGrid(const std::pair& vertexCoords, bool dragOperation); + + /// Alter one pixel's colour + void alterColour(CSMWorld::LandColoursColumn::DataType& landColorsNew, int inCellX, int inCellY, + float alteredHeight, bool useTool = true); + + /// Check if global selection coordinate belongs to cell in view + bool isInCellSelection(int globalSelectionX, int globalSelectionY); + + /// Select vertex at global selection coordinate + void handleSelection(int globalSelectionX, int globalSelectionY, std::vector>* selections); + + /// Handle brush mechanics for terrain shape selection + void selectTerrainShapes(const std::pair& vertexCoords, unsigned char selectMode); + + bool noCell(const std::string& cellId); + + bool noLand(const std::string& cellId); + + bool noLandLoaded(const std::string& cellId); + + bool isLandLoaded(const std::string& cellId); + + /// Push terrain shape edits to command macro + void pushEditToCommand(const CSMWorld::LandColoursColumn::DataType& newLandColours, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, 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 allowLandColourEditing(const std::string& textureFileName, bool useTool = true); + + std::string mBrushTexture; + int mBrushSize = 1; + CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + std::unique_ptr mBrushDraw; + CSVWidget::SceneToolVertexPaintBrush* mVertexPaintBrushScenetool = nullptr; + int mDragMode = InteractionType_None; + osg::Group* mParentNode; + bool mIsEditing = false; + std::shared_ptr mTerrainShapeSelection; + int mTotalDiffY = 0; + std::vector mAlteredCells; + osg::Vec3d mEditingPos; + int mVertexPaintEditTool = VertexPaintEditTool_Replace; + QColor mVertexPaintEditToolColor; + int mTargetHeight = 0; + + PagedWorldspaceWidget& getPagedWorldspaceWidget(); + + public slots: + void setBrushSize(int brushSize); + void setBrushShape(CSVWidget::BrushShape brushShape); + void setVertexPaintEditTool(int shapeEditTool); + void setVertexPaintColor(const QColor& color); + }; +} + +#endif diff --git a/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp b/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp new file mode 100644 index 0000000000..ad49a282e9 --- /dev/null +++ b/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp @@ -0,0 +1,234 @@ +#include "scenetoolvertexpaintbrush.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "brushshapes.hpp" +#include "scenetool.hpp" + +#include "../../model/prefs/state.hpp" + +namespace CSVWidget +{ + class SceneToolbar; +} + +namespace CSMDoc +{ + class Document; +} + +CSVWidget::VertexPaintBrushSizeControls::VertexPaintBrushSizeControls(const QString& title, QWidget* parent) + : QGroupBox(title, parent) +{ + mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); + mBrushSizeSlider->setTickInterval(10); + mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mBrushSizeSlider->setSingleStep(1); + + mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mBrushSizeSpinBox->setSingleStep(1); + + QHBoxLayout* layoutSliderSize = new QHBoxLayout; + layoutSliderSize->addWidget(mBrushSizeSlider); + layoutSliderSize->addWidget(mBrushSizeSpinBox); + + connect(mBrushSizeSlider, &QSlider::valueChanged, mBrushSizeSpinBox, &QSpinBox::setValue); + connect(mBrushSizeSpinBox, qOverload(&QSpinBox::valueChanged), mBrushSizeSlider, &QSlider::setValue); + + setLayout(layoutSliderSize); +} + +CSVWidget::VertexPaintBrushWindow::VertexPaintBrushWindow(CSMDoc::Document& document, QWidget* parent) + : QFrame(parent, Qt::Popup) + , 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); + + mSizeSliders = new VertexPaintBrushSizeControls("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); + + mButtonPoint->setToolTip(toolTipPoint); + mButtonSquare->setToolTip(toolTipSquare); + mButtonCircle->setToolTip(toolTipCircle); + + QButtonGroup* brushButtonGroup = new QButtonGroup(this); + brushButtonGroup->addButton(mButtonPoint); + brushButtonGroup->addButton(mButtonSquare); + brushButtonGroup->addButton(mButtonCircle); + + brushButtonGroup->setExclusive(true); + + layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop); + + mHorizontalGroupBox = new QGroupBox(tr("")); + mHorizontalGroupBox->setLayout(layoutHorizontal); + + mToolSelector = new QComboBox(this); + mToolSelector->addItem(tr("Replace")); + // TOOD: in the future could add types like smooth blend, multiply etc + + QLabel* colorLabel = new QLabel(this); + colorLabel->setText("Color:"); + mColorButtonWidget = new ColorButtonWidget(); + + layoutMain->addWidget(mHorizontalGroupBox); + layoutMain->addWidget(mSizeSliders); + layoutMain->addWidget(mToolSelector); + layoutMain->addWidget(colorLabel); + layoutMain->addWidget(mColorButtonWidget); + + setLayout(layoutMain); + + connect(mButtonPoint, &QPushButton::clicked, this, &VertexPaintBrushWindow::setBrushShape); + connect(mButtonSquare, &QPushButton::clicked, this, &VertexPaintBrushWindow::setBrushShape); + connect(mButtonCircle, &QPushButton::clicked, this, &VertexPaintBrushWindow::setBrushShape); +} + +void CSVWidget::VertexPaintBrushWindow::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::VertexPaintBrushWindow::setBrushSize(int brushSize) +{ + mBrushSize = brushSize; + emit passBrushSize(mBrushSize); +} + +void CSVWidget::VertexPaintBrushWindow::setBrushShape() +{ + if (mButtonPoint->isChecked()) + mBrushShape = BrushShape_Point; + if (mButtonSquare->isChecked()) + mBrushShape = BrushShape_Square; + if (mButtonCircle->isChecked()) + mBrushShape = BrushShape_Circle; + emit passBrushShape(mBrushShape); +} + +void CSVWidget::SceneToolVertexPaintBrush::adjustToolTips() {} + +CSVWidget::SceneToolVertexPaintBrush::SceneToolVertexPaintBrush( + SceneToolbar* parent, const QString& toolTip, CSMDoc::Document& document) + : SceneTool(parent, Type_TopAction) + , mToolTip(toolTip) + , mDocument(document) + , mVertexPaintBrushWindow(new VertexPaintBrushWindow(document, this)) +{ + setAcceptDrops(true); + connect(mVertexPaintBrushWindow, &VertexPaintBrushWindow::passBrushShape, this, + &SceneToolVertexPaintBrush::setButtonIcon); + setButtonIcon(mVertexPaintBrushWindow->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(); + mTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + mTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + mTable->setSelectionMode(QAbstractItemView::NoSelection); + + layout->addWidget(mTable); + + connect(mTable, &QTableWidget::clicked, this, &SceneToolVertexPaintBrush::clicked); +} + +void CSVWidget::SceneToolVertexPaintBrush::setButtonIcon(CSVWidget::BrushShape brushShape) +{ + QString tooltip = "Change brush settings

Currently selected: "; + + switch (brushShape) + { + case BrushShape_Point: + + setIcon(QIcon(QPixmap(":scenetoolbar/brush-point"))); + tooltip += mVertexPaintBrushWindow->toolTipPoint; + break; + + case BrushShape_Square: + + setIcon(QIcon(QPixmap(":scenetoolbar/brush-square"))); + tooltip += mVertexPaintBrushWindow->toolTipSquare; + break; + + case BrushShape_Circle: + + setIcon(QIcon(QPixmap(":scenetoolbar/brush-circle"))); + tooltip += mVertexPaintBrushWindow->toolTipCircle; + break; + } + + setToolTip(tooltip); +} + +void CSVWidget::SceneToolVertexPaintBrush::showPanel(const QPoint& position) {} + +void CSVWidget::SceneToolVertexPaintBrush::updatePanel() {} + +void CSVWidget::SceneToolVertexPaintBrush::clicked(const QModelIndex& index) {} + +void CSVWidget::SceneToolVertexPaintBrush::activate() +{ + QPoint position = QCursor::pos(); + mVertexPaintBrushWindow->mSizeSliders->mBrushSizeSlider->setRange( + 1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mVertexPaintBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange( + 1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); + mVertexPaintBrushWindow->move(position); + mVertexPaintBrushWindow->show(); +} + +void CSVWidget::SceneToolVertexPaintBrush::dragEnterEvent(QDragEnterEvent* event) +{ + emit passEvent(event); + event->accept(); +} +void CSVWidget::SceneToolVertexPaintBrush::dropEvent(QDropEvent* event) +{ + emit passEvent(event); + event->accept(); +} diff --git a/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp b/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp new file mode 100644 index 0000000000..35f3ee3ec4 --- /dev/null +++ b/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp @@ -0,0 +1,164 @@ +#ifndef CSV_WIDGET_SCENETOOLVERTEXPAINTBRUSH_H +#define CSV_WIDGET_SCENETOOLVERTEXPAINTBRUSH_H + +#include +#include +#include +#include +#include + +#ifndef Q_MOC_RUN +#include "brushshapes.hpp" +#include "scenetool.hpp" +#endif + +class QComboBox; +class QDragEnterEvent; +class QDropEvent; +class QModelIndex; +class QObject; +class QPoint; +class QPushButton; +class QWidget; + +namespace CSMDoc +{ + class Document; +} + +class QTableWidget; + +namespace CSVRender +{ + class TerrainVertexPaintMode; +} + +namespace CSVWidget +{ + class SceneToolbar; + + class ColorButtonWidget : public QPushButton + { + Q_OBJECT + + public: + ColorButtonWidget(QWidget* parent = nullptr) + : QPushButton(parent) + { + this->setFixedSize(50, 25); + this->setObjectName("colorSwatchButton"); + this->setStyleSheet("QPushButton#colorSwatchButton { border: 1px solid #ccc; }"); + + connect(this, &QPushButton::clicked, this, &ColorButtonWidget::openColorDialog); + } + + private: + QColor mColor = Qt::white; + + signals: + void colorChanged(const QColor& newColor); + + private slots: + void openColorDialog() + { + QColor color = QColorDialog::getColor(mColor, this, "Select Color"); + if (color.isValid()) + { + mColor = color; + QString css = QString("QPushButton#colorSwatchButton { background-color: %1; border: 1px solid #ccc; }") + .arg(color.name()); + this->setStyleSheet(css); + emit colorChanged(color); + } + } + }; + + /// \brief Layout-box for some brush button settings + class VertexPaintBrushSizeControls : public QGroupBox + { + Q_OBJECT + + public: + VertexPaintBrushSizeControls(const QString& title, QWidget* parent); + + private: + QSlider* mBrushSizeSlider = new QSlider(Qt::Horizontal); + QSpinBox* mBrushSizeSpinBox = new QSpinBox; + + friend class SceneToolVertexPaintBrush; + friend class CSVRender::TerrainVertexPaintMode; + }; + + /// \brief Brush settings window + class VertexPaintBrushWindow : public QFrame + { + Q_OBJECT + + public: + VertexPaintBrushWindow(CSMDoc::Document& document, QWidget* parent = nullptr); + void configureButtonInitialSettings(QPushButton* button); + + const QString toolTipPoint = "Paint single point"; + const QString toolTipSquare = "Paint with square brush"; + const QString toolTipCircle = "Paint with circle brush"; + + private: + CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + int mBrushSize = 1; + CSMDoc::Document& mDocument; + QGroupBox* mHorizontalGroupBox; + QComboBox* mToolSelector; + QPushButton* mButtonPoint; + QPushButton* mButtonSquare; + QPushButton* mButtonCircle; + VertexPaintBrushSizeControls* mSizeSliders; + ColorButtonWidget* mColorButtonWidget; + + friend class SceneToolVertexPaintBrush; + friend class CSVRender::TerrainVertexPaintMode; + + public slots: + void setBrushShape(); + void setBrushSize(int brushSize); + + signals: + void passBrushSize(int brushSize); + void passBrushShape(CSVWidget::BrushShape brushShape); + }; + + class SceneToolVertexPaintBrush : public SceneTool + { + Q_OBJECT + + QString mToolTip; + CSMDoc::Document& mDocument; + QFrame* mPanel; + QTableWidget* mTable; + VertexPaintBrushWindow* mVertexPaintBrushWindow; + + private: + void adjustToolTips(); + + public: + SceneToolVertexPaintBrush(SceneToolbar* parent, const QString& toolTip, CSMDoc::Document& document); + + void showPanel(const QPoint& position) override; + void updatePanel(); + + void dropEvent(QDropEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + + friend class CSVRender::TerrainVertexPaintMode; + + public slots: + void setButtonIcon(CSVWidget::BrushShape brushShape); + void clicked(const QModelIndex& index); + void activate() override; + + signals: + void passEvent(QDropEvent* event); + void passEvent(QDragEnterEvent* event); + }; +} + +#endif From 3e24fe8d4dd76984cbee33121e5130611e070cfb Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Mon, 15 Apr 2024 03:47:53 +0100 Subject: [PATCH 2/8] Fix build --- apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp | 6 ++++++ apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp b/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp index ad49a282e9..d5da14e991 100644 --- a/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp +++ b/apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp @@ -200,6 +200,12 @@ void CSVWidget::SceneToolVertexPaintBrush::setButtonIcon(CSVWidget::BrushShape b setIcon(QIcon(QPixmap(":scenetoolbar/brush-circle"))); tooltip += mVertexPaintBrushWindow->toolTipCircle; break; + + case BrushShape_Custom: + + setIcon(QIcon(QPixmap(":scenetoolbar/brush-custom"))); + tooltip += mVertexPaintBrushWindow->toolTipCustom; + break; } setToolTip(tooltip); diff --git a/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp b/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp index 35f3ee3ec4..a9b845d231 100644 --- a/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp +++ b/apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp @@ -101,6 +101,7 @@ namespace CSVWidget const QString toolTipPoint = "Paint single point"; const QString toolTipSquare = "Paint with square brush"; const QString toolTipCircle = "Paint with circle brush"; + const QString toolTipCustom = "Paint with custom brush"; private: CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; From 7d8202ce5c3c484dc50c0561dcd24e4b39e91077 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sat, 27 Apr 2024 00:12:40 +0100 Subject: [PATCH 3/8] Update icon --- apps/opencs/view/render/terrainvertexpaintmode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/terrainvertexpaintmode.cpp b/apps/opencs/view/render/terrainvertexpaintmode.cpp index 26b227d2c2..e66e0a407c 100644 --- a/apps/opencs/view/render/terrainvertexpaintmode.cpp +++ b/apps/opencs/view/render/terrainvertexpaintmode.cpp @@ -62,7 +62,7 @@ namespace osg CSVRender::TerrainVertexPaintMode::TerrainVertexPaintMode( WorldspaceWidget* worldspaceWidget, osg::Group* parentNode, QWidget* parent) - : EditMode(worldspaceWidget, QIcon{ ":placeholder" }, Mask_Terrain, "Terrain vertex paint editing", parent) + : EditMode(worldspaceWidget, QIcon{ ":scenetoolbar/editing-terrain-vertex-paint" }, Mask_Terrain, "Terrain vertex paint editing", parent) , mParentNode(parentNode) , mVertexPaintEditToolColor(Qt::white) { From ae65c946c40910624177b1525a898af17b38cee9 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sat, 27 Apr 2024 00:16:14 +0100 Subject: [PATCH 4/8] Hail clang --- apps/opencs/view/render/terrainvertexpaintmode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/terrainvertexpaintmode.cpp b/apps/opencs/view/render/terrainvertexpaintmode.cpp index e66e0a407c..7e2caa67d3 100644 --- a/apps/opencs/view/render/terrainvertexpaintmode.cpp +++ b/apps/opencs/view/render/terrainvertexpaintmode.cpp @@ -62,7 +62,8 @@ namespace osg CSVRender::TerrainVertexPaintMode::TerrainVertexPaintMode( WorldspaceWidget* worldspaceWidget, osg::Group* parentNode, QWidget* parent) - : EditMode(worldspaceWidget, QIcon{ ":scenetoolbar/editing-terrain-vertex-paint" }, Mask_Terrain, "Terrain vertex paint editing", parent) + : EditMode(worldspaceWidget, QIcon{ ":scenetoolbar/editing-terrain-vertex-paint" }, Mask_Terrain, + "Terrain vertex paint editing", parent) , mParentNode(parentNode) , mVertexPaintEditToolColor(Qt::white) { From e29dee8ddf80d165b314634ecd8d75d08fb0841d Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sat, 27 Apr 2024 00:17:42 +0100 Subject: [PATCH 5/8] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aeb030bda5..5f703b0bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -172,6 +172,7 @@ Feature #2566: Handle NAM9 records for manual cell references Feature #3537: Shader-based water ripples Feature #5173: Support for NiFogProperty + Feature #5197: Editor: terrain vertex paint editmode Feature #5492: Let rain and snow collide with statics Feature #5926: Refraction based on water depth Feature #5944: Option to use camera as sound listener From 990eae58d51b70dbf6b63ed477ee1e787e5a12a1 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sun, 12 May 2024 00:28:12 +0100 Subject: [PATCH 6/8] Fix undo to use macros --- .../view/render/pagedworldspacewidget.cpp | 3 +- .../view/render/terrainvertexpaintmode.cpp | 53 +++++++++++++------ .../view/render/terrainvertexpaintmode.hpp | 20 +++---- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index d0167b1e63..109848c85b 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -172,7 +172,8 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons(CSVWidget::Sce tool->addButton(new TerrainShapeMode(this, mRootNode, tool), "terrain-shape"); tool->addButton(new TerrainTextureMode(this, mRootNode, tool), "terrain-texture"); tool->addButton(new TerrainVertexPaintMode(this, mRootNode, tool), "terrain-vertex"); - tool->addButton(new EditMode(this, QIcon(":placeholder"), Mask_Reference, "Terrain movement"), "terrain-move"); + const QIcon movementIcon = QIcon(":scenetoolbar/editing-terrain-movement"); + tool->addButton(new EditMode(this, movementIcon, Mask_Reference, "Terrain movement"), "terrain-move"); } void CSVRender::PagedWorldspaceWidget::handleInteractionPress(const WorldspaceHitResult& hit, InteractionType type) diff --git a/apps/opencs/view/render/terrainvertexpaintmode.cpp b/apps/opencs/view/render/terrainvertexpaintmode.cpp index 7e2caa67d3..195c2406fa 100644 --- a/apps/opencs/view/render/terrainvertexpaintmode.cpp +++ b/apps/opencs/view/render/terrainvertexpaintmode.cpp @@ -71,9 +71,9 @@ CSVRender::TerrainVertexPaintMode::TerrainVertexPaintMode( void CSVRender::TerrainVertexPaintMode::activate(CSVWidget::SceneToolbar* toolbar) { - if (!mTerrainShapeSelection) + if (!mTerrainSelection) { - mTerrainShapeSelection + mTerrainSelection = std::make_shared(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Shape); } @@ -107,9 +107,9 @@ void CSVRender::TerrainVertexPaintMode::deactivate(CSVWidget::SceneToolbar* tool toolbar->removeTool(mVertexPaintBrushScenetool); } - if (mTerrainShapeSelection) + if (mTerrainSelection) { - mTerrainShapeSelection.reset(); + mTerrainSelection.reset(); } if (mBrushDraw) @@ -128,10 +128,13 @@ void CSVRender::TerrainVertexPaintMode::primaryEditPressed(const WorldspaceHitRe { if (mDragMode == InteractionType_PrimaryEdit) { + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + // QUndoStack& undoStack = document.getUndoStack(); + // undoStack.beginMacro("Set land normals"); editVertexColourGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); } } - endShapeEditing(); + endVertexPaintEditing(); } void CSVRender::TerrainVertexPaintMode::primarySelectPressed(const WorldspaceHitResult& hit) @@ -139,7 +142,7 @@ void CSVRender::TerrainVertexPaintMode::primarySelectPressed(const WorldspaceHit if (hit.hit && hit.tag == nullptr) { selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0); - mTerrainShapeSelection->clearTemporarySelection(); + mTerrainSelection->clearTemporarySelection(); } } @@ -148,7 +151,7 @@ void CSVRender::TerrainVertexPaintMode::secondarySelectPressed(const WorldspaceH if (hit.hit && hit.tag == nullptr) { selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1); - mTerrainShapeSelection->clearTemporarySelection(); + mTerrainSelection->clearTemporarySelection(); } } @@ -162,6 +165,9 @@ bool CSVRender::TerrainVertexPaintMode::primaryEditStartDrag(const QPoint& pos) { mEditingPos = hit.worldPos; mIsEditing = true; + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + QUndoStack& undoStack = document.getUndoStack(); + undoStack.beginMacro("Set land normals"); } return true; @@ -227,26 +233,38 @@ void CSVRender::TerrainVertexPaintMode::dragCompleted(const QPoint& pos) { if (mDragMode == InteractionType_PrimaryEdit) { - endShapeEditing(); + if (mIsEditing) + { + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + QUndoStack& undoStack = document.getUndoStack(); + undoStack.endMacro(); + } + endVertexPaintEditing(); } if (mDragMode == InteractionType_PrimarySelect || mDragMode == InteractionType_SecondarySelect) { - mTerrainShapeSelection->clearTemporarySelection(); + mTerrainSelection->clearTemporarySelection(); } } void CSVRender::TerrainVertexPaintMode::dragAborted() { - endShapeEditing(); + if (mIsEditing) + { + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + QUndoStack& undoStack = document.getUndoStack(); + undoStack.endMacro(); + } + endVertexPaintEditing(); mDragMode = InteractionType_None; } void CSVRender::TerrainVertexPaintMode::dragWheel(int diff, double speedFactor) {} -void CSVRender::TerrainVertexPaintMode::endShapeEditing() +void CSVRender::TerrainVertexPaintMode::endVertexPaintEditing() { mIsEditing = false; - mTerrainShapeSelection->update(); + mTerrainSelection->update(); } void CSVRender::TerrainVertexPaintMode::editVertexColourGrid( @@ -458,6 +476,7 @@ void CSVRender::TerrainVertexPaintMode::pushEditToCommand(const CSMWorld::LandCo QUndoStack& undoStack = document.getUndoStack(); undoStack.push(new CSMWorld::ModifyCommand(landTable, index, changedLand)); + } bool CSVRender::TerrainVertexPaintMode::isInCellSelection(int globalSelectionX, int globalSelectionY) @@ -569,13 +588,13 @@ void CSVRender::TerrainVertexPaintMode::selectTerrainShapes( selectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString(); if (selectAction == "Select only") - mTerrainShapeSelection->onlySelect(selections); + mTerrainSelection->onlySelect(selections); else if (selectAction == "Add to selection") - mTerrainShapeSelection->addSelect(selections); + mTerrainSelection->addSelect(selections); else if (selectAction == "Remove from selection") - mTerrainShapeSelection->removeSelect(selections); + mTerrainSelection->removeSelect(selections); else if (selectAction == "Invert selection") - mTerrainShapeSelection->toggleSelect(selections); + mTerrainSelection->toggleSelect(selections); } bool CSVRender::TerrainVertexPaintMode::noCell(const std::string& cellId) @@ -823,7 +842,7 @@ void CSVRender::TerrainVertexPaintMode::mouseMoveEvent(QMouseEvent* event) std::shared_ptr CSVRender::TerrainVertexPaintMode::getTerrainSelection() { - return mTerrainShapeSelection; + return mTerrainSelection; } void CSVRender::TerrainVertexPaintMode::setBrushSize(int brushSize) diff --git a/apps/opencs/view/render/terrainvertexpaintmode.hpp b/apps/opencs/view/render/terrainvertexpaintmode.hpp index 8dd8f9f8e1..a713175fe4 100644 --- a/apps/opencs/view/render/terrainvertexpaintmode.hpp +++ b/apps/opencs/view/render/terrainvertexpaintmode.hpp @@ -73,12 +73,12 @@ namespace CSVRender VertexPaintEditTool_Replace = 0 }; - /// Editmode for terrain shape grid + /// Editmode for terrain vertex colour grid TerrainVertexPaintMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); void primaryOpenPressed(const WorldspaceHitResult& hit) override; - /// Create single command for one-click shape editing + /// Create single command for one-click vertex paint editing void primaryEditPressed(const WorldspaceHitResult& hit) override; /// Open brush settings window @@ -89,20 +89,20 @@ namespace CSVRender void activate(CSVWidget::SceneToolbar*) override; void deactivate(CSVWidget::SceneToolbar*) override; - /// Start shape editing command macro + /// Start vertex paint editing command macro bool primaryEditStartDrag(const QPoint& pos) override; bool secondaryEditStartDrag(const QPoint& pos) override; bool primarySelectStartDrag(const QPoint& pos) override; bool secondarySelectStartDrag(const QPoint& pos) override; - /// Handle shape edit behavior during dragging + /// Handle vertex paint edit behavior during dragging void drag(const QPoint& pos, int diffX, int diffY, double speedFactor) override; - /// End shape editing command macro + /// End vertex paint editing command macro void dragCompleted(const QPoint& pos) override; - /// Cancel shape editing, and reset all pending changes + /// Cancel vertex paint editing, and reset all pending changes void dragAborted() override; void dragWheel(int diff, double speedFactor) override; @@ -113,7 +113,7 @@ namespace CSVRender private: /// Reset everything in the current edit - void endShapeEditing(); + void endVertexPaintEditing(); /// Handle brush mechanics for colour editing void editVertexColourGrid(const std::pair& vertexCoords, bool dragOperation); @@ -128,7 +128,7 @@ namespace CSVRender /// Select vertex at global selection coordinate void handleSelection(int globalSelectionX, int globalSelectionY, std::vector>* selections); - /// Handle brush mechanics for terrain shape selection + /// Handle brush mechanics for terrain selection void selectTerrainShapes(const std::pair& vertexCoords, unsigned char selectMode); bool noCell(const std::string& cellId); @@ -139,7 +139,7 @@ namespace CSVRender bool isLandLoaded(const std::string& cellId); - /// Push terrain shape edits to command macro + /// Push terrain vertex coloir edits to command macro void pushEditToCommand(const CSMWorld::LandColoursColumn::DataType& newLandColours, CSMDoc::Document& document, CSMWorld::IdTable& landTable, const std::string& cellId); @@ -159,7 +159,7 @@ namespace CSVRender int mDragMode = InteractionType_None; osg::Group* mParentNode; bool mIsEditing = false; - std::shared_ptr mTerrainShapeSelection; + std::shared_ptr mTerrainSelection; int mTotalDiffY = 0; std::vector mAlteredCells; osg::Vec3d mEditingPos; From decc3879305423d3981a171bf4a6aff03ab4317b Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sat, 18 May 2024 22:00:51 +0100 Subject: [PATCH 7/8] Formatting fix --- apps/opencs/view/render/terrainvertexpaintmode.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terrainvertexpaintmode.cpp b/apps/opencs/view/render/terrainvertexpaintmode.cpp index 195c2406fa..d78d21c776 100644 --- a/apps/opencs/view/render/terrainvertexpaintmode.cpp +++ b/apps/opencs/view/render/terrainvertexpaintmode.cpp @@ -254,7 +254,7 @@ void CSVRender::TerrainVertexPaintMode::dragAborted() CSMDoc::Document& document = getWorldspaceWidget().getDocument(); QUndoStack& undoStack = document.getUndoStack(); undoStack.endMacro(); - } + } endVertexPaintEditing(); mDragMode = InteractionType_None; } @@ -476,7 +476,6 @@ void CSVRender::TerrainVertexPaintMode::pushEditToCommand(const CSMWorld::LandCo QUndoStack& undoStack = document.getUndoStack(); undoStack.push(new CSMWorld::ModifyCommand(landTable, index, changedLand)); - } bool CSVRender::TerrainVertexPaintMode::isInCellSelection(int globalSelectionX, int globalSelectionY) From 4d9cdb6128c74d25d32442783a5955104a3f70b4 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sun, 19 May 2024 01:43:43 +0100 Subject: [PATCH 8/8] Cleanup --- apps/opencs/view/render/terrainvertexpaintmode.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/opencs/view/render/terrainvertexpaintmode.cpp b/apps/opencs/view/render/terrainvertexpaintmode.cpp index d78d21c776..93cdf1d37b 100644 --- a/apps/opencs/view/render/terrainvertexpaintmode.cpp +++ b/apps/opencs/view/render/terrainvertexpaintmode.cpp @@ -128,9 +128,6 @@ void CSVRender::TerrainVertexPaintMode::primaryEditPressed(const WorldspaceHitRe { if (mDragMode == InteractionType_PrimaryEdit) { - CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - // QUndoStack& undoStack = document.getUndoStack(); - // undoStack.beginMacro("Set land normals"); editVertexColourGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); } } @@ -167,7 +164,7 @@ bool CSVRender::TerrainVertexPaintMode::primaryEditStartDrag(const QPoint& pos) mIsEditing = true; CSMDoc::Document& document = getWorldspaceWidget().getDocument(); QUndoStack& undoStack = document.getUndoStack(); - undoStack.beginMacro("Set land normals"); + undoStack.beginMacro("Set land vertex colours"); } return true;