Merge pull request #2517 from unelsson/terraintextureselection

[Final tests] Editor: Terrain texture selection
pull/541/head
Alexei Dobrohotov 5 years ago committed by GitHub
commit 6128279dbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

@ -8,13 +8,6 @@
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
namespace
{
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
}
CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
@ -76,10 +69,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x,
return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));
} }
std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldPos) std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(const osg::Vec3d& worldPos)
{ {
const auto xd = static_cast<float>(worldPos.x() * landTextureSize / cellSize - 0.25f); const auto xd = static_cast<float>(worldPos.x() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE - 0.25f);
const auto yd = static_cast<float>(worldPos.y() * landTextureSize / cellSize + 0.25f); const auto yd = static_cast<float>(worldPos.y() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f);
const auto x = static_cast<int>(std::floor(xd)); const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd)); const auto y = static_cast<int>(std::floor(yd));
@ -87,10 +80,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldP
return std::make_pair(x, y); return std::make_pair(x, y);
} }
std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPos) std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& worldPos)
{ {
const auto xd = static_cast<float>(worldPos.x() * (landSize - 1) / cellSize + 0.5f); const auto xd = static_cast<float>(worldPos.x() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto yd = static_cast<float>(worldPos.y() * (landSize - 1) / cellSize + 0.5f); const auto yd = static_cast<float>(worldPos.y() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto x = static_cast<int>(std::floor(xd)); const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd)); const auto y = static_cast<int>(std::floor(yd));
@ -98,25 +91,32 @@ std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPo
return std::make_pair(x, y); return std::make_pair(x, y);
} }
float CSMWorld::CellCoordinates::textureSelectionToWorldCoords(int pos) float CSMWorld::CellCoordinates::textureGlobalToWorldCoords(int textureGlobal)
{ {
return cellSize * static_cast<float>(pos) / landTextureSize; return ESM::Land::REAL_SIZE * static_cast<float>(textureGlobal) / ESM::Land::LAND_TEXTURE_SIZE;
} }
float CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(int pos) float CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal)
{ {
return cellSize * static_cast<float>(pos) / (landSize - 1); return ESM::Land::REAL_SIZE * static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1);
} }
int CSMWorld::CellCoordinates::vertexSelectionToInCellCoords(int pos) int CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(int vertexGlobal)
{ {
return static_cast<int>(pos - std::floor(static_cast<float>(pos) / (landSize - 1)) * (landSize - 1)); return static_cast<int>(vertexGlobal - std::floor(static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1)) * (ESM::Land::LAND_SIZE - 1));
}
std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal)
{
int x = std::floor(static_cast<float>(textureGlobal.first) / ESM::Land::LAND_TEXTURE_SIZE);
int y = std::floor(static_cast<float>(textureGlobal.second) / ESM::Land::LAND_TEXTURE_SIZE);
return generateId(x, y);
} }
std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(std::pair<int, int> vertexGlobal) std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal)
{ {
int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1)); int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1));
int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1)); int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1));
return generateId(x, y); return generateId(x, y);
} }

@ -49,22 +49,25 @@ namespace CSMWorld
static std::pair<int, int> coordinatesToCellIndex (float x, float y); static std::pair<int, int> coordinatesToCellIndex (float x, float y);
///Converts worldspace coordinates to global texture selection, taking in account the texture offset. ///Converts worldspace coordinates to global texture selection, taking in account the texture offset.
static std::pair<int, int> toTextureCoords(osg::Vec3d worldPos); static std::pair<int, int> toTextureCoords(const osg::Vec3d& worldPos);
///Converts worldspace coordinates to global vertex selection. ///Converts worldspace coordinates to global vertex selection.
static std::pair<int, int> toVertexCoords(osg::Vec3d worldPos); static std::pair<int, int> toVertexCoords(const osg::Vec3d& worldPos);
///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture. ///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture.
static float textureSelectionToWorldCoords(int); static float textureGlobalToWorldCoords(int textureGlobal);
///Converts global vertex coordinate to worldspace coordinate ///Converts global vertex coordinate to worldspace coordinate
static float vertexSelectionToWorldCoords(int); static float vertexGlobalToWorldCoords(int vertexGlobal);
///Converts local cell's heightmap coordinates from the global vertex coordinate ///Converts global vertex coordinate to local cell's heightmap coordinates
static int vertexSelectionToInCellCoords(int); static int vertexGlobalToInCellCoords(int vertexGlobal);
///Converts global texture coordinates to cell id
static std::string textureGlobalToCellId(const std::pair<int, int>& textureGlobal);
///Converts global vertex coordinates to cell id ///Converts global vertex coordinates to cell id
static std::string vertexGlobalToCellId(std::pair<int, int>); static std::string vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal);
}; };
bool operator== (const CellCoordinates& left, const CellCoordinates& right); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

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

