From 18ea4d8eb232821987c47a862f23057e797a4fde Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Sun, 9 May 2021 22:19:57 +0200 Subject: [PATCH 01/10] First step toward fixing terrain selection issues. --- apps/opencs/view/render/terrainselection.cpp | 79 +++++++++++++++++++- apps/opencs/view/render/terrainselection.hpp | 3 +- apps/opencs/view/render/terrainshapemode.cpp | 18 ++++- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/render/terrainselection.cpp b/apps/opencs/view/render/terrainselection.cpp index 4e209af57..be7fefe30 100644 --- a/apps/opencs/view/render/terrainselection.cpp +++ b/apps/opencs/view/render/terrainselection.cpp @@ -42,13 +42,84 @@ void CSVRender::TerrainSelection::onlySelect(const std::vector &localPos) +void CSVRender::TerrainSelection::addSelect(const std::vector>& localPositions, bool toggleInProgress) { - if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end()) + 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.emplace_back(localPos); + } + } + + mTemporarySelection.push_back(localPos); + } + } + else if (mDraggedOperationFlag == false) { - mSelection.emplace_back(localPos); - update(); + for (auto const& localPos : localPositions) + { + const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); + if (iter == mSelection.end()) + { + mSelection.emplace_back(localPos); + } + } } + else + { + mDraggedOperationFlag = false; + mTemporarySelection.clear(); + } + update(); +} + +void CSVRender::TerrainSelection::removeSelect(const std::vector>& 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); + } + } + + 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 + { + mDraggedOperationFlag = false; + mTemporarySelection.clear(); + } + update(); } void CSVRender::TerrainSelection::toggleSelect(const std::vector> &localPositions, bool toggleInProgress) diff --git a/apps/opencs/view/render/terrainselection.hpp b/apps/opencs/view/render/terrainselection.hpp index 84ee6f25a..9c1eb55b1 100644 --- a/apps/opencs/view/render/terrainselection.hpp +++ b/apps/opencs/view/render/terrainselection.hpp @@ -36,7 +36,8 @@ namespace CSVRender ~TerrainSelection(); void onlySelect(const std::vector> &localPositions); - void addSelect(const std::pair &localPos); + void addSelect(const std::vector>& localPositions, bool toggleInProgress); + void removeSelect(const std::vector>& localPositions, bool toggleInProgress); void toggleSelect(const std::vector> &localPositions, bool toggleInProgress); void activate(); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 1739e143d..6209e161e 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -42,7 +42,7 @@ #include "worldspacewidget.hpp" 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) { } @@ -1089,9 +1089,21 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } } - if(selectMode == 0) mTerrainShapeSelection->onlySelect(selections); - if(selectMode == 1) mTerrainShapeSelection->toggleSelect(selections, dragOperation); + std::string selectAction; + 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, From 008bf64dd916a926156c0e19ffe215c8bf32db9e Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Mon, 10 May 2021 14:20:53 +0200 Subject: [PATCH 02/10] Second step toward fixing terrain selection issues. --- apps/opencs/view/render/cellborder.cpp | 9 +++--- apps/opencs/view/render/cellborder.hpp | 3 +- apps/opencs/view/render/commands.cpp | 20 +++++++++++++ apps/opencs/view/render/commands.hpp | 30 ++++++++++++++++++++ apps/opencs/view/render/terrainshapemode.cpp | 3 ++ 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 apps/opencs/view/render/commands.cpp create mode 100644 apps/opencs/view/render/commands.hpp diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index 6073807ce..88de12ca5 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -19,9 +19,12 @@ const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3; CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords) : mParentNode(cellNode) { + mBorderGeode = new osg::Geode(); + mBaseNode = new osg::PositionAttitudeTransform(); mBaseNode->setNodeMask(Mask_CellBorder); mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10)); + mBaseNode->addChild(mBorderGeode); mParentNode->addChild(mBaseNode); } @@ -79,10 +82,8 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) geometry->addPrimitiveSet(primitives); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - - osg::ref_ptr geode = new osg::Geode(); - geode->addDrawable(geometry); - mBaseNode->addChild(geode); + mBorderGeode->removeDrawables(0); + mBorderGeode->addDrawable(geometry); } size_t CSVRender::CellBorder::landIndex(int x, int y) diff --git a/apps/opencs/view/render/cellborder.hpp b/apps/opencs/view/render/cellborder.hpp index c91aa46c6..c7e47a00f 100644 --- a/apps/opencs/view/render/cellborder.hpp +++ b/apps/opencs/view/render/cellborder.hpp @@ -7,6 +7,7 @@ namespace osg { + class Geode; class Group; class PositionAttitudeTransform; } @@ -47,7 +48,7 @@ namespace CSVRender osg::Group* mParentNode; osg::ref_ptr mBaseNode; - + osg::ref_ptr mBorderGeode; }; } diff --git a/apps/opencs/view/render/commands.cpp b/apps/opencs/view/render/commands.cpp new file mode 100644 index 000000000..40634acec --- /dev/null +++ b/apps/opencs/view/render/commands.cpp @@ -0,0 +1,20 @@ +#include "commands.hpp" + +#include + +#include "cell.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(); +} diff --git a/apps/opencs/view/render/commands.hpp b/apps/opencs/view/render/commands.hpp new file mode 100644 index 000000000..f11a76461 --- /dev/null +++ b/apps/opencs/view/render/commands.hpp @@ -0,0 +1,30 @@ +#ifndef CSV_RENDER_COMMANDS_HPP +#define CSV_RENDER_COMMANDS_HPP + +#include + +namespace ESM +{ + struct Land; +} + +namespace CSVRender +{ + class Cell; + class TerrainSelection; + + + class DrawTerrainSelectionCommand : public QUndoCommand + { + private: + TerrainSelection& mTerrainSelection; + + public: + DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent = nullptr); + + void redo() override; + void undo() override; + }; +} + +#endif diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 6209e161e..418655f2f 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -34,6 +34,7 @@ #include "../../model/world/universalid.hpp" #include "brushdraw.hpp" +#include "commands.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" #include "mask.hpp" @@ -284,6 +285,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() sortAndLimitAlteredCells(); undoStack.beginMacro ("Edit shape and normal records"); + undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection)); for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) { @@ -353,6 +355,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); } + undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection)); undoStack.endMacro(); clearTransientEdits(); } From 6c49074765240064e5fab9b403cb4afb1d0358f2 Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Mon, 10 May 2021 15:19:24 +0200 Subject: [PATCH 03/10] Remove old references to Cell class. --- apps/opencs/view/render/commands.cpp | 1 - apps/opencs/view/render/commands.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/opencs/view/render/commands.cpp b/apps/opencs/view/render/commands.cpp index 40634acec..7b3760296 100644 --- a/apps/opencs/view/render/commands.cpp +++ b/apps/opencs/view/render/commands.cpp @@ -2,7 +2,6 @@ #include -#include "cell.hpp" #include "terrainselection.hpp" CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent) diff --git a/apps/opencs/view/render/commands.hpp b/apps/opencs/view/render/commands.hpp index f11a76461..545d8071f 100644 --- a/apps/opencs/view/render/commands.hpp +++ b/apps/opencs/view/render/commands.hpp @@ -10,7 +10,6 @@ namespace ESM namespace CSVRender { - class Cell; class TerrainSelection; From 73949d5bd063f5b1e9c227e5a55b658c617f90ae Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Mon, 10 May 2021 16:07:46 +0200 Subject: [PATCH 04/10] Updating the CMake file isn't a bad idea either... --- apps/opencs/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index b73cd37b8..19c32df60 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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 terrainselection terrainshapemode brushdraw + cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands ) opencs_units_noqt (view/render From 7be891b4409178c9001d6690112a2b441e858ff9 Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Mon, 10 May 2021 23:05:34 +0200 Subject: [PATCH 05/10] Directly use Geometry instead of Geode; fix for loop; add size_t type-cast. --- apps/opencs/view/render/cellborder.cpp | 35 +++++++++++++------------- apps/opencs/view/render/cellborder.hpp | 4 +-- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index 88de12ca5..a1ae4f452 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -19,12 +18,12 @@ const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3; CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords) : mParentNode(cellNode) { - mBorderGeode = new osg::Geode(); + mBorderGeometry = new osg::Geometry(); mBaseNode = new osg::PositionAttitudeTransform(); mBaseNode->setNodeMask(Mask_CellBorder); mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10)); - mBaseNode->addChild(mBorderGeode); + mBaseNode->addChild(mBorderGeometry); mParentNode->addChild(mBaseNode); } @@ -41,54 +40,56 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) if (!landData) return; - osg::ref_ptr geometry = new osg::Geometry(); + mBaseNode->removeChild(mBorderGeometry); + mBorderGeometry = new osg::Geometry(); // Vertices osg::ref_ptr vertices = new osg::Vec3Array(); - int x = 0, y = 0; - for (; x < ESM::Land::LAND_SIZE; ++x) + int x = 0; + int y = 0; + + for (/* */; x < ESM::Land::LAND_SIZE - 1; ++x) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); 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)])); 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)])); x = 0; - for (; y >= 0; --y) + for (/* */; y >= 0; --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 colors = new osg::Vec4Array(); 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 primitives = - new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount+1); + new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount); for (size_t i = 0; i < VertexCount; ++i) primitives->setElement(i, i); primitives->setElement(VertexCount, 0); - geometry->addPrimitiveSet(primitives); - geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + mBorderGeometry->addPrimitiveSet(primitives); + mBorderGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - mBorderGeode->removeDrawables(0); - mBorderGeode->addDrawable(geometry); + mBaseNode->addChild(mBorderGeometry); } size_t CSVRender::CellBorder::landIndex(int x, int y) { - return y * ESM::Land::LAND_SIZE + x; + return static_cast(y) * ESM::Land::LAND_SIZE + x; } float CSVRender::CellBorder::scaleToWorld(int value) diff --git a/apps/opencs/view/render/cellborder.hpp b/apps/opencs/view/render/cellborder.hpp index c7e47a00f..be2e18eee 100644 --- a/apps/opencs/view/render/cellborder.hpp +++ b/apps/opencs/view/render/cellborder.hpp @@ -7,7 +7,7 @@ namespace osg { - class Geode; + class Geometry; class Group; class PositionAttitudeTransform; } @@ -48,7 +48,7 @@ namespace CSVRender osg::Group* mParentNode; osg::ref_ptr mBaseNode; - osg::ref_ptr mBorderGeode; + osg::ref_ptr mBorderGeometry; }; } From 425f745d53ae6858de353564f0d1ee3c12d0984b Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Tue, 18 May 2021 15:03:56 +0200 Subject: [PATCH 06/10] Resolved merge conflicts with changelog. --- CHANGELOG.md | 4 ++++ CHANGELOG_PR.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d5c97352..632e02309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ Bug #5452: Autowalk is being included in savegames Bug #5469: Local map is reset when re-entering certain cells 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 #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 @@ -124,6 +125,8 @@ Bug #5995: NiUVController doesn't calculate the UV offset properly Bug #6007: Crash when ending cutscene is playing 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 #6043: Actor can have torch missing when torch animation is played Bug #6047: Mouse bindings can be triggered during save loading @@ -166,6 +169,7 @@ Feature #5814: Bsatool should be able to create BSA archives, not only to extract it Feature #5828: Support more than 8 lights 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 #6034: Find path based on area cost depending on NPC stats Task #5480: Drop Qt4 support diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index cfe709e97..25528f21d 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -38,9 +38,13 @@ Editor Bug Fixes: - 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) - 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) - Flicker and crashing on XFCE4 fixed (#5703) - 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) Miscellaneous: - Prevent save-game bloating by using an appropriate fog texture format (#5108) From 356efa15a2bcfc2d57f002ba8f424acb4a111846 Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Wed, 12 May 2021 13:33:36 +0200 Subject: [PATCH 07/10] Fixes #6035 (circle brush selects outside of circle) and #6036 (some corner vertices not selected). --- apps/opencs/view/render/terrainshapemode.cpp | 30 ++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 418655f2f..c1b20da4c 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -433,7 +433,9 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair float smoothedByDistance = 0.0f; if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = calculateBumpShape(distance, r, mTotalDiffY); 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_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) @@ -1036,10 +1038,25 @@ void CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int glob return; int selectionX = globalSelectionX; int selectionY = globalSelectionY; - if (xIsAtCellBorder) + + if (xIsAtCellBorder && yIsAtCellBorder) + { + 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--; - if (yIsAtCellBorder) + } + else if (yIsAtCellBorder) + { selectionY--; + } + if (isInCellSelection(selectionX, selectionY)) selections->emplace_back(globalSelectionX, globalSelectionY); } @@ -1074,8 +1091,11 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& { int distanceX = abs(i - vertexCoords.first); int distanceY = abs(j - vertexCoords.second); - int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); - if (distance <= r) handleSelection(i, j, &selections); + float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2)); + + // Using floating-point radius here to prevent selecting too few vertices. + if (distance <= mBrushSize / 2.0f) + handleSelection(i, j, &selections); } } } From d500507dec6db05e0f458c3b2527f4c74363232d Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Tue, 18 May 2021 15:05:19 +0200 Subject: [PATCH 08/10] Resolved merge conflicts with changelog (cont.) --- CHANGELOG.md | 2 ++ CHANGELOG_PR.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 632e02309..e11d05d3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,6 +128,8 @@ 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 #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 #6047: Mouse bindings can be triggered during save loading Feature #390: 3rd person look "over the shoulder" diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 25528f21d..100bc376f 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -45,6 +45,8 @@ Editor Bug Fixes: - 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: - Prevent save-game bloating by using an appropriate fog texture format (#5108) From ca80aeaaeac1b90736c107eb8d1dda320f18f4d1 Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Thu, 13 May 2021 01:21:24 +0200 Subject: [PATCH 09/10] Fix vertex calculation for cell-border drawing. --- apps/opencs/view/render/cellborder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index a1ae4f452..674572232 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -12,7 +12,7 @@ #include "../../model/world/cellcoordinates.hpp" const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE; -const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3; +const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 4; CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords) @@ -61,7 +61,7 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); x = 0; - for (/* */; y >= 0; --y) + for (/* */; y > 0; --y) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); mBorderGeometry->setVertexArray(vertices); @@ -74,7 +74,7 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) // Primitive osg::ref_ptr primitives = - new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount); + new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount + 1); for (size_t i = 0; i < VertexCount; ++i) primitives->setElement(i, i); From 3a1243a5d074cf5861886a6bad94103c276a619c Mon Sep 17 00:00:00 2001 From: Atahualpa Date: Tue, 18 May 2021 17:53:00 +0200 Subject: [PATCH 10/10] Rebased branch, reduced code duplication, added comments, adjusted formatting. --- apps/opencs/view/render/cellborder.cpp | 22 +- apps/opencs/view/render/commands.hpp | 18 +- apps/opencs/view/render/terrainselection.cpp | 216 +++++++++---------- apps/opencs/view/render/terrainselection.hpp | 10 + apps/opencs/view/render/terrainshapemode.cpp | 14 ++ 5 files changed, 150 insertions(+), 130 deletions(-) diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index 674572232..d8ff63801 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -12,6 +12,11 @@ #include "../../model/world/cellcoordinates.hpp" const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE; + +/* + 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; @@ -43,42 +48,45 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) mBaseNode->removeChild(mBorderGeometry); mBorderGeometry = new osg::Geometry(); - // Vertices osg::ref_ptr vertices = new osg::Vec3Array(); int x = 0; int y = 0; - for (/* */; x < ESM::Land::LAND_SIZE - 1; ++x) + /* + 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)])); x = ESM::Land::LAND_SIZE - 1; - for (/* */; y < ESM::Land::LAND_SIZE - 1; ++y) + for (; y < ESM::Land::LAND_SIZE - 1; ++y) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); 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)])); x = 0; - for (/* */; y > 0; --y) + for (; y > 0; --y) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); mBorderGeometry->setVertexArray(vertices); - // Color osg::ref_ptr colors = new osg::Vec4Array(); colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f)); mBorderGeometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET); - // Primitive osg::ref_ptr primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount + 1); + // Assign one primitive to each vertex. for (size_t i = 0; i < VertexCount; ++i) primitives->setElement(i, i); + // Assign the last primitive to the first vertex to close the loop. primitives->setElement(VertexCount, 0); mBorderGeometry->addPrimitiveSet(primitives); diff --git a/apps/opencs/view/render/commands.hpp b/apps/opencs/view/render/commands.hpp index 545d8071f..cdc389e33 100644 --- a/apps/opencs/view/render/commands.hpp +++ b/apps/opencs/view/render/commands.hpp @@ -3,16 +3,22 @@ #include -namespace ESM -{ - struct Land; -} - 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: diff --git a/apps/opencs/view/render/terrainselection.cpp b/apps/opencs/view/render/terrainselection.cpp index be7fefe30..0593917e0 100644 --- a/apps/opencs/view/render/terrainselection.cpp +++ b/apps/opencs/view/render/terrainselection.cpp @@ -39,135 +39,23 @@ std::vector> CSVRender::TerrainSelection::getTerrainSelectio void CSVRender::TerrainSelection::onlySelect(const std::vector> &localPositions) { mSelection = localPositions; + update(); } void CSVRender::TerrainSelection::addSelect(const std::vector>& 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.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.emplace_back(localPos); - } - } - } - else - { - mDraggedOperationFlag = false; - mTemporarySelection.clear(); - } - update(); + handleSelection(localPositions, toggleInProgress, SelectionMethod::AddSelect); } void CSVRender::TerrainSelection::removeSelect(const std::vector>& 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); - } - } - - 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 - { - mDraggedOperationFlag = false; - mTemporarySelection.clear(); - } - update(); + handleSelection(localPositions, toggleInProgress, SelectionMethod::RemoveSelect); } -void CSVRender::TerrainSelection::toggleSelect(const std::vector> &localPositions, bool toggleInProgress) +void CSVRender::TerrainSelection::toggleSelect(const std::vector>& 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(); + handleSelection(localPositions, toggleInProgress, SelectionMethod::ToggleSelect); } void CSVRender::TerrainSelection::activate() @@ -310,6 +198,100 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr>& 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) { CSMDoc::Document& document = mWorldspaceWidget->getDocument(); diff --git a/apps/opencs/view/render/terrainselection.hpp b/apps/opencs/view/render/terrainselection.hpp index 9c1eb55b1..18622ad13 100644 --- a/apps/opencs/view/render/terrainselection.hpp +++ b/apps/opencs/view/render/terrainselection.hpp @@ -27,6 +27,14 @@ namespace CSVRender Shape }; + enum class SelectionMethod + { + OnlySelect, + AddSelect, + RemoveSelect, + ToggleSelect + }; + /// \brief Class handling the terrain selection data and rendering class TerrainSelection { @@ -56,6 +64,8 @@ namespace CSVRender private: + void handleSelection(const std::vector>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod); + bool noCell(const std::string& cellId); bool noLand(const std::string& cellId); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index c1b20da4c..866ff69cd 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -285,6 +285,8 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() sortAndLimitAlteredCells(); 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) @@ -355,7 +357,9 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } 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(); clearTransientEdits(); } @@ -1039,8 +1043,18 @@ void CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int glob int selectionX = globalSelectionX; int selectionY = globalSelectionY; + /* + 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))