Terrain texture selection, support for vertex selection

pull/2517/head
Nelsson Huotari 5 years ago
parent 2e05e0e829
commit d6722c7492

@ -143,6 +143,7 @@
Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file
Feature #3610: Option to invert X axis
Feature #3871: Editor: Terrain Selection
Feature #3893: Implicit target for "set" function in console
Feature #3980: In-game option to disable controller
Feature #3999: Shift + Double Click should maximize/restore menu size

@ -89,7 +89,7 @@ opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode actor
cellwater terraintexturemode actor terrainselection
)
opencs_units_noqt (view/render

@ -140,7 +140,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"),
"terrain-shape");
tool->addButton (
new TerrainTextureMode (this, tool),
new TerrainTextureMode (this, mRootNode, tool),
"terrain-texture");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),

@ -0,0 +1,271 @@
#include "terrainselection.hpp"
#include <algorithm>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
#include "../../model/world/columnimp.hpp"
#include "../../model/world/idtable.hpp"
#include "cell.hpp"
#include "worldspacewidget.hpp"
namespace
{
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
}
CSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type):
mParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mDraggedOperationFlag(false), mSelectionType(type)
{
mGeometry = new osg::Geometry();
mSelectionNode = new osg::Group();
mSelectionNode->addChild(mGeometry);
activate();
}
CSVRender::TerrainSelection::~TerrainSelection()
{
deactivate();
}
std::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelection() const
{
return mSelection;
}
void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> localPositions)
{
mSelection.clear();
for(auto const& value: localPositions)
{
mSelection.emplace_back(value);
}
update();
}
void CSVRender::TerrainSelection::addSelect(const std::pair<int, int> localPos)
{
if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end())
mSelection.emplace_back(localPos);
update();
}
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>> localPositions, bool toggleInProgress)
{
if (toggleInProgress == true)
{
for(auto const& localPos: localPositions)
{
auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
auto itertemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
mDraggedOperationFlag = true;
if (itertemp == mTemporarySelection.end())
{
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
else
{
mSelection.emplace_back(localPos);
}
}
mTemporarySelection.push_back(localPos);
}
}
else if (mDraggedOperationFlag == false)
{
for(auto const& localPos: localPositions)
{
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
else
{
mSelection.emplace_back(localPos);
}
}
}
else
{
mDraggedOperationFlag = false;
mTemporarySelection.clear();
}
update();
}
void CSVRender::TerrainSelection::activate()
{
mParentNode->addChild(mSelectionNode);
}
void CSVRender::TerrainSelection::deactivate()
{
mParentNode->removeChild(mSelectionNode);
}
void CSVRender::TerrainSelection::update()
{
mSelectionNode->removeChild(mGeometry);
mGeometry = new osg::Geometry();
const osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array);
switch (mSelectionType)
{
case TerrainSelectionType::Texture : drawTextureSelection(vertices);
break;
case TerrainSelectionType::Shape : drawShapeSelection(vertices);
break;
}
mGeometry->setVertexArray(vertices);
osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES);
drawArrays->setCount(vertices->size());
mGeometry->addPrimitiveSet(drawArrays);
mSelectionNode->addChild(mGeometry);
}
void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
for (std::pair<int, int> localPos : mSelection)
{
int x (localPos.first);
int y (localPos.second);
float xWorldCoord(CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(x));
float yWorldCoord(CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(y));
osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2);
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2));
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2));
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y) + 2));
}
}
}
}
void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
// Nudge selection by 1/4th of a texture size, similar how blendmaps are nudged
const float nudgePercentage = 0.25f;
const int nudgeOffset = (cellSize / landTextureSize) * nudgePercentage;
const int landHeightsNudge = (cellSize / landSize) / (landSize - 1); // Does this work with all land size configurations?
const int textureSizeToLandSizeModifier = (landSize - 1) / landTextureSize;
for (std::pair<int, int> localPos : mSelection)
{
int x (localPos.first);
int y (localPos.second);
// convert texture selection to global vertex coordinates at selection box corners
int x1 = x * textureSizeToLandSizeModifier + landHeightsNudge;
int x2 = x * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier + landHeightsNudge;
int y1 = y * textureSizeToLandSizeModifier - landHeightsNudge;
int y2 = y * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier - landHeightsNudge;
// Draw edges (check all sides, draw lines between vertices, +1 height to keep lines above ground)
// Check adjancent selections, draw lines only to edges of the selection
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x)+(i-1)*(cellSize / (landSize - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x)+i*(cellSize / (landSize - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+(i-1), y2)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+i, y2)+2));
}
}
const auto south = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y - 1));
if (south == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x)+(i-1)*(cellSize / (landSize - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x)+i*(cellSize / (landSize - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+(i-1), y1)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+i, y1)+2));
}
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y)+(i-1)*(cellSize / (landSize - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y)+i*(cellSize / (landSize - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x + 1) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x2, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x + 1) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x2, y1+i)+2));
}
}
const auto west = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x - 1, y));
if (west == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y)+(i-1)*(cellSize / (landSize - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureSelectionToWorldCoords(y)+i*(cellSize / (landSize - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x1, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureSelectionToWorldCoords(x) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x1, y1+i)+2));
}
}
}
}
}
int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates
{
int cellX = std::floor((1.0f*x / (landSize - 1)));
int cellY = std::floor((1.0f*y / (landSize - 1)));
int localX = x - cellX * (landSize - 1);
int localY = y - cellY * (landSize - 1);
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
return mPointer[localY*landSize + localX];
}