@ -0,0 +1,261 @@
#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"
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 = localPositions;
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)
{
for(auto const& localPos: localPositions)
{
auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
mDraggedOperationFlag = true;
if (iterTemp == mTemporarySelection.end())
{
auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
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()
{
if (!mParentNode->containsNode(mSelectionNode)) 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());
if (vertices->size() != 0) 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::vertexGlobalToWorldCoords(x));
float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y));
osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2);
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(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::vertexGlobalToWorldCoords(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::vertexGlobalToWorldCoords(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 = (ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE) * nudgePercentage;
const int landHeightsNudge = (ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE) / (ESM::Land::LAND_SIZE - 1); // Does this work with all land size configurations?
const int textureSizeToLandSizeModifier = (ESM::Land::LAND_SIZE - 1) / ESM::Land::LAND_TEXTURE_SIZE;
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::textureGlobalToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+(i-1), y2)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(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::textureGlobalToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+(i-1), y1)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(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::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x2, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(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::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x1, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(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 / (ESM::Land::LAND_SIZE - 1)));
int cellY = std::floor((1.0f*y / (ESM::Land::LAND_SIZE - 1)));
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int localY = y - cellY * (ESM::Land::LAND_SIZE - 1);
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
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*ESM::Land::LAND_SIZE + localX];
}

@ -0,0 +1,70 @@
#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 toggleInProgress);
void activate();
void deactivate();
std::vector<std::pair<int, int>> getTerrainSelection() const;
protected:
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 <QDragEnterEvent>
#include <QDrag> #include <QDrag>
#include <osg/Group>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp" #include "../widget/modebutton.hpp"
@ -36,12 +38,15 @@
#include "object.hpp" // Something small needed regarding pointers from here () #include "object.hpp" // Something small needed regarding pointers from here ()
#include "worldspacewidget.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), : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
mBrushTexture("L0#0"), mBrushTexture("L0#0"),
mBrushSize(0), mBrushSize(0),
mBrushShape(0), mBrushShape(0),
mTextureBrushScenetool(0) mTextureBrushScenetool(0),
mDragMode(InteractionType_None),
mParentNode(parentNode),
mIsEditing(false)
{ {
} }
@ -62,6 +67,11 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string))); connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));
} }
if (!mTerrainTextureSelection)
{
mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture));
}
EditMode::activate(toolbar); EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool); toolbar->addTool (mTextureBrushScenetool);
} }
@ -74,6 +84,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
delete mTextureBrushScenetool; delete mTextureBrushScenetool;
mTextureBrushScenetool = 0; mTextureBrushScenetool = 0;
} }
if (mTerrainTextureSelection)
{
mTerrainTextureSelection.reset();
}
EditMode::deactivate(toolbar); EditMode::deactivate(toolbar);
} }
@ -95,10 +111,10 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); 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"); undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true) if(allowLandTextureEditing(mCellId))
{ {
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
@ -109,10 +125,18 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit) 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) 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) bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
@ -129,13 +153,16 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
mDragMode = InteractionType_PrimaryEdit;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); 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"); undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true) mIsEditing = true;
if(allowLandTextureEditing(mCellId))
{ {
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
@ -152,29 +179,64 @@ bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)
bool CSVRender::TerrainTextureMode::primarySelectStartDrag (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; return false;
} }
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos) 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; return false;
} }
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
{ {
if (mDragMode == InteractionType_PrimaryEdit)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos);
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); 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)
{ {
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
} }
}
if (mDragMode == InteractionType_PrimarySelect)
{
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) { void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
{
if (mDragMode == InteractionType_PrimaryEdit && mIsEditing)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
@ -184,15 +246,21 @@ void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) {
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{ {
undoStack.endMacro(); 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()); 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 if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
@ -228,7 +296,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos); mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
if(allowLandTextureEditing(mCellId)==true) {} if(allowLandTextureEditing(mCellId)) {}
std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId); std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId);
@ -236,8 +304,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
int cellY = cellCoordinates_pair.first.getY(); int cellY = cellCoordinates_pair.first.getY();
// The coordinates of hit in mCellId // The coordinates of hit in mCellId
int xHitInCell (float(((hit.worldPos.x() - (cellX* 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.5)); int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25));
if (xHitInCell < 0) if (xHitInCell < 0)
{ {
xHitInCell = xHitInCell + landTextureSize; xHitInCell = xHitInCell + landTextureSize;
@ -249,8 +317,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
cellY = cellY + 1; cellY = cellY + 1;
} }
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY); mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
if(allowLandTextureEditing(mCellId)==true) {} if(allowLandTextureEditing(mCellId)) {}
std::string iteratedCellId; std::string iteratedCellId;
@ -266,13 +334,13 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 0) if (mBrushShape == 0)
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId)==true) if(allowLandTextureEditing(mCellId))
{ {
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt; newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId); pushEditToCommand(newTerrain, document, landTable, mCellId);
} }
} }
@ -292,11 +360,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{ {
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) 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) if(allowLandTextureEditing(iteratedCellId))
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++) for(int i = 0; i < landTextureSize; i++)
{ {
for(int j = 0; j < landTextureSize; j++) for(int j = 0; j < landTextureSize; j++)
@ -304,7 +372,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
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 else
{ {
@ -316,11 +384,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); 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 +410,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{ {
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) 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) if(allowLandTextureEditing(iteratedCellId))
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++) for(int i = 0; i < landTextureSize; i++)
{ {
for(int j = 0; j < landTextureSize; j++) for(int j = 0; j < landTextureSize; j++)
@ -363,7 +431,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); 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 else
{ {
@ -376,11 +444,104 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); 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(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) && !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))
{
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);
}
} }
}
bool CSVRender::TerrainTextureMode::isInCellSelection(int globalSelectionX, int globalSelectionY)
{
if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
std::pair<int, int> textureCoords = std::make_pair(globalSelectionX, globalSelectionY);
std::string cellId = CSMWorld::CellCoordinates::textureGlobalToCellId(textureCoords);
return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first);
} }
return false;
}
void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation)
{
int r = mBrushSize / 2;
std::vector<std::pair<int, int>> selections;
if (mBrushShape == 0)
{
if (isInCellSelection(texCoords.first, texCoords.second)) selections.emplace_back(texCoords);
}
if (mBrushShape == 1)
{
for (int i = -r; i <= r; i++)
{
for (int j = -r; j <= r; j++)
{
int x = i + texCoords.first;
int y = j + texCoords.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
}
}
}
if (mBrushShape == 2)
{
for (int i = -r; i <= r; i++)
{
for (int j = -r; j <= r; j++)
{
osg::Vec2f coords(i,j);
if (std::round(coords.length()) < r)
{
int x = i + texCoords.first;
int y = j + texCoords.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
} }
pushEditToCommand(mNew, document, landTable, iteratedCellId);
} }
} }
} }
@ -388,9 +549,22 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 3) if (mBrushShape == 3)
{ {
// Not implemented if(!mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
int x = texCoords.first + value.first;
int y = texCoords.second + value.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
}
}
} }
if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections);
if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);
} }
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
@ -405,8 +579,8 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex))); QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
} }
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName) void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
@ -422,13 +596,15 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
int counter=0; int counter=0;
bool freeIndexFound = false; bool freeIndexFound = false;
do { do
{
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1; const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try try
{ {
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter; 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); newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true; freeIndexFound = true;
@ -527,7 +703,8 @@ bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
return true; return true;
} }
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event)
{
} }
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
@ -538,6 +715,28 @@ void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
void CSVRender::TerrainTextureMode::setBrushShape(int brushShape) void CSVRender::TerrainTextureMode::setBrushShape(int brushShape)
{ {
mBrushShape = 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 += value.first;
selectionCenterY += value.second;
++selectionAmount;
}
selectionCenterX /= selectionAmount;
selectionCenterY /= selectionAmount;
mCustomBrushShape.clear();
for (auto const& value: terrainSelection)
mCustomBrushShape.emplace_back(value.first - selectionCenterX, value.second - selectionCenterY);
}
} }
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture) void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)

