diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f07b518a9..d1ebcde42 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -81,14 +81,14 @@ opencs_units_noqt (view/world opencs_units (view/widget scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton - scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit + scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit ) opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater + cellwater terraintexturemode ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 8a9dad7f3..6f64da72e 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -222,7 +222,15 @@ void CSMPrefs::State::declare() EnumValues insertOutsideVisibleCell; insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway); - declareCategory ("Scene Drops"); + EnumValue createAndLandEdit ("Create cell and land, then edit"); + EnumValue showAndLandEdit ("Show cell and edit"); + EnumValue dontLandEdit ("Discard"); + EnumValues landeditOutsideCell; + landeditOutsideCell.add (createAndLandEdit).add (dontLandEdit); + EnumValues landeditOutsideVisibleCell; + landeditOutsideVisibleCell.add (showAndLandEdit).add (dontLandEdit); + + declareCategory ("3D Scene Editing"); declareInt ("distance", "Drop Distance", 50). setTooltip ("If an instance drop can not be placed against another object at the " "insert point, it will be placed by this distance from the insert point instead"); @@ -230,6 +238,12 @@ void CSMPrefs::State::declare() addValues (insertOutsideCell); declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). addValues (insertOutsideVisibleCell); + declareEnum ("outside-landedit", "Handling land edit outside of cells", createAndLandEdit). + addValues (landeditOutsideCell); + declareEnum ("outside-visible-landedit", "Handling land edit outside of visible cells", showAndLandEdit). + addValues (landeditOutsideVisibleCell); + declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). + setMin (1); declareCategory ("Key Bindings"); diff --git a/apps/opencs/model/world/landtexturetableproxymodel.cpp b/apps/opencs/model/world/landtexturetableproxymodel.cpp index cf33fab9e..e064bbe8a 100644 --- a/apps/opencs/model/world/landtexturetableproxymodel.cpp +++ b/apps/opencs/model/world/landtexturetableproxymodel.cpp @@ -11,11 +11,6 @@ namespace CSMWorld bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const { - int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification); - QModelIndex index = mSourceModel->index(sourceRow, columnIndex); - if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly) - return false; - return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent); } } diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index ee3e18d71..4b14e29bf 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -551,7 +551,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) if (noCell) { - std::string mode = CSMPrefs::get()["Scene Drops"]["outside-drop"].toString(); + std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-drop"].toString(); // target cell does not exist if (mode=="Discard") @@ -585,7 +585,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) { // target cell exists, but is not shown std::string mode = - CSMPrefs::get()["Scene Drops"]["outside-visible-drop"].toString(); + CSMPrefs::get()["3D Scene Editing"]["outside-visible-drop"].toString(); if (mode=="Discard") return; diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 4a745195b..1d1a7cd17 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -21,6 +21,7 @@ #include "mask.hpp" #include "cameracontroller.hpp" #include "cellarrow.hpp" +#include "terraintexturemode.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { @@ -136,7 +137,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), "terrain-shape"); tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain texture editing"), + new TerrainTextureMode (this, tool), "terrain-texture"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp new file mode 100644 index 000000000..376258c5e --- /dev/null +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -0,0 +1,542 @@ +#include "terraintexturemode.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "../widget/modebutton.hpp" +#include "../widget/scenetoolbar.hpp" +#include "../widget/scenetooltexturebrush.hpp" + +#include "../../model/doc/document.hpp" +#include "../../model/prefs/state.hpp" +#include "../../model/world/columnbase.hpp" +#include "../../model/world/commandmacro.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/idtree.hpp" +#include "../../model/world/land.hpp" +#include "../../model/world/landtexture.hpp" +#include "../../model/world/resourcetable.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/universalid.hpp" + +#include "editmode.hpp" +#include "pagedworldspacewidget.hpp" +#include "mask.hpp" +#include "object.hpp" // Something small needed regarding pointers from here () +#include "worldspacewidget.hpp" + +CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), + mBrushTexture("L0#0"), + mBrushSize(0), + mBrushShape(0), + mTextureBrushScenetool(0) +{ +} + +void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar) +{ + if(!mTextureBrushScenetool) + { + mTextureBrushScenetool = new CSVWidget::SceneToolTextureBrush (toolbar, "scenetooltexturebrush", getWorldspaceWidget().getDocument()); + connect(mTextureBrushScenetool, SIGNAL (clicked()), mTextureBrushScenetool, SLOT (activate())); + connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int))); + connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setBrushShape(int))); + connect(mTextureBrushScenetool->mTextureBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int))); + connect(mTextureBrushScenetool, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string))); + connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string))); + + connect(mTextureBrushScenetool, SIGNAL(passEvent(QDropEvent*)), this, SLOT(handleDropEvent(QDropEvent*))); + connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool->mTextureBrushWindow, SLOT(setBrushTexture(std::string))); + connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string))); + } + + EditMode::activate(toolbar); + toolbar->addTool (mTextureBrushScenetool); +} + +void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar) +{ + if(mTextureBrushScenetool) + { + toolbar->removeTool (mTextureBrushScenetool); + delete mTextureBrushScenetool; + mTextureBrushScenetool = 0; + } + EditMode::deactivate(toolbar); +} + +void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult& hit) // Apply changes here +{ + 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)); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + + QUndoStack& undoStack = document.getUndoStack(); + CSMWorld::IdCollection& landtexturesCollection = document.getData().getLandTextures(); + int index = landtexturesCollection.searchId(mBrushTexture); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) + { + undoStack.beginMacro ("Edit texture records"); + if(allowLandTextureEditing(mCellId)==true) + { + undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); + editTerrainTextureGrid(hit); + } + undoStack.endMacro(); + } +} + +void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit) +{ +} + +void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit) +{ +} + +bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + + 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)); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::IdCollection& landtexturesCollection = document.getData().getLandTextures(); + int index = landtexturesCollection.searchId(mBrushTexture); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) + { + undoStack.beginMacro ("Edit texture records"); + if(allowLandTextureEditing(mCellId)==true && hit.hit == true) + { + undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); + editTerrainTextureGrid(hit); + } + } + + return true; +} + +bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos) +{ + return false; +} + +bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos) +{ + return false; +} + +bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos) +{ + return false; +} + +void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + + CSMWorld::IdCollection& landtexturesCollection = document.getData().getLandTextures(); + int index = landtexturesCollection.searchId(mBrushTexture); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) + { + editTerrainTextureGrid(hit); + } +} + +void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) { + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::IdCollection& landtexturesCollection = document.getData().getLandTextures(); + int index = landtexturesCollection.searchId(mBrushTexture); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) + { + undoStack.endMacro(); + } +} + +void CSVRender::TerrainTextureMode::dragAborted() { +} + +void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {} + +void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) { + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + + if (mime->holdsType (CSMWorld::UniversalId::Type_LandTexture)) + { + const std::vector ids = mime->getData(); + + for (const CSMWorld::UniversalId& uid : ids) + { + mBrushTexture = uid.getId(); + emit passBrushTexture(mBrushTexture); + } + } + if (mime->holdsType (CSMWorld::UniversalId::Type_Texture)) + { + const std::vector ids = mime->getData(); + + for (const CSMWorld::UniversalId& uid : ids) + { + std::string textureFileName = uid.toString(); + createTexture(textureFileName); + emit passBrushTexture(mBrushTexture); + } + } +} + +void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitResult& hit) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + if(allowLandTextureEditing(mCellId)==true) {} + + std::pair cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId); + + int cellX = cellCoordinates_pair.first.getX(); + int cellY = cellCoordinates_pair.first.getY(); + + // The coordinates of hit in mCellId + int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5)); + int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5)); + if (xHitInCell < 0) + { + xHitInCell = xHitInCell + landTextureSize; + cellX = cellX - 1; + } + if (yHitInCell > 15) + { + yHitInCell = yHitInCell - landTextureSize; + cellY = cellY + 1; + } + + mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY); + if(allowLandTextureEditing(mCellId)==true) {} + + std::string iteratedCellId; + + int textureColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandTexturesIndex); + + std::size_t hashlocation = mBrushTexture.find("#"); + std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1); + int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1 + + float rf = mBrushSize/2; + int r = (mBrushSize/2)+1; + float distance = 0; + + if (mBrushShape == 0) + { + CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value(); + CSMWorld::LandTexturesColumn::DataType mNew(mPointer); + + if(allowLandTextureEditing(mCellId)==true) + { + mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt; + pushEditToCommand(mNew, document, landTable, mCellId); + } + } + + if (mBrushShape == 1) + { + int upperLeftCellX = cellX - std::floor(r / landTextureSize); + int upperLeftCellY = cellY - std::floor(r / landTextureSize); + if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--; + if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--; + + int lowerrightCellX = cellX + std::floor(r / landTextureSize); + int lowerrightCellY = cellY + std::floor(r / landTextureSize); + if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++; + if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++; + + for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++) + { + for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) + { + iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); + if(allowLandTextureEditing(iteratedCellId)==true) + { + CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value(); + CSMWorld::LandTexturesColumn::DataType mNew(mPointer); + for(int i = 0; i < landTextureSize; i++) + { + for(int j = 0; j < landTextureSize; j++) + { + + if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) + { + mNew[j*landTextureSize+i] = brushInt; + } + else + { + int distanceX(0); + int distanceY(0); + if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i; + if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j; + if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i; + if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * 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) mNew[j*landTextureSize+i] = brushInt; + } + } + } + pushEditToCommand(mNew, document, landTable, iteratedCellId); + } + } + } + } + + if (mBrushShape == 2) + { + int upperLeftCellX = cellX - std::floor(r / landTextureSize); + int upperLeftCellY = cellY - std::floor(r / landTextureSize); + if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--; + if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--; + + int lowerrightCellX = cellX + std::floor(r / landTextureSize); + int lowerrightCellY = cellY + std::floor(r / landTextureSize); + if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++; + if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++; + + for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++) + { + for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) + { + iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); + if(allowLandTextureEditing(iteratedCellId)==true) + { + CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value(); + CSMWorld::LandTexturesColumn::DataType mNew(mPointer); + for(int i = 0; i < landTextureSize; i++) + { + for(int j = 0; j < landTextureSize; j++) + { + + if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) + { + int distanceX(0); + int distanceY(0); + if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i; + if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j; + if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize* abs(i_cell-cellX) + i; + if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; + if (i_cell == cellX) distanceX = abs(i-xHitInCell); + if (j_cell == cellY) distanceY = abs(j-yHitInCell); + distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + if (distance < rf) mNew[j*landTextureSize+i] = brushInt; + } + else + { + int distanceX(0); + int distanceY(0); + if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i; + if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j; + if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i; + if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; + if (i_cell == cellX) distanceX = abs(i-xHitInCell); + if (j_cell == cellY) distanceY = abs(j-yHitInCell); + distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); + if (distance < rf) mNew[j*landTextureSize+i] = brushInt; + } + } + } + pushEditToCommand(mNew, document, landTable, iteratedCellId); + } + } + } + } + + if (mBrushShape == 3) + { + // Not implemented + } + +} + +void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, std::string cellId) +{ + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + QVariant changedLand; + changedLand.setValue(newLandGrid); + + QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex))); + + QUndoStack& undoStack = document.getUndoStack(); + undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); + undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); +} + +void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + QUndoStack& undoStack = document.getUndoStack(); + + std::string newId; + + int counter=0; + bool freeIndexFound = false; + do { + const size_t maxCounter = std::numeric_limits::max() - 1; + try + { + newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); + if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter; + } catch (const std::exception& e) + { + newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); + freeIndexFound = true; + } + } while (freeIndexFound == false); + + std::size_t idlocation = textureFileName.find("Texture: "); + textureFileName = textureFileName.substr (idlocation + 9); + + QVariant textureNameVariant; + + QVariant textureFileNameVariant; + textureFileNameVariant.setValue(QString::fromStdString(textureFileName)); + + undoStack.beginMacro ("Add land texture record"); + + undoStack.push (new CSMWorld::CreateCommand (ltexTable, newId)); + QModelIndex index(ltexTable.getModelIndex (newId, ltexTable.findColumnIndex (CSMWorld::Columns::ColumnId_Texture))); + undoStack.push (new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant)); + undoStack.endMacro(); + mBrushTexture = newId; +} + +bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId) +{ + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& landTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTree& cellTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + bool noCell = document.getData().getCells().searchId (cellId)==-1; + bool noLand = document.getData().getLand().searchId (cellId)==-1; + + if (noCell) + { + std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); + + // target cell does not exist + if (mode=="Discard") + return false; + + if (mode=="Create cell and land, then edit") + { + std::unique_ptr createCommand ( + new CSMWorld::CreateCommand (cellTable, cellId)); + int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); + int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior); + createCommand->addNestedValue (parentIndex, index, false); + document.getUndoStack().push (createCommand.release()); + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + } + } + else if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first)) + { + // target cell exists, but is not shown + std::string mode = + CSMPrefs::get()["3D Scene Editing"]["outside-visible-landedit"].toString(); + + if (mode=="Discard") + return false; + + if (mode=="Show cell and edit") + { + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + } + } + + if (noLand) + { + std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString(); + + // target cell does not exist + if (mode=="Discard") + return false; + + if (mode=="Create cell and land, then edit") + { + document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId)); + } + } + + return true; +} + +void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { +} + +void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) +{ + mBrushSize = brushSize; +} + +void CSVRender::TerrainTextureMode::setBrushShape(int brushShape) +{ + mBrushShape = brushShape; +} + +void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture) +{ + mBrushTexture = brushTexture; +} diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp new file mode 100644 index 000000000..e1538e243 --- /dev/null +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -0,0 +1,100 @@ +#ifndef CSV_RENDER_TERRAINTEXTUREMODE_H +#define CSV_RENDER_TERRAINTEXTUREMODE_H + +#include "editmode.hpp" + +#include + +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/land.hpp" + +#include "../../model/doc/document.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/landtexture.hpp" + +namespace CSVWidget +{ + class SceneToolTextureBrush; +} + +namespace CSVRender +{ + + class TerrainTextureMode : public EditMode + { + Q_OBJECT + + public: + + /// \brief Editmode for terrain texture grid + TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr); + + /// \brief Create single command for one-click texture editing + void primaryEditPressed (const WorldspaceHitResult& hit); + + /// \brief Open brush settings window + void primarySelectPressed(const WorldspaceHitResult&); + + void secondarySelectPressed(const WorldspaceHitResult&); + + void activate(CSVWidget::SceneToolbar*); + void deactivate(CSVWidget::SceneToolbar*); + + /// \brief Start texture editing command macro + virtual bool primaryEditStartDrag (const QPoint& pos); + + virtual bool secondaryEditStartDrag (const QPoint& pos); + virtual bool primarySelectStartDrag (const QPoint& pos); + virtual bool secondarySelectStartDrag (const QPoint& pos); + + /// \brief Handle texture edit behavior during dragging + virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); + + /// \brief End texture editing command macro + virtual void dragCompleted(const QPoint& pos); + + virtual void dragAborted(); + virtual void dragWheel (int diff, double speedFactor); + virtual void dragMoveEvent (QDragMoveEvent *event); + + /// \brief Handle brush mechanics, maths regarding worldspace hit etc. + void editTerrainTextureGrid (const WorldspaceHitResult& hit); + + /// \brief Push texture edits to command macro + void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, + CSMWorld::IdTable& landTable, std::string cellId); + + /// \brief Create new land texture record from texture asset + void createTexture(std::string textureFileName); + + /// \brief Create new cell and land if needed + bool allowLandTextureEditing(std::string textureFileName); + + private: + std::string mCellId; + std::string mBrushTexture; + int mBrushSize; + int mBrushShape; + CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; + + const int cellSize {ESM::Land::REAL_SIZE}; + const int landSize {ESM::Land::LAND_SIZE}; + const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; + + signals: + void passBrushTexture(std::string brushTexture); + + public slots: + void handleDropEvent(QDropEvent *event); + void setBrushSize(int brushSize); + void setBrushShape(int brushShape); + void setBrushTexture(std::string brushShape); + }; +} + + +#endif diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index af53c86f0..084fb87e6 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -445,7 +445,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo // Default placement direction.normalize(); - direction *= CSMPrefs::get()["Scene Drops"]["distance"].toInt(); + direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt(); WorldspaceHitResult hit = { false, 0, 0, 0, 0, start + direction }; return hit; @@ -648,6 +648,12 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) mDragX = event->posF().x(); mDragY = height() - event->posF().y(); #endif + + if (mDragMode == InteractionType_PrimaryEdit) + { + EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); + editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos + } } } else diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp new file mode 100644 index 000000000..d6df4e9f9 --- /dev/null +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -0,0 +1,362 @@ +#include "scenetooltexturebrush.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scenetool.hpp" + +#include "../../model/doc/document.hpp" +#include "../../model/prefs/state.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idcollection.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/landtexture.hpp" +#include "../../model/world/universalid.hpp" + + +CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent) + : QGroupBox(title, parent) +{ + mBrushSizeSlider = new QSlider(Qt::Horizontal); + mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); + mBrushSizeSlider->setTickInterval(10); + mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); + mBrushSizeSlider->setSingleStep(1); + + mBrushSizeSpinBox = new QSpinBox; + mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); + mBrushSizeSpinBox->setSingleStep(1); + + mLayoutSliderSize = new QHBoxLayout; + mLayoutSliderSize->addWidget(mBrushSizeSlider); + mLayoutSliderSize->addWidget(mBrushSizeSpinBox); + + connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int))); + connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int))); + + setLayout(mLayoutSliderSize); +} + +CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent) + : QFrame(parent, Qt::Popup), + mBrushShape(0), + mBrushSize(0), + mBrushTexture("L0#0"), + mDocument(document) +{ + mBrushTextureLabel = "Selected texture: " + mBrushTexture + " "; + + CSMWorld::IdCollection& landtexturesCollection = mDocument.getData().getLandTextures(); + + int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture); + int index = landtexturesCollection.searchId(mBrushTexture); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) + { + mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value()); + } else + { + mBrushTextureLabel = "No selected texture or invalid texture"; + mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel)); + } + + QVBoxLayout *layoutMain = new QVBoxLayout; + layoutMain->setSpacing(0); + layoutMain->setContentsMargins(4,0,4,4); + + QHBoxLayout *layoutHorizontal = new QHBoxLayout; + layoutHorizontal->setSpacing(0); + layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0)); + + configureButtonInitialSettings(mButtonPoint); + configureButtonInitialSettings(mButtonSquare); + configureButtonInitialSettings(mButtonCircle); + configureButtonInitialSettings(mButtonCustom); + + mButtonPoint->setToolTip (toolTipPoint); + mButtonSquare->setToolTip (toolTipSquare); + mButtonCircle->setToolTip (toolTipCircle); + mButtonCustom->setToolTip (toolTipCustom); + + QButtonGroup* brushButtonGroup = new QButtonGroup(this); + brushButtonGroup->addButton(mButtonPoint); + brushButtonGroup->addButton(mButtonSquare); + brushButtonGroup->addButton(mButtonCircle); + brushButtonGroup->addButton(mButtonCustom); + + brushButtonGroup->setExclusive(true); + + layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop); + layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop); + + mHorizontalGroupBox = new QGroupBox(tr("")); + mHorizontalGroupBox->setLayout(layoutHorizontal); + + layoutMain->addWidget(mHorizontalGroupBox); + layoutMain->addWidget(mSizeSliders); + layoutMain->addWidget(mSelectedBrush); + + setLayout(layoutMain); + + connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape())); + connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape())); + connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape())); + connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape())); +} + +void CSVWidget::TextureBrushWindow::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::TextureBrushWindow::setBrushTexture(std::string brushTexture) +{ + mBrushTexture = brushTexture; + + CSMWorld::IdCollection& landtexturesCollection = mDocument.getData().getLandTextures(); + + int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture); + int columnModification = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Modification); + int index = landtexturesCollection.searchId(mBrushTexture); + + // Check if texture exists in current plugin + if(landtexturesCollection.getData(index, columnModification).value() == 0) + { + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + QUndoStack& undoStack = mDocument.getUndoStack(); + + QVariant textureFileNameVariant; + textureFileNameVariant.setValue(landtexturesCollection.getData(index, landTextureFilename).value()); + + std::size_t hashlocation = mBrushTexture.find("#"); + std::string mBrushTexturePlugin = "L0#" + mBrushTexture.substr (hashlocation+1); + int indexPlugin = landtexturesCollection.searchId(mBrushTexturePlugin); + + // Reindex texture if needed + if (indexPlugin != -1 && !landtexturesCollection.getRecord(indexPlugin).isDeleted()) + { + int counter=0; + bool freeIndexFound = false; + do { + const size_t maxCounter = std::numeric_limits::max() - 1; + mBrushTexturePlugin = CSMWorld::LandTexture::createUniqueRecordId(0, counter); + if (landtexturesCollection.searchId(mBrushTexturePlugin) != -1 && landtexturesCollection.getRecord(mBrushTexturePlugin).isDeleted() == 0) counter = (counter + 1) % maxCounter; + else freeIndexFound = true; + } while (freeIndexFound == false); + } + + undoStack.beginMacro ("Add land texture record"); + undoStack.push (new CSMWorld::CloneCommand (ltexTable, mBrushTexture, mBrushTexturePlugin, CSMWorld::UniversalId::Type_LandTexture)); + undoStack.endMacro(); + mBrushTexture = mBrushTexturePlugin; + emit passTextureId(mBrushTexture); + } + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) + { + mBrushTextureLabel = "Selected texture: " + mBrushTexture + " "; + mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value()); + } else + { + mBrushTextureLabel = "No selected texture or invalid texture"; + mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel)); + } + + emit passBrushShape(mBrushShape); // update icon +} + +void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize) +{ + mBrushSize = brushSize; + emit passBrushSize(mBrushSize); +} + +void CSVWidget::TextureBrushWindow::setBrushShape() +{ + if(mButtonPoint->isChecked()) mBrushShape = 0; + if(mButtonSquare->isChecked()) mBrushShape = 1; + if(mButtonCircle->isChecked()) mBrushShape = 2; + if(mButtonCustom->isChecked()) mBrushShape = 3; + emit passBrushShape(mBrushShape); +} + +void CSVWidget::SceneToolTextureBrush::adjustToolTips() +{ +} + +CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document) +: SceneTool (parent, Type_TopAction), + mToolTip (toolTip), + mDocument (document), + mTextureBrushWindow(new TextureBrushWindow(document, this)) +{ + mBrushHistory.resize(1); + mBrushHistory[0] = "L0#0"; + + setAcceptDrops(true); + connect(mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int))); + setButtonIcon(mTextureBrushWindow->mBrushShape); + + mPanel = new QFrame (this, Qt::Popup); + + QHBoxLayout *layout = new QHBoxLayout (mPanel); + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + mTable = new QTableWidget (0, 2, this); + + mTable->setShowGrid (true); + mTable->verticalHeader()->hide(); + mTable->horizontalHeader()->hide(); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); + mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); +#else + mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); + mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); +#endif + mTable->setSelectionMode (QAbstractItemView::NoSelection); + + layout->addWidget (mTable); + + connect (mTable, SIGNAL (clicked (const QModelIndex&)), + this, SLOT (clicked (const QModelIndex&))); + +} + +void CSVWidget::SceneToolTextureBrush::setButtonIcon (int brushShape) +{ + QString tooltip = "Brush settings

