mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-14 01:19:59 +00:00
Merge branch 'feat-openmw-cs-colour-paint' into 'master'
OpenMW-CS: Land color painting See merge request OpenMW/openmw!4031
This commit is contained in:
commit
4e0610300b
@ -234,6 +234,7 @@
|
||||
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "mask.hpp"
|
||||
#include "terrainshapemode.hpp"
|
||||
#include "terraintexturemode.hpp"
|
||||
#include "terrainvertexpaintmode.hpp"
|
||||
|
||||
class QWidget;
|
||||
|
||||
@ -170,9 +171,8 @@ 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");
|
||||
const QIcon vertexIcon = Misc::ScalableIcon::load(":scenetoolbar/editing-terrain-vertex-paint");
|
||||
tool->addButton(new TerrainVertexPaintMode(this, mRootNode, tool), "terrain-vertex");
|
||||
const QIcon movementIcon = Misc::ScalableIcon::load(":scenetoolbar/editing-terrain-movement");
|
||||
tool->addButton(new EditMode(this, vertexIcon, Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex");
|
||||
tool->addButton(new EditMode(this, movementIcon, Mask_Reference, "Terrain movement"), "terrain-move");
|
||||
}
|
||||
|
||||
|
867
apps/opencs/view/render/terrainvertexpaintmode.cpp
Normal file
867
apps/opencs/view/render/terrainvertexpaintmode.cpp
Normal file
@ -0,0 +1,867 @@
|
||||
#include "terrainvertexpaintmode.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDropEvent>
|
||||
#include <QEvent>
|
||||
#include <QIcon>
|
||||
#include <QWidget>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <apps/opencs/model/doc/document.hpp>
|
||||
#include <apps/opencs/model/prefs/category.hpp>
|
||||
#include <apps/opencs/model/prefs/setting.hpp>
|
||||
#include <apps/opencs/model/world/cellselection.hpp>
|
||||
#include <apps/opencs/model/world/columnimp.hpp>
|
||||
#include <apps/opencs/model/world/columns.hpp>
|
||||
#include <apps/opencs/model/world/data.hpp>
|
||||
#include <apps/opencs/model/world/idcollection.hpp>
|
||||
#include <apps/opencs/model/world/idtable.hpp>
|
||||
#include <apps/opencs/model/world/land.hpp>
|
||||
#include <apps/opencs/model/world/record.hpp>
|
||||
#include <apps/opencs/model/world/universalid.hpp>
|
||||
#include <apps/opencs/view/widget/brushshapes.hpp>
|
||||
#include <apps/opencs/view/widget/scenetool.hpp>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/esm3/loadland.hpp>
|
||||
|
||||
#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{ ":scenetoolbar/editing-terrain-vertex-paint" }, Mask_Terrain,
|
||||
"Terrain vertex paint editing", parent)
|
||||
, mParentNode(parentNode)
|
||||
, mVertexPaintEditToolColor(Qt::white)
|
||||
{
|
||||
}
|
||||
|
||||
void CSVRender::TerrainVertexPaintMode::activate(CSVWidget::SceneToolbar* toolbar)
|
||||
{
|
||||
if (!mTerrainSelection)
|
||||
{
|
||||
mTerrainSelection
|
||||
= std::make_shared<TerrainSelection>(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<int>(&QComboBox::currentIndexChanged), this, &TerrainVertexPaintMode::setVertexPaintEditTool);
|
||||
connect(mVertexPaintBrushScenetool->mVertexPaintBrushWindow->mColorButtonWidget,
|
||||
&CSVWidget::ColorButtonWidget::colorChanged, this, &TerrainVertexPaintMode::setVertexPaintColor);
|
||||
}
|
||||
|
||||
if (!mBrushDraw)
|
||||
mBrushDraw = std::make_unique<BrushDraw>(mParentNode);
|
||||
|
||||
EditMode::activate(toolbar);
|
||||
toolbar->addTool(mVertexPaintBrushScenetool);
|
||||
}
|
||||
|
||||
void CSVRender::TerrainVertexPaintMode::deactivate(CSVWidget::SceneToolbar* toolbar)
|
||||
{
|
||||
if (mVertexPaintBrushScenetool)
|
||||
{
|
||||
toolbar->removeTool(mVertexPaintBrushScenetool);
|
||||
}
|
||||
|
||||
if (mTerrainSelection)
|
||||
{
|
||||
mTerrainSelection.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);
|
||||
}
|
||||
}
|
||||
endVertexPaintEditing();
|
||||
}
|
||||
|
||||
void CSVRender::TerrainVertexPaintMode::primarySelectPressed(const WorldspaceHitResult& hit)
|
||||
{
|
||||
if (hit.hit && hit.tag == nullptr)
|
||||
{
|
||||
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0);
|
||||
mTerrainSelection->clearTemporarySelection();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::TerrainVertexPaintMode::secondarySelectPressed(const WorldspaceHitResult& hit)
|
||||
{
|
||||
if (hit.hit && hit.tag == nullptr)
|
||||
{
|
||||
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1);
|
||||
mTerrainSelection->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;
|
||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||
QUndoStack& undoStack = document.getUndoStack();
|
||||
undoStack.beginMacro("Set land vertex colours");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (mIsEditing)
|
||||
{
|
||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||
QUndoStack& undoStack = document.getUndoStack();
|
||||
undoStack.endMacro();
|
||||
}
|
||||
endVertexPaintEditing();
|
||||
}
|
||||
if (mDragMode == InteractionType_PrimarySelect || mDragMode == InteractionType_SecondarySelect)
|
||||
{
|
||||
mTerrainSelection->clearTemporarySelection();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::TerrainVertexPaintMode::dragAborted()
|
||||
{
|
||||
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::endVertexPaintEditing()
|
||||
{
|
||||
mIsEditing = false;
|
||||
mTerrainSelection->update();
|
||||
}
|
||||
|
||||
void CSVRender::TerrainVertexPaintMode::editVertexColourGrid(
|
||||
const std::pair<int, int>& vertexCoords, bool dragOperation)
|
||||
{
|
||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||
CSMWorld::IdTable& landTable
|
||||
= dynamic_cast<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land));
|
||||
|
||||
std::string mCellId = CSMWorld::CellCoordinates::vertexGlobalToCellId(vertexCoords);
|
||||
if (allowLandColourEditing(mCellId))
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<CSMWorld::CellCoordinates, bool> 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<float>(mBrushSize) / 2.0f, 1.0f);
|
||||
|
||||
if (mBrushShape == CSVWidget::BrushShape_Point)
|
||||
{
|
||||
CSMWorld::LandColoursColumn::DataType newTerrain
|
||||
= landTable.data(landTable.getModelIndex(mCellId, colourColumn))
|
||||
.value<CSMWorld::LandColoursColumn::DataType>();
|
||||
|
||||
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<CSMWorld::LandColoursColumn::DataType>();
|
||||
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<CSMWorld::LandColoursColumn::DataType>();
|
||||
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<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget()))
|
||||
{
|
||||
std::pair<int, int> 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<std::pair<int, int>>* 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<int, int>& vertexCoords, unsigned char selectMode)
|
||||
{
|
||||
int r = mBrushSize / 2;
|
||||
std::vector<std::pair<int, int>> 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")
|
||||
mTerrainSelection->onlySelect(selections);
|
||||
else if (selectAction == "Add to selection")
|
||||
mTerrainSelection->addSelect(selections);
|
||||
else if (selectAction == "Remove from selection")
|
||||
mTerrainSelection->removeSelect(selections);
|
||||
else if (selectAction == "Invert selection")
|
||||
mTerrainSelection->toggleSelect(selections);
|
||||
}
|
||||
|
||||
bool CSVRender::TerrainVertexPaintMode::noCell(const std::string& cellId)
|
||||
{
|
||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& 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<CSMWorld::Land>& 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<CSMWorld::Land>& 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<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land));
|
||||
CSMWorld::IdTable& ltexTable
|
||||
= dynamic_cast<CSMWorld::IdTable&>(*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<CSMWorld::LandHeightsColumn::DataType>();
|
||||
const CSMWorld::LandNormalsColumn::DataType landNormalsPointer
|
||||
= landTable.data(landTable.getModelIndex(cellId, landnormalsColumn))
|
||||
.value<CSMWorld::LandNormalsColumn::DataType>();
|
||||
CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);
|
||||
CSMWorld::LandNormalsColumn::DataType landNormalsNew(landNormalsPointer);
|
||||
|
||||
if (CSVRender::PagedWorldspaceWidget* paged
|
||||
= dynamic_cast<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget()))
|
||||
{
|
||||
if (isLandLoaded(cellLeftId))
|
||||
{
|
||||
const CSMWorld::LandHeightsColumn::DataType landLeftShapePointer
|
||||
= landTable.data(landTable.getModelIndex(cellLeftId, landshapeColumn))
|
||||
.value<CSMWorld::LandHeightsColumn::DataType>();
|
||||
|
||||
++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<CSMWorld::LandHeightsColumn::DataType>();
|
||||
|
||||
++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<CSMWorld::LandHeightsColumn::DataType>();
|
||||
|
||||
++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<CSMWorld::LandHeightsColumn::DataType>();
|
||||
|
||||
++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<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_Land));
|
||||
CSMWorld::IdTree& cellTable
|
||||
= dynamic_cast<CSMWorld::IdTree&>(*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<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<CSVRender::PagedWorldspaceWidget*>(&getWorldspaceWidget()))
|
||||
{
|
||||
CSMWorld::CellSelection selection = paged->getCellSelection();
|
||||
selection.add(CSMWorld::CellCoordinates::fromId(cellId).first);
|
||||
paged->setCellSelection(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CSVRender::PagedWorldspaceWidget* paged
|
||||
= dynamic_cast<CSVRender::PagedWorldspaceWidget*>(&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::TerrainSelection> CSVRender::TerrainVertexPaintMode::getTerrainSelection()
|
||||
{
|
||||
return mTerrainSelection;
|
||||
}
|
||||
|
||||
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<PagedWorldspaceWidget&>(getWorldspaceWidget());
|
||||
}
|
180
apps/opencs/view/render/terrainvertexpaintmode.hpp
Normal file
180
apps/opencs/view/render/terrainvertexpaintmode.hpp
Normal file
@ -0,0 +1,180 @@
|
||||
#ifndef CSV_RENDER_TERRAINVERTEXPAINTMODE_H
|
||||
#define CSV_RENDER_TERRAINVERTEXPAINTMODE_H
|
||||
|
||||
#include "editmode.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
||||
|
||||
#include <osg/Vec3d>
|
||||
|
||||
#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 vertex colour grid
|
||||
TerrainVertexPaintMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
|
||||
|
||||
void primaryOpenPressed(const WorldspaceHitResult& hit) override;
|
||||
|
||||
/// Create single command for one-click vertex paint 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 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 vertex paint edit behavior during dragging
|
||||
void drag(const QPoint& pos, int diffX, int diffY, double speedFactor) override;
|
||||
|
||||
/// End vertex paint editing command macro
|
||||
void dragCompleted(const QPoint& pos) override;
|
||||
|
||||
/// Cancel vertex paint 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<TerrainSelection> getTerrainSelection();
|
||||
|
||||
private:
|
||||
/// Reset everything in the current edit
|
||||
void endVertexPaintEditing();
|
||||
|
||||
/// Handle brush mechanics for colour editing
|
||||
void editVertexColourGrid(const std::pair<int, int>& 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<std::pair<int, int>>* selections);
|
||||
|
||||
/// Handle brush mechanics for terrain selection
|
||||
void selectTerrainShapes(const std::pair<int, int>& 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 vertex coloir 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<BrushDraw> mBrushDraw;
|
||||
CSVWidget::SceneToolVertexPaintBrush* mVertexPaintBrushScenetool = nullptr;
|
||||
int mDragMode = InteractionType_None;
|
||||
osg::Group* mParentNode;
|
||||
bool mIsEditing = false;
|
||||
std::shared_ptr<TerrainSelection> mTerrainSelection;
|
||||
int mTotalDiffY = 0;
|
||||
std::vector<CSMWorld::CellCoordinates> 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
|
240
apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp
Normal file
240
apps/opencs/view/widget/scenetoolvertexpaintbrush.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
#include "scenetoolvertexpaintbrush.hpp"
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QComboBox>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QFrame>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QSizePolicy>
|
||||
#include <QSlider>
|
||||
#include <QTableWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include <apps/opencs/model/prefs/category.hpp>
|
||||
#include <apps/opencs/model/prefs/setting.hpp>
|
||||
#include <apps/opencs/view/widget/pushbutton.hpp>
|
||||
|
||||
#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<int>(&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 <p>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;
|
||||
|
||||
case BrushShape_Custom:
|
||||
|
||||
setIcon(QIcon(QPixmap(":scenetoolbar/brush-custom")));
|
||||
tooltip += mVertexPaintBrushWindow->toolTipCustom;
|
||||
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();
|
||||
}
|
165
apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp
Normal file
165
apps/opencs/view/widget/scenetoolvertexpaintbrush.hpp
Normal file
@ -0,0 +1,165 @@
|
||||
#ifndef CSV_WIDGET_SCENETOOLVERTEXPAINTBRUSH_H
|
||||
#define CSV_WIDGET_SCENETOOLVERTEXPAINTBRUSH_H
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QFrame>
|
||||
#include <QGroupBox>
|
||||
#include <QSlider>
|
||||
#include <QSpinBox>
|
||||
|
||||
#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";
|
||||
const QString toolTipCustom = "Paint with custom 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
|
Loading…
x
Reference in New Issue
Block a user