@ -4,6 +4,7 @@
#include "editmode.hpp" #include "editmode.hpp"
#include <string> #include <string>
#include <memory>
#include <QWidget> #include <QWidget>
#include <QEvent> #include <QEvent>
@ -18,6 +19,13 @@
#include "../../model/world/landtexture.hpp" #include "../../model/world/landtexture.hpp"
#endif #endif
#include "terrainselection.hpp"
namespace osg
{
class Group;
}
namespace CSVWidget namespace CSVWidget
{ {
class SceneToolTextureBrush; class SceneToolTextureBrush;
@ -25,15 +33,23 @@ namespace CSVWidget
namespace CSVRender namespace CSVRender
{ {
class TerrainTextureMode : public EditMode class TerrainTextureMode : public EditMode
{ {
Q_OBJECT Q_OBJECT
public: public:
enum InteractionType
{
InteractionType_PrimaryEdit,
InteractionType_PrimarySelect,
InteractionType_SecondaryEdit,
InteractionType_SecondarySelect,
InteractionType_None
};
/// \brief Editmode for terrain texture grid /// \brief Editmode for terrain texture grid
TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr); TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
void primaryOpenPressed (const WorldspaceHitResult& hit); void primaryOpenPressed (const WorldspaceHitResult& hit);
@ -68,6 +84,12 @@ namespace CSVRender
/// \brief Handle brush mechanics, maths regarding worldspace hit etc. /// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit); void editTerrainTextureGrid (const WorldspaceHitResult& hit);
/// \brief Check if global selection coordinate belongs to cell in view
bool isInCellSelection(int globalSelectionX, int globalSelectionY);
/// \brief Handle brush mechanics for texture selection
void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation);
/// \brief Push texture edits to command macro /// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId); CSMWorld::IdTable& landTable, std::string cellId);
@ -83,7 +105,12 @@ namespace CSVRender
std::string mBrushTexture; std::string mBrushTexture;
int mBrushSize; int mBrushSize;
int mBrushShape; int mBrushShape;
std::vector<std::pair<int, int>> mCustomBrushShape;
CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;
int mDragMode;
osg::Group* mParentNode;
bool mIsEditing;
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
const int cellSize {ESM::Land::REAL_SIZE}; const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE}; const int landSize {ESM::Land::LAND_SIZE};

Loading…
Cancel
Save