Currently selected: "; + if(brushShape == 0) + { + setIcon (QIcon (QPixmap (":scenetoolbar/brush-point"))); + tooltip += dynamic_cast (mTextureBrushWindow->toolTipPoint); + } + if(brushShape == 1) + { + setIcon (QIcon (QPixmap (":scenetoolbar/brush-square"))); + tooltip += dynamic_cast (mTextureBrushWindow->toolTipSquare); + } + if(brushShape == 2) + { + setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle"))); + tooltip += dynamic_cast (mTextureBrushWindow->toolTipCircle); + } + if(brushShape == 3) + { + setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom"))); + tooltip += dynamic_cast (mTextureBrushWindow->toolTipCustom); + } + + CSMWorld::IdCollection& landtexturesCollection = mDocument.getData().getLandTextures(); + + int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture); + int index = landtexturesCollection.searchId(mTextureBrushWindow->mBrushTexture); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) + { + tooltip += "

Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + " "; + + tooltip += landtexturesCollection.getData(index, landTextureFilename).value(); + } else + { + tooltip += "

No selected texture or invalid texture"; + } + + tooltip += "
(drop texture here to change)"; + setToolTip (tooltip); +} + +void CSVWidget::SceneToolTextureBrush::showPanel (const QPoint& position) +{ + updatePanel(); + mPanel->move (position); + mPanel->show(); +} + +void CSVWidget::SceneToolTextureBrush::updatePanel() +{ + mTable->setRowCount (mBrushHistory.size()); + + for (int i = mBrushHistory.size()-1; i >= 0; --i) + { + CSMWorld::IdCollection& landtexturesCollection = mDocument.getData().getLandTextures(); + int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture); + int index = landtexturesCollection.searchId(mBrushHistory[i]); + + if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) + { + mTable->setItem (i, 1, new QTableWidgetItem (landtexturesCollection.getData(index, landTextureFilename).value())); + mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i]))); + } else + { + mTable->setItem (i, 1, new QTableWidgetItem ("Invalid/deleted texture")); + mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i]))); + } + } +} + +void CSVWidget::SceneToolTextureBrush::updateBrushHistory (const std::string& brushTexture) +{ + mBrushHistory.insert(mBrushHistory.begin(), brushTexture); + if(mBrushHistory.size() > 5) mBrushHistory.pop_back(); +} + +void CSVWidget::SceneToolTextureBrush::clicked (const QModelIndex& index) +{ + if (index.column()==0 || index.column()==1) + { + std::string brushTexture = mBrushHistory[index.row()]; + std::swap(mBrushHistory[index.row()], mBrushHistory[0]); + mTextureBrushWindow->setBrushTexture(brushTexture); + emit passTextureId(brushTexture); + updatePanel(); + mPanel->hide(); + } +} + +void CSVWidget::SceneToolTextureBrush::activate () +{ + QPoint position = QCursor::pos(); + mTextureBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); + mTextureBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); + mTextureBrushWindow->move (position); + mTextureBrushWindow->show(); +} + +void CSVWidget::SceneToolTextureBrush::dragEnterEvent (QDragEnterEvent *event) +{ + emit passEvent(event); + event->accept(); +} +void CSVWidget::SceneToolTextureBrush::dropEvent (QDropEvent *event) +{ + emit passEvent(event); + event->accept(); +} diff --git a/apps/opencs/view/widget/scenetooltexturebrush.hpp b/apps/opencs/view/widget/scenetooltexturebrush.hpp new file mode 100644 index 000000000..d40132d42 --- /dev/null +++ b/apps/opencs/view/widget/scenetooltexturebrush.hpp @@ -0,0 +1,117 @@ +#ifndef CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H +#define CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scenetool.hpp" + +#include "../../model/doc/document.hpp" + +class QTableWidget; + +namespace CSVWidget +{ + /// \brief Layout-box for some brush button settings + class BrushSizeControls : public QGroupBox + { + Q_OBJECT + + public: + BrushSizeControls(const QString &title, QWidget *parent); + QSlider *mBrushSizeSlider; + QSpinBox *mBrushSizeSpinBox; + + private: + QHBoxLayout *mLayoutSliderSize; + }; + + /// \brief Brush settings window + class TextureBrushWindow : public QFrame + { + Q_OBJECT + + public: + TextureBrushWindow(CSMDoc::Document& document, QWidget *parent = 0); + void configureButtonInitialSettings(QPushButton *button); + + QPushButton *mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this); + QPushButton *mButtonSquare = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-square")), "", this); + QPushButton *mButtonCircle = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-circle")), "", this); + QPushButton *mButtonCustom = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-custom")), "", this); + QString toolTipPoint = "Paint single point"; + QString toolTipSquare = "Paint with square brush"; + QString toolTipCircle = "Paint with circle brush"; + QString toolTipCustom = "Paint custom selection (not implemented yet)"; + BrushSizeControls* mSizeSliders = new BrushSizeControls("Brush size", this); + int mBrushShape; + int mBrushSize; + std::string mBrushTexture; + + private: + CSMDoc::Document& mDocument; + QLabel *mSelectedBrush; + QGroupBox *mHorizontalGroupBox; + std::string mBrushTextureLabel; + + public slots: + void setBrushTexture(std::string brushTexture); + void setBrushShape(); + void setBrushSize(int brushSize); + + signals: + void passBrushSize (int brushSize); + void passBrushShape(int brushShape); + void passTextureId(std::string brushTexture); + }; + + class SceneToolTextureBrush : public SceneTool + { + Q_OBJECT + + QString mToolTip; + CSMDoc::Document& mDocument; + QFrame *mPanel; + QTableWidget *mTable; + std::vector mBrushHistory; + + private: + + void adjustToolTips(); + + public: + + SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document); + + virtual void showPanel (const QPoint& position); + void updatePanel (); + + TextureBrushWindow *mTextureBrushWindow; + + void dropEvent (QDropEvent *event); + void dragEnterEvent (QDragEnterEvent *event); + + public slots: + void setButtonIcon(int brushShape); + void updateBrushHistory (const std::string& mBrushTexture); + void clicked (const QModelIndex& index); + virtual void activate(); + + signals: + void passEvent(QDropEvent *event); + void passEvent(QDragEnterEvent *event); + void passTextureId(std::string brushTexture); + }; +} + +#endif diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index bfa256faf..0afd855f9 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -68,7 +68,7 @@ record-preview.png record-clone.png record-add.png - resources-icon.png + resources-icon.png resources-mesh.png resources-music.png resources-sound.png @@ -149,6 +149,10 @@ transform-scale.png selection-mode-cube.png selection-mode-cube-corner.png - selection-mode-cube-sphere.png + selection-mode-cube-sphere.png + brush-point.png + brush-square.png + brush-circle.png + brush-custom.png