mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-09 09:39:53 +00:00
Merge pull request #2517 from unelsson/terraintextureselection
[Final tests] Editor: Terrain texture selection
This commit is contained in:
commit
6128279dbf
@ -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
|
||||
|
@ -8,13 +8,6 @@
|
||||
#include <components/esm/loadland.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 (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));
|
||||
}
|
||||
|
||||
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 yd = static_cast<float>(worldPos.y() * 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() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f);
|
||||
|
||||
const auto x = static_cast<int>(std::floor(xd));
|
||||
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);
|
||||
}
|
||||
|
||||
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 yd = static_cast<float>(worldPos.y() * (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() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
|
||||
|
||||
const auto x = static_cast<int>(std::floor(xd));
|
||||
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);
|
||||
}
|
||||
|
||||
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::vertexGlobalToCellId(std::pair<int, int> vertexGlobal)
|
||||
std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal)
|
||||
{
|
||||
int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1));
|
||||
int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1));
|
||||
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(const std::pair<int, int>& vertexGlobal)
|
||||
{
|
||||
int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1));
|
||||
int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1));
|
||||
return generateId(x, y);
|
||||
}
|
||||
|
||||
|
@ -49,22 +49,25 @@ namespace CSMWorld
|
||||
static std::pair<int, int> coordinatesToCellIndex (float x, float y);
|
||||
|
||||
///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.
|
||||
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.
|
||||
static float textureSelectionToWorldCoords(int);
|
||||
static float textureGlobalToWorldCoords(int textureGlobal);
|
||||
|
||||
///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
|
||||
static int vertexSelectionToInCellCoords(int);
|
||||
///Converts global vertex coordinate to local cell's heightmap coordinates
|
||||
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
|
||||
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);
|
||||
|
@ -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"),
|
||||
|
261
apps/opencs/view/render/terrainselection.cpp
Normal file
261
apps/opencs/view/render/terrainselection.cpp
Normal file
@ -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];
|
||||
}
|
70
apps/opencs/view/render/terrainselection.hpp
Normal file
70
apps/opencs/view/render/terrainselection.hpp
Normal file
@ -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 <QDrag>
|
||||
|
||||
#include <osg/Group>
|
||||
|
||||
#include <components/esm/loadland.hpp>
|
||||
|
||||
#include "../widget/modebutton.hpp"
|
||||
@ -36,12 +38,15 @@
|
||||
#include "object.hpp" // Something small needed regarding pointers from here ()
|
||||
#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 +67,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 +84,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
|
||||
delete mTextureBrushScenetool;
|
||||
mTextureBrushScenetool = 0;
|
||||
}
|
||||
|
||||
if (mTerrainTextureSelection)
|
||||
{
|
||||
mTerrainTextureSelection.reset();
|
||||
}
|
||||
|
||||
EditMode::deactivate(toolbar);
|
||||
}
|
||||
|
||||
@ -95,10 +111,10 @@ 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)
|
||||
if(allowLandTextureEditing(mCellId))
|
||||
{
|
||||
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
|
||||
editTerrainTextureGrid(hit);
|
||||
@ -109,10 +125,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 +153,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))
|
||||
{
|
||||
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
|
||||
editTerrainTextureGrid(hit);
|
||||
@ -152,47 +179,88 @@ 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();
|
||||
|
||||
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
|
||||
int index = landtexturesCollection.searchId(mBrushTexture);
|
||||
|
||||
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
|
||||
if (mDragMode == InteractionType_PrimaryEdit)
|
||||
{
|
||||
editTerrainTextureGrid(hit);
|
||||
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);
|
||||
|
||||
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
|
||||
{
|
||||
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) {
|
||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||
QUndoStack& undoStack = document.getUndoStack();
|
||||
|
||||
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
|
||||
int index = landtexturesCollection.searchId(mBrushTexture);
|
||||
|
||||
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
|
||||
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
|
||||
{
|
||||
if (mDragMode == InteractionType_PrimaryEdit && mIsEditing)
|
||||
{
|
||||
undoStack.endMacro();
|
||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||
QUndoStack& undoStack = document.getUndoStack();
|
||||
|
||||
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
|
||||
int index = landtexturesCollection.searchId(mBrushTexture);
|
||||
|
||||
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
|
||||
{
|
||||
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
|
||||
@ -228,7 +296,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
|
||||
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
|
||||
|
||||
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
|
||||
if(allowLandTextureEditing(mCellId)==true) {}
|
||||
if(allowLandTextureEditing(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();
|
||||
|
||||
// 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,8 +317,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
|
||||
cellY = cellY + 1;
|
||||
}
|
||||
|
||||
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY);
|
||||
if(allowLandTextureEditing(mCellId)==true) {}
|
||||
mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
|
||||
if(allowLandTextureEditing(mCellId)) {}
|
||||
|
||||
std::string iteratedCellId;
|
||||
|
||||
@ -266,13 +334,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)
|
||||
if(allowLandTextureEditing(mCellId))
|
||||
{
|
||||
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt;
|
||||
pushEditToCommand(mNew, document, landTable, mCellId);
|
||||
newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;
|
||||
pushEditToCommand(newTerrain, document, landTable, mCellId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,19 +360,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);
|
||||
if(allowLandTextureEditing(iteratedCellId)==true)
|
||||
iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
|
||||
if(allowLandTextureEditing(iteratedCellId))
|
||||
{
|
||||
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 +384,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 +410,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);
|
||||
if(allowLandTextureEditing(iteratedCellId)==true)
|
||||
iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
|
||||
if(allowLandTextureEditing(iteratedCellId))
|
||||
{
|
||||
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 +431,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,11 +444,11 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -388,9 +456,115 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
|
||||
|
||||
if (mBrushShape == 3)
|
||||
{
|
||||
// Not implemented
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mBrushShape == 3)
|
||||
{
|
||||
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,
|
||||
@ -405,8 +579,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,13 +596,15 @@ 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;
|
||||
@ -527,7 +703,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,6 +715,28 @@ 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 += 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)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "editmode.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QEvent>
|
||||
@ -18,6 +19,13 @@
|
||||
#include "../../model/world/landtexture.hpp"
|
||||
#endif
|
||||
|
||||
#include "terrainselection.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Group;
|
||||
}
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
class SceneToolTextureBrush;
|
||||
@ -25,15 +33,23 @@ namespace CSVWidget
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
class TerrainTextureMode : public EditMode
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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 +84,12 @@ namespace CSVRender
|
||||
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
|
||||
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
|
||||
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
|
||||
CSMWorld::IdTable& landTable, std::string cellId);
|
||||
@ -83,7 +105,12 @@ 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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user