mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 06:53:52 +00:00
Merge branch 'Terrain-Selection-Issues' into 'master'
Various fixes for the editor's "Terrain land editing" mode (fixes #5473, #6022, #6023, #6024, #6035, #6036) Closes #6036, #6035, #6024, #6023, #6022, and #5473 See merge request OpenMW/openmw!840
This commit is contained in:
commit
e596f62107
10 changed files with 272 additions and 82 deletions
|
@ -64,6 +64,7 @@
|
||||||
Bug #5452: Autowalk is being included in savegames
|
Bug #5452: Autowalk is being included in savegames
|
||||||
Bug #5469: Local map is reset when re-entering certain cells
|
Bug #5469: Local map is reset when re-entering certain cells
|
||||||
Bug #5472: Mistify mod causes CTD in 0.46 on Mac
|
Bug #5472: Mistify mod causes CTD in 0.46 on Mac
|
||||||
|
Bug #5473: OpenMW-CS: Cell border lines don't update properly on terrain change
|
||||||
Bug #5479: NPCs who should be walking around town are standing around without walking
|
Bug #5479: NPCs who should be walking around town are standing around without walking
|
||||||
Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold
|
Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold
|
||||||
Bug #5485: Intimidate doesn't increase disposition on marginal wins
|
Bug #5485: Intimidate doesn't increase disposition on marginal wins
|
||||||
|
@ -124,7 +125,11 @@
|
||||||
Bug #5995: NiUVController doesn't calculate the UV offset properly
|
Bug #5995: NiUVController doesn't calculate the UV offset properly
|
||||||
Bug #6007: Crash when ending cutscene is playing
|
Bug #6007: Crash when ending cutscene is playing
|
||||||
Bug #6016: Greeting interrupts Fargoth's sneak-walk
|
Bug #6016: Greeting interrupts Fargoth's sneak-walk
|
||||||
|
Bug #6022: OpenMW-CS: Terrain selection is not updated when undoing/redoing terrain changes
|
||||||
|
Bug #6023: OpenMW-CS: Clicking on a reference in "Terrain land editing" mode discards corresponding select/edit action
|
||||||
Bug #6028: Particle system controller values are incorrectly used
|
Bug #6028: Particle system controller values are incorrectly used
|
||||||
|
Bug #6035: OpenMW-CS: Circle brush in "Terrain land editing" mode sometimes includes vertices outside its radius
|
||||||
|
Bug #6036: OpenMW-CS: Terrain selection at the border of cells omits certain corner vertices
|
||||||
Bug #6043: Actor can have torch missing when torch animation is played
|
Bug #6043: Actor can have torch missing when torch animation is played
|
||||||
Bug #6047: Mouse bindings can be triggered during save loading
|
Bug #6047: Mouse bindings can be triggered during save loading
|
||||||
Feature #390: 3rd person look "over the shoulder"
|
Feature #390: 3rd person look "over the shoulder"
|
||||||
|
@ -166,6 +171,7 @@
|
||||||
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
|
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
|
||||||
Feature #5828: Support more than 8 lights
|
Feature #5828: Support more than 8 lights
|
||||||
Feature #5910: Fall back to delta time when physics can't keep up
|
Feature #5910: Fall back to delta time when physics can't keep up
|
||||||
|
Feature #6024: OpenMW-CS: Selecting terrain in "Terrain land editing" should support "Add to selection" and "Remove from selection" modes
|
||||||
Feature #6033: Include pathgrid to navigation mesh
|
Feature #6033: Include pathgrid to navigation mesh
|
||||||
Feature #6034: Find path based on area cost depending on NPC stats
|
Feature #6034: Find path based on area cost depending on NPC stats
|
||||||
Task #5480: Drop Qt4 support
|
Task #5480: Drop Qt4 support
|
||||||
|
|
|
@ -38,9 +38,15 @@ Editor Bug Fixes:
|
||||||
- Disabled record sorting in Topic and Journal Info tables, implemented drag-move for records (#4357)
|
- Disabled record sorting in Topic and Journal Info tables, implemented drag-move for records (#4357)
|
||||||
- Topic and Journal Info records can now be cloned with a different parent Topic/Journal Id (#4363)
|
- Topic and Journal Info records can now be cloned with a different parent Topic/Journal Id (#4363)
|
||||||
- Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400)
|
- Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400)
|
||||||
|
- Cell borders are now properly redrawn when undoing/redoing terrain changes (#5473)
|
||||||
- Loading mods now keeps the master index (#5675)
|
- Loading mods now keeps the master index (#5675)
|
||||||
- Flicker and crashing on XFCE4 fixed (#5703)
|
- Flicker and crashing on XFCE4 fixed (#5703)
|
||||||
- Collada models render properly in the Editor (#5713)
|
- Collada models render properly in the Editor (#5713)
|
||||||
|
- Terrain-selection grid is now properly updated when undoing/redoing terrain changes (#6022)
|
||||||
|
- Tool outline and select/edit actions in "Terrain land editing" mode now ignore references (#6023)
|
||||||
|
- Primary-select and secondary-select actions in "Terrain land editing" mode now behave like in "Instance editing" mode (#6024)
|
||||||
|
- Using the circle brush to select terrain in the "Terrain land editing" mode no longer selects vertices outside the circle (#6035)
|
||||||
|
- Vertices at the NW and SE corners of a cell can now also be selected in "Terrain land editing" mode if the adjacent cells aren't loaded yet (#6036)
|
||||||
|
|
||||||
Miscellaneous:
|
Miscellaneous:
|
||||||
- Prevent save-game bloating by using an appropriate fog texture format (#5108)
|
- Prevent save-game bloating by using an appropriate fog texture format (#5108)
|
||||||
|
|
|
@ -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 terrainselection terrainshapemode brushdraw
|
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units_noqt (view/render
|
opencs_units_noqt (view/render
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/Geode>
|
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/PrimitiveSet>
|
#include <osg/PrimitiveSet>
|
||||||
|
|
||||||
|
@ -13,15 +12,23 @@
|
||||||
#include "../../model/world/cellcoordinates.hpp"
|
#include "../../model/world/cellcoordinates.hpp"
|
||||||
|
|
||||||
const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE;
|
const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE;
|
||||||
const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3;
|
|
||||||
|
/*
|
||||||
|
The number of vertices per cell border is equal to the number of vertices per edge
|
||||||
|
minus the duplicated corner vertices. An additional vertex to close the loop is NOT needed.
|
||||||
|
*/
|
||||||
|
const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 4;
|
||||||
|
|
||||||
|
|
||||||
CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords)
|
CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords)
|
||||||
: mParentNode(cellNode)
|
: mParentNode(cellNode)
|
||||||
{
|
{
|
||||||
|
mBorderGeometry = new osg::Geometry();
|
||||||
|
|
||||||
mBaseNode = new osg::PositionAttitudeTransform();
|
mBaseNode = new osg::PositionAttitudeTransform();
|
||||||
mBaseNode->setNodeMask(Mask_CellBorder);
|
mBaseNode->setNodeMask(Mask_CellBorder);
|
||||||
mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10));
|
mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10));
|
||||||
|
mBaseNode->addChild(mBorderGeometry);
|
||||||
|
|
||||||
mParentNode->addChild(mBaseNode);
|
mParentNode->addChild(mBaseNode);
|
||||||
}
|
}
|
||||||
|
@ -38,56 +45,59 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand)
|
||||||
if (!landData)
|
if (!landData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
|
mBaseNode->removeChild(mBorderGeometry);
|
||||||
|
mBorderGeometry = new osg::Geometry();
|
||||||
|
|
||||||
// Vertices
|
|
||||||
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();
|
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();
|
||||||
|
|
||||||
int x = 0, y = 0;
|
int x = 0;
|
||||||
for (; x < ESM::Land::LAND_SIZE; ++x)
|
int y = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Traverse the cell border counter-clockwise starting at the SW corner vertex (0, 0).
|
||||||
|
Each loop starts at a corner vertex and ends right before the next corner vertex.
|
||||||
|
*/
|
||||||
|
for (; x < ESM::Land::LAND_SIZE - 1; ++x)
|
||||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||||
|
|
||||||
x = ESM::Land::LAND_SIZE - 1;
|
x = ESM::Land::LAND_SIZE - 1;
|
||||||
for (; y < ESM::Land::LAND_SIZE; ++y)
|
for (; y < ESM::Land::LAND_SIZE - 1; ++y)
|
||||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||||
|
|
||||||
y = ESM::Land::LAND_SIZE - 1;
|
y = ESM::Land::LAND_SIZE - 1;
|
||||||
for (; x >= 0; --x)
|
for (; x > 0; --x)
|
||||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||||
|
|
||||||
x = 0;
|
x = 0;
|
||||||
for (; y >= 0; --y)
|
for (; y > 0; --y)
|
||||||
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)]));
|
||||||
|
|
||||||
geometry->setVertexArray(vertices);
|
mBorderGeometry->setVertexArray(vertices);
|
||||||
|
|
||||||
// Color
|
|
||||||
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array();
|
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array();
|
||||||
colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f));
|
colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f));
|
||||||
|
|
||||||
geometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);
|
mBorderGeometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);
|
||||||
|
|
||||||
// Primitive
|
|
||||||
osg::ref_ptr<osg::DrawElementsUShort> primitives =
|
osg::ref_ptr<osg::DrawElementsUShort> primitives =
|
||||||
new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount+1);
|
new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount + 1);
|
||||||
|
|
||||||
|
// Assign one primitive to each vertex.
|
||||||
for (size_t i = 0; i < VertexCount; ++i)
|
for (size_t i = 0; i < VertexCount; ++i)
|
||||||
primitives->setElement(i, i);
|
primitives->setElement(i, i);
|
||||||
|
|
||||||
|
// Assign the last primitive to the first vertex to close the loop.
|
||||||
primitives->setElement(VertexCount, 0);
|
primitives->setElement(VertexCount, 0);
|
||||||
|
|
||||||
geometry->addPrimitiveSet(primitives);
|
mBorderGeometry->addPrimitiveSet(primitives);
|
||||||
geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
mBorderGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||||
|
|
||||||
|
mBaseNode->addChild(mBorderGeometry);
|
||||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode();
|
|
||||||
geode->addDrawable(geometry);
|
|
||||||
mBaseNode->addChild(geode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CSVRender::CellBorder::landIndex(int x, int y)
|
size_t CSVRender::CellBorder::landIndex(int x, int y)
|
||||||
{
|
{
|
||||||
return y * ESM::Land::LAND_SIZE + x;
|
return static_cast<size_t>(y) * ESM::Land::LAND_SIZE + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
float CSVRender::CellBorder::scaleToWorld(int value)
|
float CSVRender::CellBorder::scaleToWorld(int value)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
|
class Geometry;
|
||||||
class Group;
|
class Group;
|
||||||
class PositionAttitudeTransform;
|
class PositionAttitudeTransform;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +48,7 @@ namespace CSVRender
|
||||||
|
|
||||||
osg::Group* mParentNode;
|
osg::Group* mParentNode;
|
||||||
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||||
|
osg::ref_ptr<osg::Geometry> mBorderGeometry;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
apps/opencs/view/render/commands.cpp
Normal file
19
apps/opencs/view/render/commands.cpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "commands.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/loadland.hpp>
|
||||||
|
|
||||||
|
#include "terrainselection.hpp"
|
||||||
|
|
||||||
|
CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent)
|
||||||
|
: mTerrainSelection(terrainSelection)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void CSVRender::DrawTerrainSelectionCommand::redo()
|
||||||
|
{
|
||||||
|
mTerrainSelection.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVRender::DrawTerrainSelectionCommand::undo()
|
||||||
|
{
|
||||||
|
mTerrainSelection.update();
|
||||||
|
}
|
35
apps/opencs/view/render/commands.hpp
Normal file
35
apps/opencs/view/render/commands.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef CSV_RENDER_COMMANDS_HPP
|
||||||
|
#define CSV_RENDER_COMMANDS_HPP
|
||||||
|
|
||||||
|
#include <QUndoCommand>
|
||||||
|
|
||||||
|
namespace CSVRender
|
||||||
|
{
|
||||||
|
class TerrainSelection;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Current solution to force a redrawing of the terrain-selection grid
|
||||||
|
when undoing/redoing changes in the editor.
|
||||||
|
This only triggers a simple redraw of the grid, so only use it in
|
||||||
|
conjunction with actual data changes which deform the grid.
|
||||||
|
|
||||||
|
Please note that this command needs to be put onto the QUndoStack twice:
|
||||||
|
at the start and at the end of the related terrain manipulation.
|
||||||
|
This makes sure that the grid is always updated after all changes have
|
||||||
|
been undone or redone -- but it also means that the selection is redrawn
|
||||||
|
once at the beginning of either action. Future refinement may solve that.
|
||||||
|
*/
|
||||||
|
class DrawTerrainSelectionCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
TerrainSelection& mTerrainSelection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent = nullptr);
|
||||||
|
|
||||||
|
void redo() override;
|
||||||
|
void undo() override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -39,64 +39,23 @@ std::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelectio
|
||||||
void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> &localPositions)
|
void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> &localPositions)
|
||||||
{
|
{
|
||||||
mSelection = localPositions;
|
mSelection = localPositions;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::TerrainSelection::addSelect(const std::pair<int, int> &localPos)
|
void CSVRender::TerrainSelection::addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress)
|
||||||
{
|
{
|
||||||
if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end())
|
handleSelection(localPositions, toggleInProgress, SelectionMethod::AddSelect);
|
||||||
{
|
|
||||||
mSelection.emplace_back(localPos);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress)
|
void CSVRender::TerrainSelection::removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress)
|
||||||
{
|
{
|
||||||
if (toggleInProgress)
|
handleSelection(localPositions, toggleInProgress, SelectionMethod::RemoveSelect);
|
||||||
{
|
}
|
||||||
for(auto const& localPos: localPositions)
|
|
||||||
{
|
|
||||||
auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
|
|
||||||
mDraggedOperationFlag = true;
|
|
||||||
|
|
||||||
if (iterTemp == mTemporarySelection.end())
|
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress)
|
||||||
{
|
{
|
||||||
auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
|
handleSelection(localPositions, toggleInProgress, SelectionMethod::ToggleSelect);
|
||||||
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()
|
void CSVRender::TerrainSelection::activate()
|
||||||
|
@ -239,6 +198,100 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::V
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSVRender::TerrainSelection::handleSelection(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
switch (selectionMethod)
|
||||||
|
{
|
||||||
|
case SelectionMethod::AddSelect:
|
||||||
|
if (iter == mSelection.end())
|
||||||
|
{
|
||||||
|
mSelection.emplace_back(localPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectionMethod::RemoveSelect:
|
||||||
|
if (iter != mSelection.end())
|
||||||
|
{
|
||||||
|
mSelection.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectionMethod::ToggleSelect:
|
||||||
|
if (iter == mSelection.end())
|
||||||
|
{
|
||||||
|
mSelection.emplace_back(localPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSelection.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mTemporarySelection.push_back(localPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mDraggedOperationFlag == false)
|
||||||
|
{
|
||||||
|
for (auto const& localPos : localPositions)
|
||||||
|
{
|
||||||
|
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
|
||||||
|
|
||||||
|
switch (selectionMethod)
|
||||||
|
{
|
||||||
|
case SelectionMethod::AddSelect:
|
||||||
|
if (iter == mSelection.end())
|
||||||
|
{
|
||||||
|
mSelection.emplace_back(localPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectionMethod::RemoveSelect:
|
||||||
|
if (iter != mSelection.end())
|
||||||
|
{
|
||||||
|
mSelection.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectionMethod::ToggleSelect:
|
||||||
|
if (iter == mSelection.end())
|
||||||
|
{
|
||||||
|
mSelection.emplace_back(localPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSelection.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDraggedOperationFlag = false;
|
||||||
|
|
||||||
|
mTemporarySelection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
bool CSVRender::TerrainSelection::noCell(const std::string& cellId)
|
bool CSVRender::TerrainSelection::noCell(const std::string& cellId)
|
||||||
{
|
{
|
||||||
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
|
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
|
||||||
|
|
|
@ -27,6 +27,14 @@ namespace CSVRender
|
||||||
Shape
|
Shape
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SelectionMethod
|
||||||
|
{
|
||||||
|
OnlySelect,
|
||||||
|
AddSelect,
|
||||||
|
RemoveSelect,
|
||||||
|
ToggleSelect
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Class handling the terrain selection data and rendering
|
/// \brief Class handling the terrain selection data and rendering
|
||||||
class TerrainSelection
|
class TerrainSelection
|
||||||
{
|
{
|
||||||
|
@ -36,7 +44,8 @@ namespace CSVRender
|
||||||
~TerrainSelection();
|
~TerrainSelection();
|
||||||
|
|
||||||
void onlySelect(const std::vector<std::pair<int, int>> &localPositions);
|
void onlySelect(const std::vector<std::pair<int, int>> &localPositions);
|
||||||
void addSelect(const std::pair<int, int> &localPos);
|
void addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress);
|
||||||
|
void removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress);
|
||||||
void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress);
|
void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress);
|
||||||
|
|
||||||
void activate();
|
void activate();
|
||||||
|
@ -55,6 +64,8 @@ namespace CSVRender
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void handleSelection(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod);
|
||||||
|
|
||||||
bool noCell(const std::string& cellId);
|
bool noCell(const std::string& cellId);
|
||||||
|
|
||||||
bool noLand(const std::string& cellId);
|
bool noLand(const std::string& cellId);
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "../../model/world/universalid.hpp"
|
#include "../../model/world/universalid.hpp"
|
||||||
|
|
||||||
#include "brushdraw.hpp"
|
#include "brushdraw.hpp"
|
||||||
|
#include "commands.hpp"
|
||||||
#include "editmode.hpp"
|
#include "editmode.hpp"
|
||||||
#include "pagedworldspacewidget.hpp"
|
#include "pagedworldspacewidget.hpp"
|
||||||
#include "mask.hpp"
|
#include "mask.hpp"
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
#include "worldspacewidget.hpp"
|
#include "worldspacewidget.hpp"
|
||||||
|
|
||||||
CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
|
CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
|
||||||
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent),
|
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain, "Terrain land editing", parent),
|
||||||
mParentNode(parentNode)
|
mParentNode(parentNode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -285,6 +286,9 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
|
||||||
|
|
||||||
undoStack.beginMacro ("Edit shape and normal records");
|
undoStack.beginMacro ("Edit shape and normal records");
|
||||||
|
|
||||||
|
// One command at the beginning of the macro for redrawing the terrain-selection grid when undoing the changes.
|
||||||
|
undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection));
|
||||||
|
|
||||||
for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)
|
for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)
|
||||||
{
|
{
|
||||||
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());
|
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());
|
||||||
|
@ -353,6 +357,9 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
|
||||||
}
|
}
|
||||||
pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId);
|
pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId);
|
||||||
}
|
}
|
||||||
|
// One command at the end of the macro for redrawing the terrain-selection grid when redoing the changes.
|
||||||
|
undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection));
|
||||||
|
|
||||||
undoStack.endMacro();
|
undoStack.endMacro();
|
||||||
clearTransientEdits();
|
clearTransientEdits();
|
||||||
}
|
}
|
||||||
|
@ -430,7 +437,9 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair<int, int>
|
||||||
float smoothedByDistance = 0.0f;
|
float smoothedByDistance = 0.0f;
|
||||||
if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = calculateBumpShape(distance, r, mTotalDiffY);
|
if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = calculateBumpShape(distance, r, mTotalDiffY);
|
||||||
if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = calculateBumpShape(distance, r, r + mShapeEditToolStrength);
|
if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = calculateBumpShape(distance, r, r + mShapeEditToolStrength);
|
||||||
if (distance <= r)
|
|
||||||
|
// Using floating-point radius here to prevent selecting too few vertices.
|
||||||
|
if (distance <= mBrushSize / 2.0f)
|
||||||
{
|
{
|
||||||
if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, smoothedByDistance);
|
if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, smoothedByDistance);
|
||||||
if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower)
|
if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower)
|
||||||
|
@ -1033,10 +1042,35 @@ void CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int glob
|
||||||
return;
|
return;
|
||||||
int selectionX = globalSelectionX;
|
int selectionX = globalSelectionX;
|
||||||
int selectionY = globalSelectionY;
|
int selectionY = globalSelectionY;
|
||||||
if (xIsAtCellBorder)
|
|
||||||
|
/*
|
||||||
|
The northern and eastern edges don't belong to the current cell.
|
||||||
|
If the corresponding adjacent cell is not loaded, some special handling is necessary to select border vertices.
|
||||||
|
*/
|
||||||
|
if (xIsAtCellBorder && yIsAtCellBorder)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Handle the NW, NE, and SE corner vertices.
|
||||||
|
NW corner: (+1, -1) offset to reach current cell.
|
||||||
|
NE corner: (-1, -1) offset to reach current cell.
|
||||||
|
SE corner: (-1, +1) offset to reach current cell.
|
||||||
|
*/
|
||||||
|
if (isInCellSelection(globalSelectionX - 1, globalSelectionY - 1)
|
||||||
|
|| isInCellSelection(globalSelectionX + 1, globalSelectionY - 1)
|
||||||
|
|| isInCellSelection(globalSelectionX - 1, globalSelectionY + 1))
|
||||||
|
{
|
||||||
|
selections->emplace_back(globalSelectionX, globalSelectionY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (xIsAtCellBorder)
|
||||||
|
{
|
||||||
selectionX--;
|
selectionX--;
|
||||||
if (yIsAtCellBorder)
|
}
|
||||||
|
else if (yIsAtCellBorder)
|
||||||
|
{
|
||||||
selectionY--;
|
selectionY--;
|
||||||
|
}
|
||||||
|
|
||||||
if (isInCellSelection(selectionX, selectionY))
|
if (isInCellSelection(selectionX, selectionY))
|
||||||
selections->emplace_back(globalSelectionX, globalSelectionY);
|
selections->emplace_back(globalSelectionX, globalSelectionY);
|
||||||
}
|
}
|
||||||
|
@ -1071,8 +1105,11 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
|
||||||
{
|
{
|
||||||
int distanceX = abs(i - vertexCoords.first);
|
int distanceX = abs(i - vertexCoords.first);
|
||||||
int distanceY = abs(j - vertexCoords.second);
|
int distanceY = abs(j - vertexCoords.second);
|
||||||
int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
|
float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2));
|
||||||
if (distance <= r) handleSelection(i, j, &selections);
|
|
||||||
|
// Using floating-point radius here to prevent selecting too few vertices.
|
||||||
|
if (distance <= mBrushSize / 2.0f)
|
||||||
|
handleSelection(i, j, &selections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1089,9 +1126,21 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(selectMode == 0) mTerrainShapeSelection->onlySelect(selections);
|
std::string selectAction;
|
||||||
if(selectMode == 1) mTerrainShapeSelection->toggleSelect(selections, dragOperation);
|
|
||||||
|
|
||||||
|
if (selectMode == 0)
|
||||||
|
selectAction = CSMPrefs::get()["3D Scene Editing"]["primary-select-action"].toString();
|
||||||
|
else
|
||||||
|
selectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString();
|
||||||
|
|
||||||
|
if (selectAction == "Select only")
|
||||||
|
mTerrainShapeSelection->onlySelect(selections);
|
||||||
|
else if (selectAction == "Add to selection")
|
||||||
|
mTerrainShapeSelection->addSelect(selections, dragOperation);
|
||||||
|
else if (selectAction == "Remove from selection")
|
||||||
|
mTerrainShapeSelection->removeSelect(selections, dragOperation);
|
||||||
|
else if (selectAction == "Invert selection")
|
||||||
|
mTerrainShapeSelection->toggleSelect(selections, dragOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,
|
void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,
|
||||||
|
|
Loading…
Reference in a new issue