@ -0,0 +1,75 @@
#ifndef CSV_RENDER_TERRAINSELECTION_H
#define CSV_RENDER_TERRAINSELECTION_H
#include <utility>
#include <vector>
#include <osg/Vec3d>
#include <osg/ref_ptr>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
namespace osg
{
class Group;
}
namespace CSVRender
{
struct WorldspaceHitResult;
class WorldspaceWidget;
enum class TerrainSelectionType
{
Texture,
Shape
};
/// \brief Class handling the terrain selection data and rendering
class TerrainSelection
{
public:
TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type);
~TerrainSelection();
void onlySelect(const std::vector<std::pair<int, int>> localPositions);
void addSelect(const std::pair<int, int> localPos);
void toggleSelect(const std::vector<std::pair<int, int>> localPositions, bool);
void activate();
void deactivate();
std::vector<std::pair<int, int>> getTerrainSelection() const;
protected:
void addToSelection(osg::Vec3d worldPos);
void toggleSelection(osg::Vec3d worldPos);
void deselect();
void update();
void drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
void drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
int calculateLandHeight(int x, int y);
private:
osg::Group* mParentNode;
WorldspaceWidget *mWorldspaceWidget;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
osg::ref_ptr<osg::Geometry> mGeometry;
osg::ref_ptr<osg::Group> mSelectionNode;
std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units
std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation
bool mDraggedOperationFlag; //true during drag operation, false when click-operation
TerrainSelectionType mSelectionType;
};
}
#endif

@ -10,6 +10,8 @@
#include <QDragEnterEvent>
#include <QDrag>
#include <osg/Group>
#include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp"
@ -34,14 +36,18 @@
#include "pagedworldspacewidget.hpp"
#include "mask.hpp"
#include "object.hpp" // Something small needed regarding pointers from here ()
#include "terrainselection.hpp"
#include "worldspacewidget.hpp"
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, 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)
mTextureBrushScenetool(0),
mDragMode(InteractionType_None),
mParentNode(parentNode),
mIsEditing(false)
{
}
@ -62,6 +68,11 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));
}
if (!mTerrainTextureSelection)
{
mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture));
}
EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool);
}
@ -74,6 +85,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
delete mTextureBrushScenetool;
mTextureBrushScenetool = 0;
}
if (mTerrainTextureSelection)
{
mTerrainTextureSelection.reset();
}
EditMode::deactivate(toolbar);
}
@ -95,7 +112,7 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{
undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true)
@ -109,10 +126,18 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit)
{
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, false);
}
}
void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit)
{
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false);
}
}
bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
@ -129,13 +154,16 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
QUndoStack& undoStack = document.getUndoStack();
mDragMode = InteractionType_PrimaryEdit;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{
undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true)
mIsEditing = true;
if(allowLandTextureEditing(mCellId)==true)
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
@ -152,47 +180,91 @@ bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)
bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
mDragMode = InteractionType_PrimarySelect;
if (!hit.hit || hit.tag != 0)
{
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
return false;
}
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
mDragMode = InteractionType_SecondarySelect;
if (!hit.hit || hit.tag != 0)
{
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
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();
if (mDragMode == InteractionType_PrimaryEdit)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos);
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{
editTerrainTextureGrid(hit);
}
}
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
if (mDragMode == InteractionType_PrimarySelect)
{
editTerrainTextureGrid(hit);
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
}
if (mDragMode == InteractionType_SecondarySelect)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
}
}
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) {
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
{
if (mDragMode == InteractionType_PrimaryEdit)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
undoStack.endMacro();
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
if (mIsEditing == true)
{
undoStack.endMacro();
mIsEditing = false;
}
}
}
}
void CSVRender::TerrainTextureMode::dragAborted() {
void CSVRender::TerrainTextureMode::dragAborted()
{
}
void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {}
void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor)
{
}
void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) {
void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event)
{
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
@ -236,8 +308,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
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));
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.25));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25));
if (xHitInCell < 0)
{
xHitInCell = xHitInCell + landTextureSize;
@ -249,7 +321,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
cellY = cellY + 1;
}
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY);
mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
if(allowLandTextureEditing(mCellId)==true) {}
std::string iteratedCellId;
@ -266,13 +338,13 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 0)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId)==true)
{
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId);
newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(newTerrain, document, landTable, mCellId);
}
}
@ -292,19 +364,19 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
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)
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{
mNew[j*landTextureSize+i] = brushInt;
newTerrain[j*landTextureSize+i] = brushInt;
}
else
{
@ -316,11 +388,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
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;
if (distanceX < r && distanceY < r) newTerrain[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
}
}
}
@ -342,11 +414,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++)
{
for(int j = 0; j < landTextureSize; j++)
@ -363,7 +435,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
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;
if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
}
else
{
@ -376,21 +448,99 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
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;
if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 3)
{
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId)==true && !mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
if(yHitInCell + value.second >= 0 && yHitInCell + value.second <= 15 && xHitInCell + value.first >= 0 && xHitInCell + value.first <= 15)
{
newTerrain[(yHitInCell+value.second)*landTextureSize+xHitInCell+value.first] = brushInt;
}
else
{
int cellXDifference = std::floor(1.0f*(xHitInCell + value.first)/landTextureSize);
int cellYDifference = std::floor(1.0f*(yHitInCell + value.second)/landTextureSize);
int xInOtherCell = xHitInCell + value.first - cellXDifference * landTextureSize;
int yInOtherCell = yHitInCell + value.second - cellYDifference * landTextureSize;
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX+cellXDifference, cellY+cellYDifference);
if (allowLandTextureEditing(cellId)==true)
{
CSMWorld::LandTexturesColumn::DataType newTerrainPointerOtherCell = landTable.data(landTable.getModelIndex(cellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrainOtherCell(newTerrainPointerOtherCell);
newTerrainOtherCell[yInOtherCell*landTextureSize+xInOtherCell] = brushInt;
pushEditToCommand(newTerrainOtherCell, document, landTable, cellId);
}
}
}
pushEditToCommand(newTerrain, document, landTable, mCellId);
}
}
}
void CSVRender::TerrainTextureMode::selectTerrainTextures(std::pair<int, int> texCoords, unsigned char selectMode, bool dragOperation)
{
int r = mBrushSize / 2;
std::vector<std::pair<int, int>> selections;
if (mBrushShape == 0)
{
selections.emplace_back(texCoords);
}
if (mBrushShape == 1)
{
for(int i = texCoords.first - r; i <= texCoords.first + r; ++i)
{
for(int j = texCoords.second - r; j <= texCoords.second + r; ++j)
{
selections.emplace_back(std::make_pair(i, j));
}
}
}
if (mBrushShape == 2)
{
for(int i = texCoords.first - r; i <= texCoords.first + r; ++i)
{
for(int j = texCoords.second - r; j <= texCoords.second + r; ++j)
{
int distanceX = abs(i - texCoords.first);
int distanceY = abs(j - texCoords.second);
int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < r) selections.emplace_back(std::make_pair(i, j));
}
}
}
if (mBrushShape == 3)
{
// Not implemented
if(!mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
selections.emplace_back(std::make_pair(texCoords.first + value.first, texCoords.second + value.second));
}
}
}
if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections);
if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);
}
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
@ -405,8 +555,8 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu
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));
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
}
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
@ -422,18 +572,21 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
int counter=0;
bool freeIndexFound = false;
do {
do
{
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
} catch (const std::exception& e)
}
catch (const std::exception& e)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true;
}
} while (freeIndexFound == false);
}
while (freeIndexFound == false);
std::size_t idlocation = textureFileName.find("Texture: ");
textureFileName = textureFileName.substr (idlocation + 9);
@ -527,7 +680,8 @@ bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
return true;
}
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) {
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event)
{
}
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
@ -538,9 +692,41 @@ void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
void CSVRender::TerrainTextureMode::setBrushShape(int brushShape)
{
mBrushShape = brushShape;
//Set custom brush shape
if (mBrushShape == 3 && !mTerrainTextureSelection->getTerrainSelection().empty())
{
auto terrainSelection = mTerrainTextureSelection->getTerrainSelection();
int selectionCenterX = 0;
int selectionCenterY = 0;
int selectionAmount = 0;
for(auto const& value: terrainSelection)
{
selectionCenterX = selectionCenterX + value.first;
selectionCenterY = selectionCenterY + value.second;
++selectionAmount;
}
selectionCenterX = selectionCenterX / selectionAmount;
selectionCenterY = selectionCenterY / selectionAmount;
mCustomBrushShape.clear();
std::pair<int, int> differentialPos {};
for(auto const& value: terrainSelection)
{
differentialPos.first = value.first - selectionCenterX;
differentialPos.second = value.second - selectionCenterY;
mCustomBrushShape.push_back(differentialPos);
}
}
}
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)
{
mBrushTexture = brushTexture;
}
CSVRender::PagedWorldspaceWidget& CSVRender::TerrainTextureMode::getPagedWorldspaceWidget()
{
return dynamic_cast<PagedWorldspaceWidget&>(getWorldspaceWidget());
}

@ -4,6 +4,7 @@
#include "editmode.hpp"
#include <string>
#include <memory>
#include <QWidget>
#include <QEvent>
@ -18,6 +19,8 @@
#include "../../model/world/landtexture.hpp"
#endif
#include "terrainselection.hpp"
namespace CSVWidget
{
class SceneToolTextureBrush;
@ -25,6 +28,7 @@ namespace CSVWidget
namespace CSVRender
{
class PagedWorldspaceWidget;
class TerrainTextureMode : public EditMode
{
@ -32,8 +36,17 @@ namespace CSVRender
public:
enum InteractionType
{
InteractionType_PrimaryEdit,
InteractionType_PrimarySelect,
InteractionType_SecondaryEdit,
InteractionType_SecondarySelect,
InteractionType_None
};
/// \brief Editmode for terrain texture grid
TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr);
TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
void primaryOpenPressed (const WorldspaceHitResult& hit);
@ -68,6 +81,9 @@ namespace CSVRender
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit);
/// \brief Handle brush mechanics for texture selection
void selectTerrainTextures (std::pair<int, int>, unsigned char, bool);
/// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId);
@ -83,12 +99,19 @@ namespace CSVRender
std::string mBrushTexture;
int mBrushSize;
int mBrushShape;
std::vector<std::pair<int, int>> mCustomBrushShape;
CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;
int mDragMode;
osg::Group* mParentNode;
bool mIsEditing;
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
PagedWorldspaceWidget& getPagedWorldspaceWidget();
signals:
void passBrushTexture(std::string brushTexture);

Loading…
Cancel
Save