From 9645d0cc8aeab969575d413213725ad58b42861e Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 12 May 2016 18:21:43 -0400 Subject: [PATCH 01/37] Pass more mouse pick information in scene view editor. --- apps/opencs/view/render/editmode.cpp | 16 ++--- apps/opencs/view/render/editmode.hpp | 17 +++--- apps/opencs/view/render/instancemode.cpp | 30 +++++----- apps/opencs/view/render/instancemode.hpp | 12 ++-- .../view/render/pagedworldspacewidget.cpp | 9 +-- .../view/render/pagedworldspacewidget.hpp | 2 +- apps/opencs/view/render/worldspacewidget.cpp | 58 ++++++++++++------- apps/opencs/view/render/worldspacewidget.hpp | 13 ++++- 8 files changed, 92 insertions(+), 65 deletions(-) diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index be264afc7..8da000dc9 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -29,30 +29,30 @@ void CSVRender::EditMode::setEditLock (bool locked) } -void CSVRender::EditMode::primaryEditPressed (osg::ref_ptr tag) {} +void CSVRender::EditMode::primaryEditPressed (const WorldspaceHitResult& hit) {} -void CSVRender::EditMode::secondaryEditPressed (osg::ref_ptr tag) {} +void CSVRender::EditMode::secondaryEditPressed (const WorldspaceHitResult& hit) {} -void CSVRender::EditMode::primarySelectPressed (osg::ref_ptr tag) {} +void CSVRender::EditMode::primarySelectPressed (const WorldspaceHitResult& hit) {} -void CSVRender::EditMode::secondarySelectPressed (osg::ref_ptr tag) {} +void CSVRender::EditMode::secondarySelectPressed (const WorldspaceHitResult& hit) {} -bool CSVRender::EditMode::primaryEditStartDrag (osg::ref_ptr tag) +bool CSVRender::EditMode::primaryEditStartDrag (const WorldspaceHitResult& hit) { return false; } -bool CSVRender::EditMode::secondaryEditStartDrag (osg::ref_ptr tag) +bool CSVRender::EditMode::secondaryEditStartDrag (const WorldspaceHitResult& hit) { return false; } -bool CSVRender::EditMode::primarySelectStartDrag (osg::ref_ptr tag) +bool CSVRender::EditMode::primarySelectStartDrag (const WorldspaceHitResult& hit) { return false; } -bool CSVRender::EditMode::secondarySelectStartDrag (osg::ref_ptr tag) +bool CSVRender::EditMode::secondarySelectStartDrag (const WorldspaceHitResult& hit) { return false; } diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index f5cef1be2..3381a7105 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -12,6 +12,7 @@ class QDragMoveEvent; namespace CSVRender { class WorldspaceWidget; + struct WorldspaceHitResult; class TagBase; class EditMode : public CSVWidget::ModeButton @@ -38,36 +39,36 @@ namespace CSVRender virtual void setEditLock (bool locked); /// Default-implementation: Ignored. - virtual void primaryEditPressed (osg::ref_ptr tag); + virtual void primaryEditPressed (const WorldspaceHitResult& hit); /// Default-implementation: Ignored. - virtual void secondaryEditPressed (osg::ref_ptr tag); + virtual void secondaryEditPressed (const WorldspaceHitResult& hit); /// Default-implementation: Ignored. - virtual void primarySelectPressed (osg::ref_ptr tag); + virtual void primarySelectPressed (const WorldspaceHitResult& hit); /// Default-implementation: Ignored. - virtual void secondarySelectPressed (osg::ref_ptr tag); + virtual void secondarySelectPressed (const WorldspaceHitResult& hit); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool primaryEditStartDrag (osg::ref_ptr tag); + virtual bool primaryEditStartDrag (const WorldspaceHitResult& hit); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool secondaryEditStartDrag (osg::ref_ptr tag); + virtual bool secondaryEditStartDrag (const WorldspaceHitResult& hit); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool primarySelectStartDrag (osg::ref_ptr tag); + virtual bool primarySelectStartDrag (const WorldspaceHitResult& hit); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool secondarySelectStartDrag (osg::ref_ptr tag); + virtual bool secondarySelectStartDrag (const WorldspaceHitResult& hit); /// Default-implementation: ignored virtual void drag (int diffX, int diffY, double speedFactor); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4c708242a..52335f244 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -103,25 +103,25 @@ void CSVRender::InstanceMode::setEditLock (bool locked) getWorldspaceWidget().abortDrag(); } -void CSVRender::InstanceMode::primaryEditPressed (osg::ref_ptr tag) +void CSVRender::InstanceMode::primaryEditPressed (const WorldspaceHitResult& hit) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) - primarySelectPressed (tag); + primarySelectPressed (hit); } -void CSVRender::InstanceMode::secondaryEditPressed (osg::ref_ptr tag) +void CSVRender::InstanceMode::secondaryEditPressed (const WorldspaceHitResult& hit) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) - secondarySelectPressed (tag); + secondarySelectPressed (hit); } -void CSVRender::InstanceMode::primarySelectPressed (osg::ref_ptr tag) +void CSVRender::InstanceMode::primarySelectPressed (const WorldspaceHitResult& hit) { getWorldspaceWidget().clearSelection (Mask_Reference); - if (tag) + if (hit.tag) { - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) { // hit an Object, select it CSVRender::Object* object = objectTag->mObject; @@ -131,11 +131,11 @@ void CSVRender::InstanceMode::primarySelectPressed (osg::ref_ptr tag) } } -void CSVRender::InstanceMode::secondarySelectPressed (osg::ref_ptr tag) +void CSVRender::InstanceMode::secondarySelectPressed (const WorldspaceHitResult& hit) { - if (tag) + if (hit.tag) { - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) { // hit an Object, toggle its selection state CSVRender::Object* object = objectTag->mObject; @@ -145,15 +145,15 @@ void CSVRender::InstanceMode::secondarySelectPressed (osg::ref_ptr tag) } } -bool CSVRender::InstanceMode::primaryEditStartDrag (osg::ref_ptr tag) +bool CSVRender::InstanceMode::primaryEditStartDrag (const WorldspaceHitResult& hit) { if (mDragMode!=DragMode_None || mLocked) return false; - if (tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) + if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { getWorldspaceWidget().clearSelection (Mask_Reference); - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) { CSVRender::Object* object = objectTag->mObject; object->setSelected (true); @@ -177,7 +177,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (osg::ref_ptr tag) // \todo check for sub-mode - if (CSVRender::ObjectMarkerTag *objectTag = dynamic_cast (tag.get())) + if (CSVRender::ObjectMarkerTag *objectTag = dynamic_cast (hit.tag.get())) { mDragAxis = objectTag->mAxis; } @@ -189,7 +189,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (osg::ref_ptr tag) return true; } -bool CSVRender::InstanceMode::secondaryEditStartDrag (osg::ref_ptr tag) +bool CSVRender::InstanceMode::secondaryEditStartDrag (const WorldspaceHitResult& hit) { if (mLocked) return false; diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index db7fdaa96..5b1252912 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -41,17 +41,17 @@ namespace CSVRender virtual void setEditLock (bool locked); - virtual void primaryEditPressed (osg::ref_ptr tag); + virtual void primaryEditPressed (const WorldspaceHitResult& hit); - virtual void secondaryEditPressed (osg::ref_ptr tag); + virtual void secondaryEditPressed (const WorldspaceHitResult& hit); - virtual void primarySelectPressed (osg::ref_ptr tag); + virtual void primarySelectPressed (const WorldspaceHitResult& hit); - virtual void secondarySelectPressed (osg::ref_ptr tag); + virtual void secondarySelectPressed (const WorldspaceHitResult& hit); - virtual bool primaryEditStartDrag (osg::ref_ptr tag); + virtual bool primaryEditStartDrag (const WorldspaceHitResult& hit); - virtual bool secondaryEditStartDrag (osg::ref_ptr tag); + virtual bool secondaryEditStartDrag (const WorldspaceHitResult& hit); virtual void drag (int diffX, int diffY, double speedFactor); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 1d9b22216..90088f24e 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -142,14 +142,15 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( "terrain-move"); } -void CSVRender::PagedWorldspaceWidget::handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift) +void CSVRender::PagedWorldspaceWidget::handleMouseClick (const WorldspaceHitResult& hit, const std::string& button, + bool shift) { - if (tag && tag->getMask()==Mask_CellArrow) + if (hit.tag && hit.tag->getMask()==Mask_CellArrow) { if (button=="p-edit" || button=="s-edit") { if (CellArrowTag *cellArrowTag = - dynamic_cast (tag.get())) + dynamic_cast (hit.tag.get())) { CellArrow *arrow = cellArrowTag->getCellArrow(); @@ -209,7 +210,7 @@ void CSVRender::PagedWorldspaceWidget::handleMouseClick (osg::ref_ptr t } } - WorldspaceWidget::handleMouseClick (tag, button, shift); + WorldspaceWidget::handleMouseClick (hit, button, shift); } void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 3cd628df0..8c17d4fcd 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -135,7 +135,7 @@ namespace CSVRender virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool); - virtual void handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift); + virtual void handleMouseClick (const WorldspaceHitResult& hit, const std::string& button, bool shift); signals: diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 1bd3df981..e90a4fd5a 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -469,7 +469,7 @@ bool CSVRender::WorldspaceWidget::storeMappingSetting (const CSMPrefs::Setting * return SceneWidget::storeMappingSetting(setting); } -osg::ref_ptr CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos) +CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos) { // (0,0) is considered the lower left corner of an OpenGL window int x = localPos.x(); @@ -499,16 +499,31 @@ osg::ref_ptr CSVRender::WorldspaceWidget::mousePick (const Q { osg::Node* node = *it; if (osg::ref_ptr tag = dynamic_cast(node->getUserData())) - return tag; + { + WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() }; + if (intersection.indexList.size() >= 3) + { + hit.i0 = intersection.indexList[0]; + hit.i1 = intersection.indexList[1]; + hit.i2 = intersection.indexList[2]; + } + return hit; + } } -// ignoring terrain for now - // must be terrain, report coordinates -// std::cout << "Terrain hit at " << intersection.getWorldIntersectPoint().x() << " " << intersection.getWorldIntersectPoint().y() << std::endl; -// return; + // Something untagged, probably terrain + WorldspaceHitResult hit = { true, 0, 0, 0, 0, intersection.getWorldIntersectPoint() }; + if (intersection.indexList.size() >= 3) + { + hit.i0 = intersection.indexList[0]; + hit.i1 = intersection.indexList[1]; + hit.i2 = intersection.indexList[2]; + } + return hit; } - return osg::ref_ptr(); + WorldspaceHitResult hit = { false, 0, 0, 0, 0, osg::Vec3d() }; + return hit; } void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event) @@ -595,10 +610,11 @@ void CSVRender::WorldspaceWidget::showToolTip() { QPoint pos = QCursor::pos(); - if (osg::ref_ptr tag = mousePick (mapFromGlobal (pos))) + WorldspaceHitResult hit = mousePick (mapFromGlobal (pos)); + if (hit.tag) { bool hideBasics = CSMPrefs::get()["Tooltips"]["scene-hide-basic"].isTrue(); - QToolTip::showText (pos, tag->getToolTip (hideBasics), this); + QToolTip::showText (pos, hit.tag->getToolTip (hideBasics), this); } } } @@ -635,18 +651,18 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) } else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="p-select" || mDragMode=="s-select") { - osg::ref_ptr tag = mousePick (event->pos()); + WorldspaceHitResult hit = mousePick (event->pos()); EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); if (mDragMode=="p-edit") - mDragging = editMode.primaryEditStartDrag (tag); + mDragging = editMode.primaryEditStartDrag (hit); else if (mDragMode=="s-edit") - mDragging = editMode.secondaryEditStartDrag (tag); + mDragging = editMode.secondaryEditStartDrag (hit); else if (mDragMode=="p-select") - mDragging = editMode.primarySelectStartDrag (tag); + mDragging = editMode.primarySelectStartDrag (hit); else if (mDragMode=="s-select") - mDragging = editMode.secondarySelectStartDrag (tag); + mDragging = editMode.secondarySelectStartDrag (hit); if (mDragging) { @@ -704,9 +720,9 @@ void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event) } else { - osg::ref_ptr tag = mousePick (event->pos()); + WorldspaceHitResult hit = mousePick (event->pos()); - handleMouseClick (tag, button, event->modifiers() & Qt::ShiftModifier); + handleMouseClick (hit, button, event->modifiers() & Qt::ShiftModifier); } } else @@ -740,18 +756,18 @@ void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event) SceneWidget::keyPressEvent(event); } -void CSVRender::WorldspaceWidget::handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift) +void CSVRender::WorldspaceWidget::handleMouseClick (const WorldspaceHitResult& hit, const std::string& button, bool shift) { EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); if (button=="p-edit") - editMode.primaryEditPressed (tag); + editMode.primaryEditPressed (hit); else if (button=="s-edit") - editMode.secondaryEditPressed (tag); + editMode.secondaryEditPressed (hit); else if (button=="p-select") - editMode.primarySelectPressed (tag); + editMode.primarySelectPressed (hit); else if (button=="s-select") - editMode.secondarySelectPressed (tag); + editMode.secondarySelectPressed (hit); } CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode() diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index d70694d22..4160da3d8 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" @@ -35,6 +36,14 @@ namespace CSVRender class CellArrow; class EditMode; + struct WorldspaceHitResult + { + bool hit; + osg::ref_ptr tag; + unsigned int i0, i1, i2; + osg::Vec3d worldPos; + }; + class WorldspaceWidget : public SceneWidget { Q_OBJECT @@ -191,7 +200,7 @@ namespace CSVRender virtual void wheelEvent (QWheelEvent *event); virtual void keyPressEvent (QKeyEvent *event); - virtual void handleMouseClick (osg::ref_ptr tag, const std::string& button, + virtual void handleMouseClick (const WorldspaceHitResult& hit, const std::string& button, bool shift); /// \return Is \a key a button mapping setting? (ignored otherwise) @@ -209,7 +218,7 @@ namespace CSVRender void dragMoveEvent(QDragMoveEvent *event); - osg::ref_ptr mousePick (const QPoint& localPos); + WorldspaceHitResult mousePick (const QPoint& localPos); virtual std::string getStartupInstruction() = 0; From 32ba5bf8b87bb25eb09b02ff8eca4c996a22e134 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 12 May 2016 19:35:49 -0400 Subject: [PATCH 02/37] Initial pathgrid editting, support for selection and movement. --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/view/render/cell.cpp | 50 ++-- apps/opencs/view/render/cell.hpp | 6 +- apps/opencs/view/render/pathgrid.cpp | 287 +++++++++++++++++++ apps/opencs/view/render/pathgrid.hpp | 105 +++++++ apps/opencs/view/render/pathgridmode.cpp | 152 ++++++++++ apps/opencs/view/render/pathgridmode.hpp | 53 ++++ apps/opencs/view/render/worldspacewidget.cpp | 8 +- components/sceneutil/pathgridutil.cpp | 77 +++++ components/sceneutil/pathgridutil.hpp | 5 + 10 files changed, 707 insertions(+), 40 deletions(-) create mode 100644 apps/opencs/view/render/pathgrid.cpp create mode 100644 apps/opencs/view/render/pathgrid.hpp create mode 100644 apps/opencs/view/render/pathgridmode.cpp create mode 100644 apps/opencs/view/render/pathgridmode.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index e7312d0ba..b33dc1508 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -86,12 +86,12 @@ opencs_units (view/widget opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode - orbitcameramode + orbitcameramode pathgridmode ) opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder cameracontroller + cellarrow cellmarker cellborder cameracontroller pathgrid ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 691b909bb..a8e36728c 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -17,6 +17,7 @@ #include "../../model/world/cellcoordinates.hpp" #include "mask.hpp" +#include "pathgrid.hpp" #include "terrainstorage.hpp" bool CSVRender::Cell::removeObject (const std::string& id) @@ -68,18 +69,6 @@ bool CSVRender::Cell::addObjects (int start, int end) return modified; } -void CSVRender::Cell::recreatePathgrid() -{ - const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); - int pathgridIndex = pathgrids.searchId(mId); - if (pathgridIndex != -1) - { - mPathgridGeode->removeDrawable(mPathgridGeometry); - mPathgridGeometry = SceneUtil::createPathgridGeometry(pathgrids.getRecord(pathgridIndex).get()); - mPathgridGeode->addDrawable(mPathgridGeometry); - } -} - CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), @@ -93,17 +82,6 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st mCellNode = new osg::Group; rootNode->addChild(mCellNode); - osg::ref_ptr pathgridTransform = new osg::PositionAttitudeTransform(); - pathgridTransform->setPosition(osg::Vec3f(mCoordinates.getX() * ESM::Land::REAL_SIZE, - mCoordinates.getY() * ESM::Land::REAL_SIZE, 0)); - pathgridTransform->setNodeMask(Mask_Pathgrid); - mCellNode->addChild(pathgridTransform); - - mPathgridGeode = new osg::Geode(); - pathgridTransform->addChild(mPathgridGeode); - - mPathgridGeometry = 0; - setCellMarker(); if (!mDeleted) @@ -132,7 +110,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st } } - recreatePathgrid(); + mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); } } @@ -285,27 +263,27 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int void CSVRender::Cell::pathgridAdded(const CSMWorld::Pathgrid& pathgrid) { - recreatePathgrid(); + mPathgrid->recreateGeometry(); } void CSVRender::Cell::pathgridRemoved() { - mPathgridGeode->removeDrawable(mPathgridGeometry); + mPathgrid->recreateGeometry(); } void CSVRender::Cell::pathgridDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { - recreatePathgrid(); + mPathgrid->recreateGeometry(); } void CSVRender::Cell::pathgridRowRemoved(const QModelIndex& parent, int start, int end) { - recreatePathgrid(); + mPathgrid->recreateGeometry(); } void CSVRender::Cell::pathgridRowAdded(const QModelIndex& parent, int start, int end) { - recreatePathgrid(); + mPathgrid->recreateGeometry(); } void CSVRender::Cell::setSelection (int elementMask, Selection mode) @@ -327,6 +305,15 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode) iter->second->setSelected (selected); } } + if (elementMask & Mask_Pathgrid) + { + switch (mode) + { + case Selection_Clear: mPathgrid->clearSelected(); break; + case Selection_All: mPathgrid->selectAll(); break; + case Selection_Invert: mPathgrid->invertSelected(); break; + } + } } void CSVRender::Cell::selectAllWithSameParentId (int elementMask) @@ -406,6 +393,9 @@ std::vector > CSVRender::Cell::getSelection (un iter!=mObjects.end(); ++iter) if (iter->second->getSelected()) result.push_back (iter->second->getTag()); + if (elementMask & Mask_Pathgrid) + if (mPathgrid->isSelected()) + result.push_back(mPathgrid->getTag()); return result; } @@ -440,4 +430,6 @@ void CSVRender::Cell::reset (unsigned int elementMask) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) iter->second->reset(); + if (elementMask & Mask_Pathgrid) + mPathgrid->resetMove(); } diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index e265fc21c..caf6c59a9 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -36,6 +36,7 @@ namespace CSMWorld namespace CSVRender { + class Pathgrid; class TagBase; class Cell @@ -43,14 +44,13 @@ namespace CSVRender CSMWorld::Data& mData; std::string mId; osg::ref_ptr mCellNode; - osg::ref_ptr mPathgridGeode; - osg::ref_ptr mPathgridGeometry; std::map mObjects; std::auto_ptr mTerrain; CSMWorld::CellCoordinates mCoordinates; std::auto_ptr mCellArrows[4]; std::auto_ptr mCellMarker; std::auto_ptr mCellBorder; + std::auto_ptr mPathgrid; bool mDeleted; int mSubMode; unsigned int mSubModeElementMask; @@ -69,8 +69,6 @@ namespace CSVRender /// \return Have any objects been added? bool addObjects (int start, int end); - void recreatePathgrid(); - public: enum Selection diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp new file mode 100644 index 000000000..4aa254a44 --- /dev/null +++ b/apps/opencs/view/render/pathgrid.cpp @@ -0,0 +1,287 @@ +#include "pathgrid.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +#include "../../model/world/commands.hpp" +#include "../../model/world/commandmacro.hpp" +#include "../../model/world/data.hpp" + +namespace CSVRender +{ + PathgridTag::PathgridTag(Pathgrid* pathgrid) + : TagBase(Mask_Pathgrid), mPathgrid(pathgrid) + { + } + + Pathgrid* PathgridTag::getPathgrid() const + { + return mPathgrid; + } + + QString PathgridTag::getToolTip(bool hideBasics) const + { + QString text("Pathgrid: "); + text += mPathgrid->getId().c_str(); + + if (!hideBasics) + { + text += "

Only one pathgrid may be edited at a time."; + } + + return text; + } + + Pathgrid::Pathgrid(CSMWorld::Data& data, osg::Group* parent, const std::string& pathgridId, + const CSMWorld::CellCoordinates& coordinates) + : mData(data) + , mId(pathgridId) + , mCoords(coordinates) + , mParent(parent) + , mPathgridGeometry(0) + , mSelectedGeometry(0) + , mTag(new PathgridTag(this)) + { + const float CoordScalar = ESM::Land::REAL_SIZE; + + mBaseNode = new osg::PositionAttitudeTransform (); + mBaseNode->setPosition(osg::Vec3f(mCoords.getX() * CoordScalar, mCoords.getY() * CoordScalar, 0.f)); + mBaseNode->setUserData(mTag); + mBaseNode->setNodeMask(Mask_Pathgrid); + mParent->addChild(mBaseNode); + + mSelectedNode = new osg::PositionAttitudeTransform(); + mBaseNode->addChild(mSelectedNode); + + mPathgridGeode = new osg::Geode(); + mBaseNode->addChild(mPathgridGeode); + + mSelectedGeode = new osg::Geode(); + mSelectedNode->addChild(mSelectedGeode); + + recreateGeometry(); + } + + Pathgrid::~Pathgrid() + { + mParent->removeChild(mBaseNode); + } + + const CSMWorld::CellCoordinates& Pathgrid::getCoordinates() const + { + return mCoords; + } + + const std::string& Pathgrid::getId() const + { + return mId; + } + + bool Pathgrid::isSelected() const + { + return !mSelected.empty(); + } + + const Pathgrid::NodeList& Pathgrid::getSelected() const + { + return mSelected; + } + + void Pathgrid::selectAll() + { + mSelected.clear(); + + const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); + int pathgridIndex = pathgrids.searchId(mId); + if (pathgridIndex != -1) + { + const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); + for (unsigned short i = 0; i < static_cast(source.mPoints.size()); ++i) + mSelected.push_back(i); + + recreateSelectedGeometry(source); + } + else + { + removeSelectedGeometry(); + } + } + + void Pathgrid::toggleSelected(unsigned short node) + { + NodeList::iterator searchResult = std::find(mSelected.begin(), mSelected.end(), node); + if (searchResult != mSelected.end()) + { + mSelected.erase(searchResult); + } + else + { + mSelected.push_back(node); + } + + recreateSelectedGeometry(); + } + + void Pathgrid::invertSelected() + { + NodeList temp = NodeList(mSelected.begin(), mSelected.end()); + mSelected.clear(); + + const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); + int pathgridIndex = pathgrids.searchId(mId); + if (pathgridIndex != -1) + { + const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); + for (unsigned short i = 0; i < static_cast(source.mPoints.size()); ++i) + { + if (std::find(temp.begin(), temp.end(), i) == temp.end()) + mSelected.push_back(i); + } + + recreateSelectedGeometry(source); + } + else + { + removeSelectedGeometry(); + } + } + + void Pathgrid::clearSelected() + { + mSelected.clear(); + removeSelectedGeometry(); + } + + void Pathgrid::moveSelected(const osg::Vec3d& offset) + { + mSelectedNode->setPosition(mSelectedNode->getPosition() + offset); + } + + void Pathgrid::resetMove() + { + mSelectedNode->setPosition(osg::Vec3f(0,0,0)); + } + + void Pathgrid::applyPosition(CSMWorld::CommandMacro& commands) + { + const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); + int pathgridIndex = pathgrids.searchId(mId); + if (pathgridIndex != -1) + { + const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); + osg::Vec3d localCoords = mSelectedNode->getPosition(); + + int oX = static_cast(localCoords.x()); + int oY = static_cast(localCoords.y()); + int oZ = static_cast(localCoords.z()); + + CSMWorld::SubCellCollection& collection = mData.getPathgrids(); + QAbstractItemModel* model = mData.getTableModel (CSMWorld::UniversalId::Type_Pathgrids); + + int recordIndex = collection.getIndex (mId); + int parentColumn = collection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + + QModelIndex parent = model->index(recordIndex, parentColumn); + + for (size_t i = 0; i < mSelected.size(); ++i) + { + const CSMWorld::Pathgrid::Point& point = source.mPoints[mSelected[i]]; + int row = mSelected[i]; + + // X + int column = collection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosX); + commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, column, parent), point.mX + oX)); + + // Y + column = collection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosY); + commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, column, parent), point.mY + oY)); + + // Z + column = collection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); + commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, column, parent), point.mZ + oZ)); + } + } + + resetMove(); + } + + void Pathgrid::applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2) + { + // TODO + } + + void Pathgrid::applyEdges(CSMWorld::CommandMacro& commands, unsigned short node) + { + // TODO + } + + osg::ref_ptr Pathgrid::getTag() const + { + return mTag; + } + + void Pathgrid::recreateGeometry() + { + removePathgridGeometry(); + removeSelectedGeometry(); + + // Make new + const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); + int pathgridIndex = pathgrids.searchId(mId); + if (pathgridIndex != -1) + { + const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); + mPathgridGeometry = SceneUtil::createPathgridGeometry(source); + mPathgridGeode->addDrawable(mPathgridGeometry); + + recreateSelectedGeometry(source); + } + } + + void Pathgrid::recreateSelectedGeometry() + { + const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); + int pathgridIndex = pathgrids.searchId(mId); + if (pathgridIndex != -1) + { + const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); + recreateSelectedGeometry(source); + } + else + { + removeSelectedGeometry(); + } + } + + void Pathgrid::recreateSelectedGeometry(const CSMWorld::Pathgrid& source) + { + removeSelectedGeometry(); + mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, mSelected); + mSelectedGeode->addDrawable(mSelectedGeometry); + } + + void Pathgrid::removePathgridGeometry() + { + if (mPathgridGeometry) + { + mPathgridGeode->removeDrawable(mPathgridGeometry); + mPathgridGeometry.release(); + } + } + + void Pathgrid::removeSelectedGeometry() + { + if (mSelectedGeometry) + { + mSelectedGeode->removeDrawable(mSelectedGeometry); + mSelectedGeometry.release(); + } + } +} diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp new file mode 100644 index 000000000..c1a8f9308 --- /dev/null +++ b/apps/opencs/view/render/pathgrid.hpp @@ -0,0 +1,105 @@ +#ifndef CSV_RENDER_PATHGRID_H +#define CSV_RENDER_PATHGRID_H + +#include + +#include +#include + +#include "../../model/world/cellcoordinates.hpp" + +#include "tagbase.hpp" + +namespace osg +{ + class Geode; + class Geometry; + class Group; + class PositionAttitudeTransform; + class Vec3d; +} + +namespace CSMWorld +{ + class CommandMacro; + class Data; + class Pathgrid; +} + +namespace CSVRender +{ + class Pathgrid; + + class PathgridTag : public TagBase + { + public: + + PathgridTag (Pathgrid* pathgrid); + + Pathgrid* getPathgrid () const; + + virtual QString getToolTip (bool hideBasics) const; + + private: + + Pathgrid* mPathgrid; + }; + + class Pathgrid + { + public: + + typedef std::vector NodeList; + + Pathgrid(CSMWorld::Data& data, osg::Group* parent, const std::string& pathgridId, + const CSMWorld::CellCoordinates& coordinates); + + ~Pathgrid(); + + const CSMWorld::CellCoordinates& getCoordinates() const; + const std::string& getId() const; + + bool isSelected() const; + const NodeList& getSelected() const; + void selectAll(); + void toggleSelected(unsigned short node); // Adds to end of vector + void invertSelected(); + void clearSelected(); + + void moveSelected(const osg::Vec3d& offset); + void resetMove(); + + void applyPosition(CSMWorld::CommandMacro& commands); + void applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2); + void applyEdges(CSMWorld::CommandMacro& commands, unsigned short node); + + osg::ref_ptr getTag() const; + + void recreateGeometry(); + + private: + + CSMWorld::Data& mData; + std::string mId; + CSMWorld::CellCoordinates mCoords; + + NodeList mSelected; + + osg::Group* mParent; + osg::ref_ptr mBaseNode; + osg::ref_ptr mSelectedNode; + osg::ref_ptr mPathgridGeode; + osg::ref_ptr mSelectedGeode; + osg::ref_ptr mPathgridGeometry; + osg::ref_ptr mSelectedGeometry; + + osg::ref_ptr mTag; + + void recreateSelectedGeometry(); + void recreateSelectedGeometry(const CSMWorld::Pathgrid& source); + void removePathgridGeometry(); + void removeSelectedGeometry(); + }; +} + +#endif diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp new file mode 100644 index 000000000..93b6a747d --- /dev/null +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -0,0 +1,152 @@ +#include "pathgridmode.hpp" + +#include + +#include "../../model/world/commands.hpp" +#include "../../model/world/commandmacro.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/idtree.hpp" + +#include "mask.hpp" +#include "pathgrid.hpp" +#include "worldspacewidget.hpp" + +namespace CSVRender +{ + PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent) + : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid, "Pathgrid editing", parent) + , mDragMode(DragMode_None) + , mFromNode(0) + { + } + + void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hit) + { + } + + void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit) + { + } + + void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit) + { + getWorldspaceWidget().clearSelection(Mask_Pathgrid); + + if (hit.tag) + { + if (PathgridTag* tag = dynamic_cast(hit.tag.get())) + { + unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.i0)); + tag->getPathgrid()->toggleSelected(node); + } + } + } + + void PathgridMode::secondarySelectPressed(const WorldspaceHitResult& hit) + { + if (hit.tag) + { + if (PathgridTag* tag = dynamic_cast(hit.tag.get())) + { + if (tag->getPathgrid()->getId() != mLastId) + { + getWorldspaceWidget().clearSelection(Mask_Pathgrid); + mLastId = tag->getPathgrid()->getId(); + } + + unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.i0)); + tag->getPathgrid()->toggleSelected(node); + + return; + } + } + + getWorldspaceWidget().clearSelection(Mask_Pathgrid); + } + + bool PathgridMode::primaryEditStartDrag(const WorldspaceHitResult& hit) + { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + if (!selection.empty()) + { + mDragMode = DragMode_Move; + return true; + } + else + { + return false; + } + } + + bool PathgridMode::secondaryEditStartDrag(const WorldspaceHitResult& hit) + { + if (hit.tag) + { + if (PathgridTag* tag = dynamic_cast(hit.tag.get())) + { + mDragMode = DragMode_Edge; + mFromNode = SceneUtil::getPathgridNode(static_cast(hit.i0)); + return true; + } + } + + return false; + } + + void PathgridMode::drag(int diffX, int diffY, double speedFactor) + { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + { + if (PathgridTag* tag = dynamic_cast(it->get())) + { + if (mDragMode == DragMode_Move) + { + osg::Vec3d eye, center, up, offset; + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, center, up); + + offset = (up * diffY * speedFactor) + (((center - eye) ^ up) * diffX * speedFactor); + + tag->getPathgrid()->moveSelected(offset); + } + else if (mDragMode == DragMode_Edge) + { + // TODO make indicators + } + } + } + } + + void PathgridMode::dragCompleted() + { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + { + if (PathgridTag* tag = dynamic_cast(it->get())) + { + if (mDragMode == DragMode_Move) + { + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Move pathgrid node(s)"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyPosition(macro); + } + else if (mDragMode == DragMode_Edge) + { + // TODO raycast for other node and apply if needed with mFromNode + } + } + } + + mDragMode = DragMode_None; + } + + void PathgridMode::dragAborted() + { + getWorldspaceWidget().reset(Mask_Pathgrid); + } +} diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp new file mode 100644 index 000000000..0a84b6b10 --- /dev/null +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -0,0 +1,53 @@ +#ifndef CSV_RENDER_PATHGRIDMODE_H +#define CSV_RENDER_PATHGRIDMODE_H + +#include + +#include "editmode.hpp" + +namespace CSVRender +{ + class PathgridMode : public EditMode + { + Q_OBJECT + + public: + + PathgridMode(WorldspaceWidget* worldspace, QWidget* parent=0); + + virtual void primaryEditPressed(const WorldspaceHitResult& hit); + + virtual void secondaryEditPressed(const WorldspaceHitResult& hit); + + virtual void primarySelectPressed(const WorldspaceHitResult& hit); + + virtual void secondarySelectPressed(const WorldspaceHitResult& hit); + + virtual bool primaryEditStartDrag (const WorldspaceHitResult& hit); + + virtual bool secondaryEditStartDrag (const WorldspaceHitResult& hit); + + virtual void drag (int diffX, int diffY, double speedFactor); + + virtual void dragCompleted(); + + /// \note dragAborted will not be called, if the drag is aborted via changing + /// editing mode + virtual void dragAborted(); + + private: + + enum DragMode + { + DragMode_None, + DragMode_Move, + DragMode_Edge + }; + + std::string mLastId; + DragMode mDragMode; + unsigned short mFromNode; + }; +} + +#endif diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index e90a4fd5a..5963107e9 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -27,8 +27,8 @@ #include "object.hpp" #include "mask.hpp" -#include "editmode.hpp" #include "instancemode.hpp" +#include "pathgridmode.hpp" #include "cameracontroller.hpp" CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) @@ -295,7 +295,7 @@ unsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const void CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask) { - mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow; + mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow | Mask_Pathgrid | Mask_Terrain; } unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const @@ -320,9 +320,7 @@ void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneTo { /// \todo replace EditMode with suitable subclasses tool->addButton (new InstanceMode (this, tool), "object"); - tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Mask_Pathgrid, "Pathgrid editing"), - "pathgrid"); + tool->addButton (new PathgridMode (this, tool), "pathgrid"); } CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument() diff --git a/components/sceneutil/pathgridutil.cpp b/components/sceneutil/pathgridutil.cpp index 5d71efd1e..5078b1edb 100644 --- a/components/sceneutil/pathgridutil.cpp +++ b/components/sceneutil/pathgridutil.cpp @@ -9,6 +9,7 @@ namespace SceneUtil { const unsigned short DiamondVertexCount = 6; const unsigned short DiamondIndexCount = 24; + const unsigned short DiamondWireframeIndexCount = 24; const unsigned short DiamondConnectorVertexCount = 4; @@ -16,6 +17,7 @@ namespace SceneUtil const float DiamondHalfHeight = 40.f; const float DiamondHalfWidth = 16.f; + const float DiamondWireframeScalar = 1.1f; const osg::Vec3f DiamondPoints[DiamondVertexCount] = { @@ -39,6 +41,22 @@ namespace SceneUtil 5, 2, 4 }; + const unsigned short DiamondWireframeIndices[DiamondWireframeIndexCount] = + { + 0, 1, + 0, 2, + 0, 3, + 0, 4, + 1, 2, + 2, 4, + 4, 3, + 3, 1, + 5, 1, + 5, 2, + 5, 3, + 5, 4 + }; + const unsigned short DiamondConnectorVertices[DiamondConnectorVertexCount] = { 1, 2, 3, 4 @@ -55,6 +73,8 @@ namespace SceneUtil }; const osg::Vec4f DiamondEdgeColor = osg::Vec4f(0.5f, 1.f, 1.f, 1.f); + const osg::Vec4f DiamondWireColor = osg::Vec4f(0.8f, 1.f, 0.9f, 1.f); + const osg::Vec4f DiamondFocusWireColor = osg::Vec4f(0.4f, 1.f, 0.4f, 1.f); osg::ref_ptr createPathgridGeometry(const ESM::Pathgrid& pathgrid) { @@ -155,4 +175,61 @@ namespace SceneUtil return gridGeometry; } + + osg::ref_ptr createPathgridSelectedWireframe(const ESM::Pathgrid& pathgrid, + const std::vector& selected) + { + const unsigned short PointCount = selected.size(); + + const unsigned short VertexCount = PointCount * DiamondVertexCount; + const unsigned short ColorCount = VertexCount; + const size_t IndexCount = PointCount * DiamondWireframeIndexCount; + + osg::ref_ptr wireframeGeometry = new osg::Geometry(); + + osg::ref_ptr vertices = new osg::Vec3Array(VertexCount); + osg::ref_ptr colors = new osg::Vec4Array(ColorCount); + osg::ref_ptr indices = + new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, IndexCount); + + osg::Vec3f wireOffset = osg::Vec3f(0, 0, (1 - DiamondWireframeScalar) * DiamondHalfHeight); + + // Add each point/node + for (unsigned short it = 0; it < PointCount; ++it) + { + const ESM::Pathgrid::Point& point = pathgrid.mPoints[selected[it]]; + osg::Vec3f position = osg::Vec3f(point.mX, point.mY, point.mZ) + wireOffset; + + unsigned short vertexOffset = it * DiamondVertexCount; + unsigned short indexOffset = it * DiamondWireframeIndexCount; + + // Point + for (unsigned short i = 0; i < DiamondVertexCount; ++i) + { + (*vertices)[vertexOffset + i] = position + DiamondPoints[i] * DiamondWireframeScalar; + + if (it == PointCount - 1) + (*colors)[vertexOffset + i] = DiamondFocusWireColor; + else + (*colors)[vertexOffset + i] = DiamondWireColor; + } + + for (unsigned short i = 0; i < DiamondWireframeIndexCount; ++i) + { + indices->setElement(indexOffset + i, vertexOffset + DiamondWireframeIndices[i]); + } + } + + wireframeGeometry->setVertexArray(vertices); + wireframeGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + wireframeGeometry->addPrimitiveSet(indices); + wireframeGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + return wireframeGeometry; + } + + unsigned short getPathgridNode(unsigned short vertexIndex) + { + return vertexIndex / (DiamondVertexCount + DiamondConnectorVertexCount); + } } diff --git a/components/sceneutil/pathgridutil.hpp b/components/sceneutil/pathgridutil.hpp index eb1ab1e3e..9a93ddf91 100644 --- a/components/sceneutil/pathgridutil.hpp +++ b/components/sceneutil/pathgridutil.hpp @@ -12,6 +12,11 @@ namespace ESM namespace SceneUtil { osg::ref_ptr createPathgridGeometry(const ESM::Pathgrid& pathgrid); + + osg::ref_ptr createPathgridSelectedWireframe(const ESM::Pathgrid& pathgrid, + const std::vector& selected); + + unsigned short getPathgridNode(unsigned short vertexIndex); } #endif From f8b43b2a64562046fdafed80df36686b64642426 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Fri, 13 May 2016 12:55:35 -0400 Subject: [PATCH 03/37] Rename cryptic variables. --- apps/opencs/view/render/pathgridmode.cpp | 6 +++--- apps/opencs/view/render/worldspacewidget.cpp | 12 ++++++------ apps/opencs/view/render/worldspacewidget.hpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 93b6a747d..d38390025 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -36,7 +36,7 @@ namespace CSVRender { if (PathgridTag* tag = dynamic_cast(hit.tag.get())) { - unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.i0)); + unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.index0)); tag->getPathgrid()->toggleSelected(node); } } @@ -54,7 +54,7 @@ namespace CSVRender mLastId = tag->getPathgrid()->getId(); } - unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.i0)); + unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.index0)); tag->getPathgrid()->toggleSelected(node); return; @@ -86,7 +86,7 @@ namespace CSVRender if (PathgridTag* tag = dynamic_cast(hit.tag.get())) { mDragMode = DragMode_Edge; - mFromNode = SceneUtil::getPathgridNode(static_cast(hit.i0)); + mFromNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); return true; } } diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 5963107e9..cd65bd8e8 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -501,9 +501,9 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() }; if (intersection.indexList.size() >= 3) { - hit.i0 = intersection.indexList[0]; - hit.i1 = intersection.indexList[1]; - hit.i2 = intersection.indexList[2]; + hit.index0 = intersection.indexList[0]; + hit.index1 = intersection.indexList[1]; + hit.index2 = intersection.indexList[2]; } return hit; } @@ -513,9 +513,9 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo WorldspaceHitResult hit = { true, 0, 0, 0, 0, intersection.getWorldIntersectPoint() }; if (intersection.indexList.size() >= 3) { - hit.i0 = intersection.indexList[0]; - hit.i1 = intersection.indexList[1]; - hit.i2 = intersection.indexList[2]; + hit.index0 = intersection.indexList[0]; + hit.index1 = intersection.indexList[1]; + hit.index2 = intersection.indexList[2]; } return hit; } diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 4160da3d8..2cf866342 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -40,7 +40,7 @@ namespace CSVRender { bool hit; osg::ref_ptr tag; - unsigned int i0, i1, i2; + unsigned int index0, index1, index2; // indices of mesh vertices osg::Vec3d worldPos; }; From da6a742bebf886a2fa49fbcd397e6f4a1968ce75 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 16:50:02 -0400 Subject: [PATCH 04/37] Add control description to tooltip. --- apps/opencs/view/render/pathgrid.cpp | 1 - apps/opencs/view/render/pathgridmode.cpp | 13 ++++++++++++- apps/opencs/view/render/pathgridmode.hpp | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 4aa254a44..bd25a1b28 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -33,7 +33,6 @@ namespace CSVRender if (!hideBasics) { - text += "

Only one pathgrid may be edited at a time."; } return text; diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index d38390025..2484dc04a 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -14,12 +14,23 @@ namespace CSVRender { PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent) - : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid, "Pathgrid editing", parent) + : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid, getTooltip(), parent) , mDragMode(DragMode_None) , mFromNode(0) { } + QString PathgridMode::getTooltip() + { + return QString( + "Pathgrid editing" + "

  • Primary edit: Add node to scene
  • " + "
  • Secondary edit: Connect selected nodes to node
  • " + "
  • Primary drag: Move selected nodes
  • " + "
  • Secondary drag: Connect one node to another
  • " + "
  • Other operations may be done with the context menu
  • " + "

Note: Only a single cell's pathgrid may be edited at a time"); + } void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hit) { } diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp index 0a84b6b10..75611e1f6 100644 --- a/apps/opencs/view/render/pathgridmode.hpp +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -47,6 +47,8 @@ namespace CSVRender std::string mLastId; DragMode mDragMode; unsigned short mFromNode; + QString getTooltip(); + }; } From 55656d68ef12d6f3818e04e1ffa5c95bbb7b868e Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:00:58 -0400 Subject: [PATCH 05/37] Cleanup, put duplicated code in function --- apps/opencs/view/render/pathgrid.cpp | 108 +++++++++++++++------------ apps/opencs/view/render/pathgrid.hpp | 6 ++ 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index bd25a1b28..4c3db2562 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -41,6 +41,7 @@ namespace CSVRender Pathgrid::Pathgrid(CSMWorld::Data& data, osg::Group* parent, const std::string& pathgridId, const CSMWorld::CellCoordinates& coordinates) : mData(data) + , mPathgridCollection(mData.getPathgrids()) , mId(pathgridId) , mCoords(coordinates) , mParent(parent) @@ -97,15 +98,13 @@ namespace CSVRender { mSelected.clear(); - const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); - int pathgridIndex = pathgrids.searchId(mId); - if (pathgridIndex != -1) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); - for (unsigned short i = 0; i < static_cast(source.mPoints.size()); ++i) + for (unsigned short i = 0; i < static_cast(source->mPoints.size()); ++i) mSelected.push_back(i); - recreateSelectedGeometry(source); + recreateSelectedGeometry(*source); } else { @@ -130,21 +129,19 @@ namespace CSVRender void Pathgrid::invertSelected() { - NodeList temp = NodeList(mSelected.begin(), mSelected.end()); + NodeList temp = NodeList(mSelected); mSelected.clear(); - const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); - int pathgridIndex = pathgrids.searchId(mId); - if (pathgridIndex != -1) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); - for (unsigned short i = 0; i < static_cast(source.mPoints.size()); ++i) + for (unsigned short i = 0; i < static_cast(source->mPoints.size()); ++i) { if (std::find(temp.begin(), temp.end(), i) == temp.end()) mSelected.push_back(i); } - recreateSelectedGeometry(source); + recreateSelectedGeometry(*source); } else { @@ -170,41 +167,44 @@ namespace CSVRender void Pathgrid::applyPosition(CSMWorld::CommandMacro& commands) { - const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); - int pathgridIndex = pathgrids.searchId(mId); - if (pathgridIndex != -1) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); osg::Vec3d localCoords = mSelectedNode->getPosition(); - int oX = static_cast(localCoords.x()); - int oY = static_cast(localCoords.y()); - int oZ = static_cast(localCoords.z()); + int offsetX = static_cast(localCoords.x()); + int offsetY = static_cast(localCoords.y()); + int offsetZ = static_cast(localCoords.z()); - CSMWorld::SubCellCollection& collection = mData.getPathgrids(); - QAbstractItemModel* model = mData.getTableModel (CSMWorld::UniversalId::Type_Pathgrids); + QAbstractItemModel* model = mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids); - int recordIndex = collection.getIndex (mId); - int parentColumn = collection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + int recordIndex = mPathgridCollection.getIndex(mId); + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + + int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridPosX); + + int posYColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridPosY); + + int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridPosZ); QModelIndex parent = model->index(recordIndex, parentColumn); for (size_t i = 0; i < mSelected.size(); ++i) { - const CSMWorld::Pathgrid::Point& point = source.mPoints[mSelected[i]]; - int row = mSelected[i]; + const CSMWorld::Pathgrid::Point& point = source->mPoints[mSelected[i]]; + int row = static_cast(mSelected[i]); - // X - int column = collection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosX); - commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, column, parent), point.mX + oX)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), + point.mX + offsetX)); - // Y - column = collection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosY); - commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, column, parent), point.mY + oY)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), + point.mY + offsetY)); - // Z - column = collection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); - commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, column, parent), point.mZ + oZ)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), + point.mZ + offsetZ)); } } @@ -228,30 +228,29 @@ namespace CSVRender void Pathgrid::recreateGeometry() { - removePathgridGeometry(); - removeSelectedGeometry(); - // Make new - const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); - int pathgridIndex = pathgrids.searchId(mId); - if (pathgridIndex != -1) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); - mPathgridGeometry = SceneUtil::createPathgridGeometry(source); + removePathgridGeometry(); + mPathgridGeometry = SceneUtil::createPathgridGeometry(*source); mPathgridGeode->addDrawable(mPathgridGeometry); - recreateSelectedGeometry(source); + recreateSelectedGeometry(*source); + } + else + { + removePathgridGeometry(); + removeSelectedGeometry(); } } void Pathgrid::recreateSelectedGeometry() { - const CSMWorld::SubCellCollection& pathgrids = mData.getPathgrids(); - int pathgridIndex = pathgrids.searchId(mId); - if (pathgridIndex != -1) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid& source = pathgrids.getRecord(pathgridIndex).get(); - recreateSelectedGeometry(source); + recreateSelectedGeometry(*source); } else { @@ -283,4 +282,15 @@ namespace CSVRender mSelectedGeometry.release(); } } + + const CSMWorld::Pathgrid* Pathgrid::getPathgridSource() + { + int index = mPathgridCollection.searchId(mId); + if (index != -1) + { + return &mPathgridCollection.getRecord(index).get(); + } + + return 0; + } } diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index c1a8f9308..2fece6e0d 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -7,6 +7,8 @@ #include #include "../../model/world/cellcoordinates.hpp" +#include "../../model/world/idcollection.hpp" +#include "../../model/world/subcellcollection.hpp" #include "tagbase.hpp" @@ -80,6 +82,7 @@ namespace CSVRender private: CSMWorld::Data& mData; + CSMWorld::SubCellCollection& mPathgridCollection; std::string mId; CSMWorld::CellCoordinates mCoords; @@ -99,6 +102,9 @@ namespace CSVRender void recreateSelectedGeometry(const CSMWorld::Pathgrid& source); void removePathgridGeometry(); void removeSelectedGeometry(); + + const CSMWorld::Pathgrid* getPathgridSource(); + }; } From 8b6cb733693d543f11b731c34733befcfdef9fd4 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:03:00 -0400 Subject: [PATCH 06/37] Add ability to invert selection. --- apps/opencs/view/render/pagedworldspacewidget.cpp | 9 +++++++++ apps/opencs/view/render/pagedworldspacewidget.hpp | 3 +++ apps/opencs/view/render/unpagedworldspacewidget.cpp | 6 ++++++ apps/opencs/view/render/unpagedworldspacewidget.hpp | 3 +++ apps/opencs/view/render/worldspacewidget.hpp | 3 +++ 5 files changed, 24 insertions(+) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 90088f24e..b44d1d58a 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -606,6 +606,15 @@ void CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask) flagAsModified(); } +void CSVRender::PagedWorldspaceWidget::invertSelection (int elementMask) +{ + for (std::map::iterator iter = mCells.begin(); + iter!=mCells.end(); ++iter) + iter->second->setSelection (elementMask, Cell::Selection_Invert); + + flagAsModified(); +} + void CSVRender::PagedWorldspaceWidget::selectAll (int elementMask) { for (std::map::iterator iter = mCells.begin(); diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 8c17d4fcd..95bf0f846 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -107,6 +107,9 @@ namespace CSVRender /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask); + /// \param elementMask Elements to be affected by the select operation + virtual void invertSelection (int elementMask); + /// \param elementMask Elements to be affected by the select operation virtual void selectAll (int elementMask); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 84b7a0ca8..df28f3f80 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -104,6 +104,12 @@ void CSVRender::UnpagedWorldspaceWidget::clearSelection (int elementMask) flagAsModified(); } +void CSVRender::UnpagedWorldspaceWidget::invertSelection (int elementMask) +{ + mCell->setSelection (elementMask, Cell::Selection_Invert); + flagAsModified(); +} + void CSVRender::UnpagedWorldspaceWidget::selectAll (int elementMask) { mCell->setSelection (elementMask, Cell::Selection_All); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index f06302032..98cc3ef96 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -47,6 +47,9 @@ namespace CSVRender /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask); + /// \param elementMask Elements to be affected by the select operation + virtual void invertSelection (int elementMask); + /// \param elementMask Elements to be affected by the select operation virtual void selectAll (int elementMask); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 2cf866342..70ebae5f8 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -136,6 +136,9 @@ namespace CSVRender /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask) = 0; + /// \param elementMask Elements to be affected by the select operation + virtual void invertSelection (int elementMask) = 0; + /// \param elementMask Elements to be affected by the select operation virtual void selectAll (int elementMask) = 0; From f0bbe2c633361a5b51794cd49104e945f28e447d Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:06:40 -0400 Subject: [PATCH 07/37] Add context menu for pathgrid editor. --- apps/opencs/view/render/pathgridmode.cpp | 59 +++++++++++++++++++++++- apps/opencs/view/render/pathgridmode.hpp | 18 ++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 2484dc04a..de7b09018 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -1,5 +1,7 @@ #include "pathgridmode.hpp" +#include + #include #include "../../model/world/commands.hpp" @@ -31,8 +33,39 @@ namespace CSVRender "

  • Other operations may be done with the context menu
  • " "

    Note: Only a single cell's pathgrid may be edited at a time"); } - void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hit) + + void PathgridMode::activate(CSVWidget::SceneToolbar* toolbar) { + mSelectAll = new QAction("Select all other nodes in cell", this); + mInvertSelection = new QAction("Invert selection", this); + mClearSelection = new QAction("Clear selection", this); + mRemoveSelected = new QAction("Remove selected nodes", this); + mRemoveSelectedEdges = new QAction("Remove edges between selected nodes", this); + + connect(mSelectAll, SIGNAL(triggered()), this, SLOT(selectAll())); + connect(mInvertSelection, SIGNAL(triggered()), this, SLOT(invertSelection())); + connect(mClearSelection, SIGNAL(triggered()), this, SLOT(clearSelection())); + connect(mRemoveSelected, SIGNAL(triggered()), this, SLOT(removeSelected())); + connect(mRemoveSelectedEdges, SIGNAL(triggered()), this, SLOT(removeSelectedEdges())); + + EditMode::activate(toolbar); + } + + bool PathgridMode::createContextMenu(QMenu* menu) + { + if (menu) + { + menu->addAction(mSelectAll); + menu->addAction(mInvertSelection); + menu->addAction(mClearSelection); + menu->addAction(mRemoveSelected); + menu->addAction(mRemoveSelectedEdges); + } + + return true; + } + + void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult) } void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit) @@ -160,4 +193,28 @@ namespace CSVRender { getWorldspaceWidget().reset(Mask_Pathgrid); } + + void PathgridMode::selectAll() + { + // Select rest of nodes in selected cell + getWorldspaceWidget().selectAll(Mask_Pathgrid); + } + + void PathgridMode::invertSelection() + { + getWorldspaceWidget().invertSelection(Mask_Pathgrid); + } + + void PathgridMode::clearSelection() + { + getWorldspaceWidget().clearSelection(Mask_Pathgrid); + } + + void PathgridMode::removeSelected() + { + } + + void PathgridMode::removeSelectedEdges() + { + } } diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp index 75611e1f6..b6a650eb4 100644 --- a/apps/opencs/view/render/pathgridmode.hpp +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -15,6 +15,10 @@ namespace CSVRender PathgridMode(WorldspaceWidget* worldspace, QWidget* parent=0); + virtual void activate(CSVWidget::SceneToolbar* toolbar); + + virtual bool createContextMenu(QMenu* menu); + virtual void primaryEditPressed(const WorldspaceHitResult& hit); virtual void secondaryEditPressed(const WorldspaceHitResult& hit); @@ -47,8 +51,22 @@ namespace CSVRender std::string mLastId; DragMode mDragMode; unsigned short mFromNode; + + QAction* mSelectAll; + QAction* mInvertSelection; + QAction* mClearSelection; + QAction* mRemoveSelected; + QAction* mRemoveSelectedEdges; + QString getTooltip(); + private slots: + + void selectAll(); + void invertSelection(); + void clearSelection(); + void removeSelected(); + void removeSelectedEdges(); }; } From 9f7c8d559c54fd85f45191d7754f9e27d87aef76 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:16:15 -0400 Subject: [PATCH 08/37] Add focus requirement for context menu. --- apps/opencs/view/render/cell.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index a8e36728c..e383ccad3 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -307,11 +307,23 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode) } if (elementMask & Mask_Pathgrid) { + // Only one pathgrid may be selected, so some operations will only have an effect + // if the pathgrid is already focused switch (mode) { - case Selection_Clear: mPathgrid->clearSelected(); break; - case Selection_All: mPathgrid->selectAll(); break; - case Selection_Invert: mPathgrid->invertSelected(); break; + case Selection_Clear: + mPathgrid->clearSelected(); + break; + + case Selection_All: + if (mPathgrid->isSelected()) + mPathgrid->selectAll(); + break; + + case Selection_Invert: + if (mPathgrid->isSelected()) + mPathgrid->invertSelected(); + break; } } } From 6fbc10dbbadfe807efe56f0e96fd670571ff53d2 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:20:07 -0400 Subject: [PATCH 09/37] Add ability to get cell, pathgrid, and ability to add nodes. Also missing include. --- apps/opencs/view/render/cell.cpp | 5 +++ apps/opencs/view/render/cell.hpp | 3 ++ .../view/render/pagedworldspacewidget.cpp | 15 +++++++ .../view/render/pagedworldspacewidget.hpp | 2 + apps/opencs/view/render/pathgrid.cpp | 42 +++++++++++++++++++ apps/opencs/view/render/pathgrid.hpp | 1 + apps/opencs/view/render/pathgridmode.cpp | 32 ++++++++++++++ .../view/render/unpagedworldspacewidget.cpp | 5 +++ .../view/render/unpagedworldspacewidget.hpp | 2 + apps/opencs/view/render/worldspacewidget.hpp | 4 ++ 10 files changed, 111 insertions(+) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index e383ccad3..eed394d0b 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -123,6 +123,11 @@ CSVRender::Cell::~Cell() mCellNode->getParent(0)->removeChild(mCellNode); } +CSVRender::Pathgrid* CSVRender::Cell::getPathgrid() const +{ + return mPathgrid.get(); +} + bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index caf6c59a9..a2efc0077 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -87,6 +87,9 @@ namespace CSVRender ~Cell(); + /// \note Returns the pathgrid representation which will exist as long as the cell exists + Pathgrid* getPathgrid() const; + /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index b44d1d58a..39e7f8361 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -644,6 +644,21 @@ std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point return cellCoordinates.getId (mWorldspace); } +CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const +{ + const int cellSize = 8192; + + CSMWorld::CellCoordinates coords( + static_cast (std::floor (point.x()/cellSize)), + static_cast (std::floor (point.y()/cellSize))); + + std::map::const_iterator searchResult = mCells.find(coords); + if (searchResult != mCells.end()) + return searchResult->second; + else + return 0; +} + std::vector > CSVRender::PagedWorldspaceWidget::getSelection ( unsigned int elementMask) const { diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 95bf0f846..c7f035dd9 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -121,6 +121,8 @@ namespace CSVRender virtual std::string getCellId (const osg::Vec3f& point) const; + virtual Cell* getCell(const osg::Vec3d& point) const; + virtual std::vector > getSelection (unsigned int elementMask) const; diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 4c3db2562..4df9852cb 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -13,6 +13,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/commandmacro.hpp" #include "../../model/world/data.hpp" +#include "../../model/world/idtree.hpp" namespace CSVRender { @@ -165,6 +166,47 @@ namespace CSVRender mSelectedNode->setPosition(osg::Vec3f(0,0,0)); } + void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos) + { + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + osg::Vec3d localCoords = worldPos - mBaseNode->getPosition(); + + int posX = static_cast(localCoords.x()); + int posY = static_cast(localCoords.y()); + int posZ = static_cast(localCoords.z()); + + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( + CSMWorld::UniversalId::Type_Pathgrids)); + + int recordIndex = mPathgridCollection.getIndex (mId); + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + + int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridPosX); + + int posYColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridPosY); + + int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridPosZ); + + QModelIndex parent = model->index(recordIndex, parentColumn); + int row = static_cast(source->mPoints.size()); + + // Add node + commands.push (new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); + commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX)); + commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY)); + commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), posZ)); + } + else + { + // Create pathgrid TODO + } + } + void Pathgrid::applyPosition(CSMWorld::CommandMacro& commands) { const CSMWorld::Pathgrid* source = getPathgridSource(); diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index 2fece6e0d..a82041740 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -71,6 +71,7 @@ namespace CSVRender void moveSelected(const osg::Vec3d& offset); void resetMove(); + void applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos); void applyPosition(CSMWorld::CommandMacro& commands); void applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2); void applyEdges(CSMWorld::CommandMacro& commands, unsigned short node); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index de7b09018..c06618e74 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -9,6 +9,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" +#include "cell.hpp" #include "mask.hpp" #include "pathgrid.hpp" #include "worldspacewidget.hpp" @@ -66,6 +67,37 @@ namespace CSVRender } void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult) + { + // Determine placement + osg::Vec3d position; + + if (hitResult.hit) + { + position = hitResult.worldPos; + } + else + { + const double DefaultDistance = 500.f; + + osg::Vec3d eye, center, up, offset; + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, center, up); + + osg::Vec3d direction = center - eye; + direction.normalize(); + position = eye + direction * DefaultDistance; + } + + // Get pathgrid cell + Cell* cell = getWorldspaceWidget().getCell (position); + if (cell) + { + // Add node + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Connect node to selected nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + cell->getPathgrid()->applyPoint(macro, position); + } } void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit) diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index df28f3f80..802194bea 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -127,6 +127,11 @@ std::string CSVRender::UnpagedWorldspaceWidget::getCellId (const osg::Vec3f& poi return mCellId; } +CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& point) const +{ + return mCell.get(); +} + std::vector > CSVRender::UnpagedWorldspaceWidget::getSelection ( unsigned int elementMask) const { diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index 98cc3ef96..dd4a8a2f7 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -61,6 +61,8 @@ namespace CSVRender virtual std::string getCellId (const osg::Vec3f& point) const; + virtual Cell* getCell(const osg::Vec3d& point) const; + virtual std::vector > getSelection (unsigned int elementMask) const; diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 70ebae5f8..59aacca29 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -33,6 +33,7 @@ namespace CSVWidget namespace CSVRender { class TagBase; + class Cell; class CellArrow; class EditMode; @@ -161,6 +162,9 @@ namespace CSVRender virtual std::string getCellId (const osg::Vec3f& point) const = 0; + /// \note Returns the cell if it exists, otherwise a null pointer + virtual Cell* getCell(const osg::Vec3d& point) const = 0; + virtual std::vector > getSelection (unsigned int elementMask) const = 0; From aea2380c2ba507a1c0fa1b28ca58d71431e9b9be Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:25:02 -0400 Subject: [PATCH 10/37] Add raytrace to dragCompleted. Implement node drag connection, node and edge removal, and some fixes. --- apps/opencs/view/render/editmode.cpp | 2 +- apps/opencs/view/render/editmode.hpp | 2 +- apps/opencs/view/render/instancemode.cpp | 2 +- apps/opencs/view/render/instancemode.hpp | 2 +- apps/opencs/view/render/pathgrid.cpp | 158 ++++++++++++++++++- apps/opencs/view/render/pathgrid.hpp | 11 ++ apps/opencs/view/render/pathgridmode.cpp | 92 +++++++++-- apps/opencs/view/render/pathgridmode.hpp | 4 +- apps/opencs/view/render/worldspacewidget.cpp | 3 +- 9 files changed, 247 insertions(+), 29 deletions(-) diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index 8da000dc9..22de54218 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -59,7 +59,7 @@ bool CSVRender::EditMode::secondarySelectStartDrag (const WorldspaceHitResult& h void CSVRender::EditMode::drag (int diffX, int diffY, double speedFactor) {} -void CSVRender::EditMode::dragCompleted() {} +void CSVRender::EditMode::dragCompleted(const WorldspaceHitResult& hit) {} void CSVRender::EditMode::dragAborted() {} diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index 3381a7105..37bbafdb1 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -74,7 +74,7 @@ namespace CSVRender virtual void drag (int diffX, int diffY, double speedFactor); /// Default-implementation: ignored - virtual void dragCompleted(); + virtual void dragCompleted(const WorldspaceHitResult& hit); /// Default-implementation: ignored /// diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 52335f244..958d29337 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -244,7 +244,7 @@ void CSVRender::InstanceMode::drag (int diffX, int diffY, double speedFactor) } } -void CSVRender::InstanceMode::dragCompleted() +void CSVRender::InstanceMode::dragCompleted(const WorldspaceHitResult& hit) { std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 5b1252912..39e3773ad 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -55,7 +55,7 @@ namespace CSVRender virtual void drag (int diffX, int diffY, double speedFactor); - virtual void dragCompleted(); + virtual void dragCompleted(const WorldspaceHitResult& hit); /// \note dragAborted will not be called, if the drag is aborted via changing /// editing mode diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 4df9852cb..7807525ef 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -45,6 +45,8 @@ namespace CSVRender , mPathgridCollection(mData.getPathgrids()) , mId(pathgridId) , mCoords(coordinates) + , mConnectionIndicator(false) + , mConnectionNode(0) , mParent(parent) , mPathgridGeometry(0) , mSelectedGeometry(0) @@ -161,9 +163,21 @@ namespace CSVRender mSelectedNode->setPosition(mSelectedNode->getPosition() + offset); } + void Pathgrid::setupConnectionIndicator(unsigned short node) + { + mConnectionIndicator = true; + mConnectionNode = node; + recreateSelectedGeometry(); + } + void Pathgrid::resetMove() { mSelectedNode->setPosition(osg::Vec3f(0,0,0)); + if (mConnectionIndicator) + { + mConnectionIndicator = false; + recreateSelectedGeometry(); + } } void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos) @@ -249,18 +263,88 @@ namespace CSVRender point.mZ + offsetZ)); } } - - resetMove(); } void Pathgrid::applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2) { - // TODO + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + addEdge(commands, *source, node1, node2); + } } void Pathgrid::applyEdges(CSMWorld::CommandMacro& commands, unsigned short node) { - // TODO + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + for (size_t i = 0; i < mSelected.size(); ++i) + { + addEdge(commands, *source, node, mSelected[i]); + } + } + } + + void Pathgrid::applyRemoveNodes(CSMWorld::CommandMacro& commands) + { + // Source is aquired here to ensure a pathgrid exists + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + // Want to remove from end of row first + std::sort(mSelected.begin(), mSelected.end(), std::greater()); + + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( + CSMWorld::UniversalId::Type_Pathgrids)); + + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + + for (std::vector::iterator row = mSelected.begin(); row != mSelected.end(); ++row) + { + commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, static_cast(*row), parentColumn)); + } + } + + clearSelected(); + } + + void Pathgrid::applyRemoveEdges(CSMWorld::CommandMacro& commands) + { + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + // Want to remove from end of row first + std::set > rowsToRemove; + for (size_t i = 0; i <= mSelected.size(); ++i) + { + for (size_t j = i + 1; j < mSelected.size(); ++j) + { + int row = edgeExists(*source, mSelected[i], mSelected[j]); + if (row != -1) + { + rowsToRemove.insert(row); + } + + row = edgeExists(*source, mSelected[j], mSelected[i]); + if (row != -1) + { + rowsToRemove.insert(row); + } + } + } + + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( + CSMWorld::UniversalId::Type_Pathgrids)); + + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); + + std::set >::iterator row; + for (row = rowsToRemove.begin(); row != rowsToRemove.end(); ++row) + { + commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, *row, parentColumn)); + } + } } osg::ref_ptr Pathgrid::getTag() const @@ -303,8 +387,25 @@ namespace CSVRender void Pathgrid::recreateSelectedGeometry(const CSMWorld::Pathgrid& source) { removeSelectedGeometry(); - mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, mSelected); - mSelectedGeode->addDrawable(mSelectedGeometry); + + if (mConnectionIndicator) + { + NodeList tempList = NodeList(mSelected); + + NodeList::iterator searchResult = std::find(tempList.begin(), tempList.end(), mConnectionNode); + if (searchResult != tempList.end()) + tempList.erase(searchResult); + + tempList.push_back(mConnectionNode); + + mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, tempList); + mSelectedGeode->addDrawable(mSelectedGeometry); + } + else + { + mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, mSelected); + mSelectedGeode->addDrawable(mSelectedGeometry); + } } void Pathgrid::removePathgridGeometry() @@ -335,4 +436,49 @@ namespace CSVRender return 0; } + + int Pathgrid::edgeExists(const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2) + { + for (size_t i = 0; i < source.mEdges.size(); ++i) + { + if (source.mEdges[i].mV0 == node1 && source.mEdges[i].mV1 == node2) + return static_cast(i); + } + + return -1; + } + + void Pathgrid::addEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1, + unsigned short node2) + { + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( + CSMWorld::UniversalId::Type_Pathgrids)); + + int recordIndex = mPathgridCollection.getIndex(mId); + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); + + int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridEdge0); + + int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridEdge1); + + QModelIndex parent = model->index(recordIndex, parentColumn); + int row = static_cast(source.mEdges.size()); + + if (edgeExists(source, node1, node2) == -1) + { + commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node1)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node2)); + ++row; + } + + if (edgeExists(source, node2, node1) == -1) + { + commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node2)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node1)); + } + } } diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index a82041740..62ecef4d6 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -71,10 +71,14 @@ namespace CSVRender void moveSelected(const osg::Vec3d& offset); void resetMove(); + void setupConnectionIndicator(unsigned short node); + void applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos); void applyPosition(CSMWorld::CommandMacro& commands); void applyEdge(CSMWorld::CommandMacro& commands, unsigned short node1, unsigned short node2); void applyEdges(CSMWorld::CommandMacro& commands, unsigned short node); + void applyRemoveNodes(CSMWorld::CommandMacro& commands); + void applyRemoveEdges(CSMWorld::CommandMacro& commands); osg::ref_ptr getTag() const; @@ -88,6 +92,8 @@ namespace CSVRender CSMWorld::CellCoordinates mCoords; NodeList mSelected; + bool mConnectionIndicator; + unsigned short mConnectionNode; osg::Group* mParent; osg::ref_ptr mBaseNode; @@ -106,6 +112,11 @@ namespace CSVRender const CSMWorld::Pathgrid* getPathgridSource(); + int edgeExists(const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2); + void addEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1, + unsigned short node2); + void removeEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1, + unsigned short node2); }; } diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index c06618e74..e4b0e0994 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -102,6 +102,22 @@ namespace CSVRender void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit) { + if (hit.tag) + { + if (PathgridTag* tag = dynamic_cast(hit.tag.get())) + { + if (tag->getPathgrid()->isSelected()) + { + unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.index0)); + + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Connect node to selected nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyEdges(macro, node); + } + } + } } void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit) @@ -112,6 +128,7 @@ namespace CSVRender { if (PathgridTag* tag = dynamic_cast(hit.tag.get())) { + mLastId = tag->getPathgrid()->getId(); unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.index0)); tag->getPathgrid()->toggleSelected(node); } @@ -147,12 +164,9 @@ namespace CSVRender if (!selection.empty()) { mDragMode = DragMode_Move; - return true; - } - else - { - return false; } + + return true; } bool PathgridMode::secondaryEditStartDrag(const WorldspaceHitResult& hit) @@ -162,12 +176,14 @@ namespace CSVRender if (PathgridTag* tag = dynamic_cast(hit.tag.get())) { mDragMode = DragMode_Edge; + mEdgeId = tag->getPathgrid()->getId(); mFromNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); - return true; + + tag->getPathgrid()->setupConnectionIndicator(mFromNode); } } - return false; + return true; } void PathgridMode::drag(int diffX, int diffY, double speedFactor) @@ -189,21 +205,20 @@ namespace CSVRender } else if (mDragMode == DragMode_Edge) { - // TODO make indicators + // TODO Add indicator, need raytrace } } } } - void PathgridMode::dragCompleted() + void PathgridMode::dragCompleted(const WorldspaceHitResult& hit) { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); - - for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + if (mDragMode == DragMode_Move) { - if (PathgridTag* tag = dynamic_cast(it->get())) + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { - if (mDragMode == DragMode_Move) + if (PathgridTag* tag = dynamic_cast(it->get())) { QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); QString description = "Move pathgrid node(s)"; @@ -211,14 +226,33 @@ namespace CSVRender CSMWorld::CommandMacro macro(undoStack, description); tag->getPathgrid()->applyPosition(macro); } - else if (mDragMode == DragMode_Edge) + } + } + else if (mDragMode == DragMode_Edge) + { + if (hit.tag) + { + if (PathgridTag* tag = dynamic_cast(hit.tag.get())) { - // TODO raycast for other node and apply if needed with mFromNode + if (tag->getPathgrid()->getId() == mEdgeId) + { + unsigned short toNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); + + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Add edge between nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyEdge(macro, mFromNode, toNode); + } } } + + mEdgeId.clear(); + mFromNode = 0; } mDragMode = DragMode_None; + getWorldspaceWidget().reset(Mask_Pathgrid); } void PathgridMode::dragAborted() @@ -244,9 +278,35 @@ namespace CSVRender void PathgridMode::removeSelected() { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + { + if (PathgridTag* tag = dynamic_cast(it->get())) + { + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Remove selected nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyRemoveNodes(macro); + } + } } void PathgridMode::removeSelectedEdges() { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + { + if (PathgridTag* tag = dynamic_cast(it->get())) + { + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Remove edges between selected nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyRemoveEdges(macro); + } + } } } diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp index b6a650eb4..6a070333d 100644 --- a/apps/opencs/view/render/pathgridmode.hpp +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -33,7 +33,7 @@ namespace CSVRender virtual void drag (int diffX, int diffY, double speedFactor); - virtual void dragCompleted(); + virtual void dragCompleted(const WorldspaceHitResult& hit); /// \note dragAborted will not be called, if the drag is aborted via changing /// editing mode @@ -48,8 +48,8 @@ namespace CSVRender DragMode_Edge }; - std::string mLastId; DragMode mDragMode; + std::string mLastId, mEdgeId; unsigned short mFromNode; QAction* mSelectAll; diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index cd65bd8e8..9146de619 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -712,8 +712,9 @@ void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event) if (mDragging) { EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); + WorldspaceHitResult hit = mousePick (event->pos()); - editMode.dragCompleted(); + editMode.dragCompleted(hit); mDragging = false; } else From cd3b96b3e8b04beac9cdcc346de658c349d69353 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 17:59:52 -0400 Subject: [PATCH 11/37] Change interaction mask back to original and place required masks in PathgridMode. --- apps/opencs/view/render/pathgridmode.cpp | 3 ++- apps/opencs/view/render/worldspacewidget.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index e4b0e0994..d50b42d35 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -17,7 +17,8 @@ namespace CSVRender { PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent) - : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid, getTooltip(), parent) + : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid | Mask_Terrain | Mask_Reference, + getTooltip(), parent) , mDragMode(DragMode_None) , mFromNode(0) { diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 9146de619..64c1be0ee 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -295,7 +295,7 @@ unsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const void CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask) { - mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow | Mask_Pathgrid | Mask_Terrain; + mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow; } unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const From d2d22e2f237e2c4619566144c941a330ba119939 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 19:03:40 -0400 Subject: [PATCH 12/37] Clamp node positions in exterior cells. --- apps/opencs/view/render/pathgrid.cpp | 35 +++++++++++++++++++++++----- apps/opencs/view/render/pathgrid.hpp | 3 +++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 7807525ef..2e4ec6a1a 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -10,6 +10,7 @@ #include +#include "../../model/world/cell.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/commandmacro.hpp" #include "../../model/world/data.hpp" @@ -45,6 +46,7 @@ namespace CSVRender , mPathgridCollection(mData.getPathgrids()) , mId(pathgridId) , mCoords(coordinates) + , mInterior(false) , mConnectionIndicator(false) , mConnectionNode(0) , mParent(parent) @@ -70,6 +72,13 @@ namespace CSVRender mSelectedNode->addChild(mSelectedGeode); recreateGeometry(); + + int index = mData.getCells().searchId(mId); + if (index != -1) + { + const CSMWorld::Cell& cell = mData.getCells().getRecord(index).get(); + mInterior = cell.mData.mFlags & ESM::Cell::Interior; + } } Pathgrid::~Pathgrid() @@ -187,9 +196,9 @@ namespace CSVRender { osg::Vec3d localCoords = worldPos - mBaseNode->getPosition(); - int posX = static_cast(localCoords.x()); - int posY = static_cast(localCoords.y()); - int posZ = static_cast(localCoords.z()); + int posX = clampToCell(static_cast(localCoords.x())); + int posY = clampToCell(static_cast(localCoords.y())); + int posZ = clampToCell(static_cast(localCoords.z())); CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); @@ -254,13 +263,13 @@ namespace CSVRender int row = static_cast(mSelected[i]); commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), - point.mX + offsetX)); + clampToCell(point.mX + offsetX))); commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), - point.mY + offsetY)); + clampToCell(point.mY + offsetY))); commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), - point.mZ + offsetZ)); + clampToCell(point.mZ + offsetZ))); } } } @@ -481,4 +490,18 @@ namespace CSVRender commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node1)); } } + + int Pathgrid::clampToCell(int v) + { + const int CellExtent = ESM::Land::REAL_SIZE; + + if (mInterior) + return v; + else if (v > CellExtent) + return CellExtent; + else if (v < 0) + return 0; + else + return v; + } } diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index 62ecef4d6..3a83716d8 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -90,6 +90,7 @@ namespace CSVRender CSMWorld::SubCellCollection& mPathgridCollection; std::string mId; CSMWorld::CellCoordinates mCoords; + bool mInterior; NodeList mSelected; bool mConnectionIndicator; @@ -117,6 +118,8 @@ namespace CSVRender unsigned short node2); void removeEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2); + + int clampToCell(int v); }; } From a3363bc09823a02041adf848c4046ed2a2fc6b7e Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 21:06:55 -0400 Subject: [PATCH 13/37] Fix and simplify pathgrid update handling, only recreate geometry once per frame, and a few naming changes. --- apps/opencs/view/render/cell.cpp | 19 +----- apps/opencs/view/render/cell.hpp | 8 +-- .../view/render/pagedworldspacewidget.cpp | 53 ++++----------- .../view/render/pagedworldspacewidget.hpp | 2 - apps/opencs/view/render/pathgrid.cpp | 66 +++++++++++++++---- apps/opencs/view/render/pathgrid.hpp | 11 +++- .../view/render/unpagedworldspacewidget.cpp | 49 +++++--------- .../view/render/unpagedworldspacewidget.hpp | 2 - apps/opencs/view/render/worldspacewidget.cpp | 2 - apps/opencs/view/render/worldspacewidget.hpp | 2 - 10 files changed, 96 insertions(+), 118 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index eed394d0b..b408503d1 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -266,29 +266,14 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int return addObjects (start, end); } -void CSVRender::Cell::pathgridAdded(const CSMWorld::Pathgrid& pathgrid) +void CSVRender::Cell::pathgridModified() { mPathgrid->recreateGeometry(); } void CSVRender::Cell::pathgridRemoved() { - mPathgrid->recreateGeometry(); -} - -void CSVRender::Cell::pathgridDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - mPathgrid->recreateGeometry(); -} - -void CSVRender::Cell::pathgridRowRemoved(const QModelIndex& parent, int start, int end) -{ - mPathgrid->recreateGeometry(); -} - -void CSVRender::Cell::pathgridRowAdded(const QModelIndex& parent, int start, int end) -{ - mPathgrid->recreateGeometry(); + mPathgrid->removeGeometry(); } void CSVRender::Cell::setSelection (int elementMask, Selection mode) diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index a2efc0077..523a6bb83 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -111,16 +111,10 @@ namespace CSVRender /// this cell? bool referenceAdded (const QModelIndex& parent, int start, int end); - void pathgridAdded(const CSMWorld::Pathgrid& pathgrid); + void pathgridModified(); void pathgridRemoved(); - void pathgridDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void pathgridRowRemoved(const QModelIndex& parent, int start, int end); - - void pathgridRowAdded(const QModelIndex& parent, int start, int end); - void setSelection (int elementMask, Selection mode); // Select everything that references the same ID as at least one of the elements diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 39e7f8361..a01df4392 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -280,38 +280,29 @@ void CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& t { const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + int rowStart = -1; + int rowEnd = -1; + if (topLeft.parent().isValid()) { - int row = topLeft.parent().row(); - - const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); - CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY); - - std::map::iterator searchResult = mCells.find(coords); - if (searchResult != mCells.end()) - { - searchResult->second->pathgridDataChanged(topLeft, bottomRight); - flagAsModified(); - } + rowStart = topLeft.parent().row(); + rowEnd = bottomRight.parent().row(); } -} - -void CSVRender::PagedWorldspaceWidget::pathgridRemoved (const QModelIndex& parent, int start, int end) -{ - const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); - - if (parent.isValid()) + else { - // Pathgrid data was modified - int row = parent.row(); + rowStart = topLeft.row(); + rowEnd = bottomRight.row(); + } + for (int row = rowStart; row <= rowEnd; ++row) + { const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY); std::map::iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) { - searchResult->second->pathgridRowRemoved(parent, start, end); + searchResult->second->pathgridModified(); flagAsModified(); } } @@ -341,11 +332,10 @@ void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelInd void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, int start, int end) { - const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); if (!parent.isValid()) { - // Pathgrid added theoretically, unable to test until it is possible to add pathgrids for (int row = start; row <= end; ++row) { const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); @@ -354,26 +344,11 @@ void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, std::map::iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) { - searchResult->second->pathgridAdded(pathgrid); + searchResult->second->pathgridModified(); flagAsModified(); } } } - else - { - // Pathgrid data was modified - int row = parent.row(); - - const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); - CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY); - - std::map::iterator searchResult = mCells.find(coords); - if (searchResult != mCells.end()) - { - searchResult->second->pathgridRowAdded(parent, start, end); - flagAsModified(); - } - } } diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index c7f035dd9..692000708 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -54,8 +54,6 @@ namespace CSVRender virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - virtual void pathgridRemoved (const QModelIndex& parent, int start, int end); - virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end); virtual void pathgridAdded (const QModelIndex& parent, int start, int end); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 2e4ec6a1a..5c8868344 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -18,6 +18,17 @@ namespace CSVRender { + class PathgridNodeCallback : public osg::NodeCallback + { + public: + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + PathgridTag* tag = dynamic_cast(node->getUserData()); + tag->getPathgrid()->update(); + } + }; + PathgridTag::PathgridTag(Pathgrid* pathgrid) : TagBase(Mask_Pathgrid), mPathgrid(pathgrid) { @@ -49,6 +60,8 @@ namespace CSVRender , mInterior(false) , mConnectionIndicator(false) , mConnectionNode(0) + , mChangeGeometry(true) + , mRemoveGeometry(false) , mParent(parent) , mPathgridGeometry(0) , mSelectedGeometry(0) @@ -59,6 +72,7 @@ namespace CSVRender mBaseNode = new osg::PositionAttitudeTransform (); mBaseNode->setPosition(osg::Vec3f(mCoords.getX() * CoordScalar, mCoords.getY() * CoordScalar, 0.f)); mBaseNode->setUserData(mTag); + mBaseNode->setUpdateCallback(new PathgridNodeCallback()); mBaseNode->setNodeMask(Mask_Pathgrid); mParent->addChild(mBaseNode); @@ -116,7 +130,7 @@ namespace CSVRender for (unsigned short i = 0; i < static_cast(source->mPoints.size()); ++i) mSelected.push_back(i); - recreateSelectedGeometry(*source); + createSelectedGeometry(*source); } else { @@ -136,7 +150,7 @@ namespace CSVRender mSelected.push_back(node); } - recreateSelectedGeometry(); + createSelectedGeometry(); } void Pathgrid::invertSelected() @@ -153,7 +167,7 @@ namespace CSVRender mSelected.push_back(i); } - recreateSelectedGeometry(*source); + createSelectedGeometry(*source); } else { @@ -176,7 +190,7 @@ namespace CSVRender { mConnectionIndicator = true; mConnectionNode = node; - recreateSelectedGeometry(); + createSelectedGeometry(); } void Pathgrid::resetMove() @@ -185,7 +199,7 @@ namespace CSVRender if (mConnectionIndicator) { mConnectionIndicator = false; - recreateSelectedGeometry(); + createSelectedGeometry(); } } @@ -363,7 +377,34 @@ namespace CSVRender void Pathgrid::recreateGeometry() { - // Make new + mChangeGeometry = true; + } + + void Pathgrid::removeGeometry() + { + mRemoveGeometry = true; + + + } + + void Pathgrid::update() + { + if (mRemoveGeometry) + { + removePathgridGeometry(); + removeSelectedGeometry(); + } + else if (mChangeGeometry) + { + createGeometry(); + } + + mChangeGeometry = false; + mRemoveGeometry = false; + } + + void Pathgrid::createGeometry() + { const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { @@ -371,21 +412,20 @@ namespace CSVRender mPathgridGeometry = SceneUtil::createPathgridGeometry(*source); mPathgridGeode->addDrawable(mPathgridGeometry); - recreateSelectedGeometry(*source); + createSelectedGeometry(*source); } else { - removePathgridGeometry(); - removeSelectedGeometry(); + removeGeometry(); } } - void Pathgrid::recreateSelectedGeometry() + void Pathgrid::createSelectedGeometry() { const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { - recreateSelectedGeometry(*source); + createSelectedGeometry(*source); } else { @@ -393,7 +433,7 @@ namespace CSVRender } } - void Pathgrid::recreateSelectedGeometry(const CSMWorld::Pathgrid& source) + void Pathgrid::createSelectedGeometry(const CSMWorld::Pathgrid& source) { removeSelectedGeometry(); @@ -438,7 +478,7 @@ namespace CSVRender const CSMWorld::Pathgrid* Pathgrid::getPathgridSource() { int index = mPathgridCollection.searchId(mId); - if (index != -1) + if (index != -1 && !mPathgridCollection.getRecord(index).isDeleted()) { return &mPathgridCollection.getRecord(index).get(); } diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index 3a83716d8..b192ed31b 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -83,6 +83,9 @@ namespace CSVRender osg::ref_ptr getTag() const; void recreateGeometry(); + void removeGeometry(); + + void update(); private: @@ -96,6 +99,9 @@ namespace CSVRender bool mConnectionIndicator; unsigned short mConnectionNode; + bool mChangeGeometry; + bool mRemoveGeometry; + osg::Group* mParent; osg::ref_ptr mBaseNode; osg::ref_ptr mSelectedNode; @@ -106,8 +112,9 @@ namespace CSVRender osg::ref_ptr mTag; - void recreateSelectedGeometry(); - void recreateSelectedGeometry(const CSMWorld::Pathgrid& source); + void createGeometry(); + void createSelectedGeometry(); + void createSelectedGeometry(const CSMWorld::Pathgrid& source); void removePathgridGeometry(); void removeSelectedGeometry(); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 802194bea..d9a85fe35 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -212,32 +212,28 @@ void CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& { const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + int rowStart = -1; + int rowEnd = -1; + if (topLeft.parent().isValid()) { - int row = topLeft.parent().row(); - - const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); - if (mCellId == pathgrid.mId) - { - mCell->pathgridDataChanged(topLeft, bottomRight); - flagAsModified(); - } + rowStart = topLeft.parent().row(); + rowEnd = bottomRight.parent().row(); + } + else + { + rowStart = topLeft.row(); + rowEnd = bottomRight.row(); } -} - -void CSVRender::UnpagedWorldspaceWidget::pathgridRemoved (const QModelIndex& parent, int start, int end) -{ - const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); - - if (parent.isValid()){ - // Pathgrid data was modified - int row = parent.row(); + for (int row = rowStart; row <= rowEnd; ++row) + { const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); if (mCellId == pathgrid.mId) { - mCell->pathgridRowRemoved(parent, start, end); + mCell->pathgridModified(); flagAsModified(); + return; } } } @@ -256,6 +252,7 @@ void CSVRender::UnpagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelI { mCell->pathgridRemoved(); flagAsModified(); + return; } } } @@ -267,29 +264,17 @@ void CSVRender::UnpagedWorldspaceWidget::pathgridAdded (const QModelIndex& paren if (!parent.isValid()) { - // Pathgrid added theoretically, unable to test until it is possible to add pathgrids for (int row = start; row <= end; ++row) { const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); if (mCellId == pathgrid.mId) { - mCell->pathgridAdded(pathgrid); + mCell->pathgridModified(); flagAsModified(); + return; } } } - else - { - // Pathgrid data was modified - int row = parent.row(); - - const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); - if (mCellId == pathgrid.mId) - { - mCell->pathgridRowAdded(parent, start, end); - flagAsModified(); - } - } } void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index dd4a8a2f7..57e8d1a19 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -91,8 +91,6 @@ namespace CSVRender virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - virtual void pathgridRemoved (const QModelIndex& parent, int start, int end); - virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end); virtual void pathgridAdded (const QModelIndex& parent, int start, int end); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 64c1be0ee..ec40d880a 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -63,8 +63,6 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg connect (pathgrids, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (pathgridDataChanged (const QModelIndex&, const QModelIndex&))); - connect (pathgrids, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), - this, SLOT (pathgridRemoved (const QModelIndex&, int, int))); connect (pathgrids, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (pathgridAboutToBeRemoved (const QModelIndex&, int, int))); connect (pathgrids, SIGNAL (rowsInserted (const QModelIndex&, int, int)), diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 59aacca29..e6cd7aab0 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -246,8 +246,6 @@ namespace CSVRender virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0; - virtual void pathgridRemoved (const QModelIndex& parent, int start, int end) = 0; - virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0; virtual void pathgridAdded (const QModelIndex& parent, int start, int end) = 0; From 9d0228659229f6ffe57db0556c7b14ebfe1ac1cb Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 22:17:03 -0400 Subject: [PATCH 14/37] Remove unused forward declaration. --- apps/opencs/view/render/cell.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 523a6bb83..a5b581d24 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -31,7 +31,6 @@ namespace CSMWorld { class Data; class CellCoordinates; - class Pathgrid; } namespace CSVRender From 5a7ebab6943c2120740de27834b3e3f6fd0db9e8 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 16 May 2016 23:06:36 -0400 Subject: [PATCH 15/37] Fix forward declaration. --- apps/opencs/view/render/pathgrid.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index b192ed31b..7cab806d3 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -25,7 +25,7 @@ namespace CSMWorld { class CommandMacro; class Data; - class Pathgrid; + struct Pathgrid; } namespace CSVRender From 13c2161b27c8c94a9ad58391f9d90ba8be26cb58 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Tue, 17 May 2016 16:14:23 -0400 Subject: [PATCH 16/37] Fix memory leak, remove empty if statement, change dynamic_cast to static_cast --- apps/opencs/view/render/pathgrid.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 5c8868344..5b2dd4f98 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -24,7 +24,7 @@ namespace CSVRender virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { - PathgridTag* tag = dynamic_cast(node->getUserData()); + PathgridTag* tag = static_cast(node->getUserData()); tag->getPathgrid()->update(); } }; @@ -44,10 +44,6 @@ namespace CSVRender QString text("Pathgrid: "); text += mPathgrid->getId().c_str(); - if (!hideBasics) - { - } - return text; } @@ -462,7 +458,7 @@ namespace CSVRender if (mPathgridGeometry) { mPathgridGeode->removeDrawable(mPathgridGeometry); - mPathgridGeometry.release(); + mPathgridGeometry = 0; } } @@ -471,7 +467,7 @@ namespace CSVRender if (mSelectedGeometry) { mSelectedGeode->removeDrawable(mSelectedGeometry); - mSelectedGeometry.release(); + mSelectedGeometry = 0; } } From ae0d2c3b9c4434fcd8206a01d711b04bdef9bcbd Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Tue, 17 May 2016 21:24:16 -0400 Subject: [PATCH 17/37] Refactor edit mode. Remove essentially duplicate function. --- apps/opencs/view/render/editmode.cpp | 14 +- apps/opencs/view/render/editmode.hpp | 15 +- apps/opencs/view/render/instancemode.cpp | 20 +-- apps/opencs/view/render/instancemode.hpp | 10 +- apps/opencs/view/render/pathgridmode.cpp | 35 ++--- apps/opencs/view/render/pathgridmode.hpp | 8 +- apps/opencs/view/render/worldspacewidget.cpp | 145 +++++++------------ apps/opencs/view/render/worldspacewidget.hpp | 13 +- 8 files changed, 104 insertions(+), 156 deletions(-) diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index 22de54218..5f0852c90 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -37,29 +37,29 @@ void CSVRender::EditMode::primarySelectPressed (const WorldspaceHitResult& hit) void CSVRender::EditMode::secondarySelectPressed (const WorldspaceHitResult& hit) {} -bool CSVRender::EditMode::primaryEditStartDrag (const WorldspaceHitResult& hit) +bool CSVRender::EditMode::primaryEditStartDrag (const QPoint& pos) { return false; } -bool CSVRender::EditMode::secondaryEditStartDrag (const WorldspaceHitResult& hit) +bool CSVRender::EditMode::secondaryEditStartDrag (const QPoint& pos) { return false; } -bool CSVRender::EditMode::primarySelectStartDrag (const WorldspaceHitResult& hit) +bool CSVRender::EditMode::primarySelectStartDrag (const QPoint& pos) { return false; } -bool CSVRender::EditMode::secondarySelectStartDrag (const WorldspaceHitResult& hit) +bool CSVRender::EditMode::secondarySelectStartDrag (const QPoint& pos) { return false; } -void CSVRender::EditMode::drag (int diffX, int diffY, double speedFactor) {} +void CSVRender::EditMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) {} -void CSVRender::EditMode::dragCompleted(const WorldspaceHitResult& hit) {} +void CSVRender::EditMode::dragCompleted(const QPoint& pos) {} void CSVRender::EditMode::dragAborted() {} @@ -67,7 +67,7 @@ void CSVRender::EditMode::dragWheel (int diff, double speedFactor) {} void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {} -void CSVRender::EditMode::dropEvent (QDropEvent* event) {} +void CSVRender::EditMode::dropEvent (QDropEvent *event) {} void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index 37bbafdb1..f9b7027f9 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -8,6 +8,7 @@ class QDragEnterEvent; class QDropEvent; class QDragMoveEvent; +class QPoint; namespace CSVRender { @@ -53,28 +54,28 @@ namespace CSVRender /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool primaryEditStartDrag (const WorldspaceHitResult& hit); + virtual bool primaryEditStartDrag (const QPoint& pos); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool secondaryEditStartDrag (const WorldspaceHitResult& hit); + virtual bool secondaryEditStartDrag (const QPoint& pos); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool primarySelectStartDrag (const WorldspaceHitResult& hit); + virtual bool primarySelectStartDrag (const QPoint& pos); /// Default-implementation: ignore and return false /// /// \return Drag accepted? - virtual bool secondarySelectStartDrag (const WorldspaceHitResult& hit); + virtual bool secondarySelectStartDrag (const QPoint& pos); /// Default-implementation: ignored - virtual void drag (int diffX, int diffY, double speedFactor); + virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); /// Default-implementation: ignored - virtual void dragCompleted(const WorldspaceHitResult& hit); + virtual void dragCompleted(const QPoint& pos); /// Default-implementation: ignored /// @@ -89,7 +90,7 @@ namespace CSVRender virtual void dragEnterEvent (QDragEnterEvent *event); /// Default-implementation: ignored - virtual void dropEvent (QDropEvent* event); + virtual void dropEvent (QDropEvent *event); /// Default-implementation: ignored virtual void dragMoveEvent (QDragMoveEvent *event); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 958d29337..df5ba7621 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -2,6 +2,7 @@ #include "instancemode.hpp" #include +#include #include "../../model/prefs/state.hpp" @@ -145,11 +146,12 @@ void CSVRender::InstanceMode::secondarySelectPressed (const WorldspaceHitResult& } } -bool CSVRender::InstanceMode::primaryEditStartDrag (const WorldspaceHitResult& hit) +bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) { if (mDragMode!=DragMode_None || mLocked) return false; + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { getWorldspaceWidget().clearSelection (Mask_Reference); @@ -189,7 +191,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const WorldspaceHitResult& h return true; } -bool CSVRender::InstanceMode::secondaryEditStartDrag (const WorldspaceHitResult& hit) +bool CSVRender::InstanceMode::secondaryEditStartDrag (const QPoint& pos) { if (mLocked) return false; @@ -197,7 +199,7 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag (const WorldspaceHitResult& return false; } -void CSVRender::InstanceMode::drag (int diffX, int diffY, double speedFactor) +void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) { osg::Vec3f eye; osg::Vec3f centre; @@ -244,7 +246,7 @@ void CSVRender::InstanceMode::drag (int diffX, int diffY, double speedFactor) } } -void CSVRender::InstanceMode::dragCompleted(const WorldspaceHitResult& hit) +void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) { std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); @@ -333,9 +335,9 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) if (!mime->fromDocument (document)) return; - osg::Vec3f insertPoint = getWorldspaceWidget().getIntersectionPoint (event->pos()); + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (event->pos(), getWorldspaceWidget().getInteractionMask()); - std::string cellId = getWorldspaceWidget().getCellId (insertPoint); + std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); CSMWorld::IdTree& cellTable = dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); @@ -412,11 +414,11 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) createCommand->addValue (referencesTable.findColumnIndex ( CSMWorld::Columns::ColumnId_Cell), QString::fromUtf8 (cellId.c_str())); createCommand->addValue (referencesTable.findColumnIndex ( - CSMWorld::Columns::ColumnId_PositionXPos), insertPoint.x()); + CSMWorld::Columns::ColumnId_PositionXPos), hit.worldPos.x()); createCommand->addValue (referencesTable.findColumnIndex ( - CSMWorld::Columns::ColumnId_PositionYPos), insertPoint.y()); + CSMWorld::Columns::ColumnId_PositionYPos), hit.worldPos.y()); createCommand->addValue (referencesTable.findColumnIndex ( - CSMWorld::Columns::ColumnId_PositionZPos), insertPoint.z()); + CSMWorld::Columns::ColumnId_PositionZPos), hit.worldPos.z()); createCommand->addValue (referencesTable.findColumnIndex ( CSMWorld::Columns::ColumnId_ReferenceableId), QString::fromUtf8 (iter->getId().c_str())); diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 39e3773ad..4f7937b51 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -49,13 +49,13 @@ namespace CSVRender virtual void secondarySelectPressed (const WorldspaceHitResult& hit); - virtual bool primaryEditStartDrag (const WorldspaceHitResult& hit); + virtual bool primaryEditStartDrag (const QPoint& pos); - virtual bool secondaryEditStartDrag (const WorldspaceHitResult& hit); + virtual bool secondaryEditStartDrag (const QPoint& pos); - virtual void drag (int diffX, int diffY, double speedFactor); + virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); - virtual void dragCompleted(const WorldspaceHitResult& hit); + virtual void dragCompleted(const QPoint& pos); /// \note dragAborted will not be called, if the drag is aborted via changing /// editing mode @@ -65,7 +65,7 @@ namespace CSVRender virtual void dragEnterEvent (QDragEnterEvent *event); - virtual void dropEvent (QDropEvent* event); + virtual void dropEvent (QDropEvent *event); virtual int getSubMode() const; diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index d50b42d35..3a2cc600a 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -1,6 +1,7 @@ #include "pathgridmode.hpp" #include +#include #include @@ -69,27 +70,8 @@ namespace CSVRender void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult) { - // Determine placement - osg::Vec3d position; - - if (hitResult.hit) - { - position = hitResult.worldPos; - } - else - { - const double DefaultDistance = 500.f; - - osg::Vec3d eye, center, up, offset; - getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, center, up); - - osg::Vec3d direction = center - eye; - direction.normalize(); - position = eye + direction * DefaultDistance; - } - // Get pathgrid cell - Cell* cell = getWorldspaceWidget().getCell (position); + Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos); if (cell) { // Add node @@ -97,7 +79,7 @@ namespace CSVRender QString description = "Connect node to selected nodes"; CSMWorld::CommandMacro macro(undoStack, description); - cell->getPathgrid()->applyPoint(macro, position); + cell->getPathgrid()->applyPoint(macro, hitResult.worldPos); } } @@ -158,7 +140,7 @@ namespace CSVRender getWorldspaceWidget().clearSelection(Mask_Pathgrid); } - bool PathgridMode::primaryEditStartDrag(const WorldspaceHitResult& hit) + bool PathgridMode::primaryEditStartDrag(const QPoint& pos) { std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); @@ -170,8 +152,9 @@ namespace CSVRender return true; } - bool PathgridMode::secondaryEditStartDrag(const WorldspaceHitResult& hit) + bool PathgridMode::secondaryEditStartDrag(const QPoint& pos) { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); if (hit.tag) { if (PathgridTag* tag = dynamic_cast(hit.tag.get())) @@ -187,7 +170,7 @@ namespace CSVRender return true; } - void PathgridMode::drag(int diffX, int diffY, double speedFactor) + void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor) { std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); @@ -212,7 +195,7 @@ namespace CSVRender } } - void PathgridMode::dragCompleted(const WorldspaceHitResult& hit) + void PathgridMode::dragCompleted(const QPoint& pos) { if (mDragMode == DragMode_Move) { @@ -231,6 +214,8 @@ namespace CSVRender } else if (mDragMode == DragMode_Edge) { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + if (hit.tag) { if (PathgridTag* tag = dynamic_cast(hit.tag.get())) diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp index 6a070333d..b6933cb8e 100644 --- a/apps/opencs/view/render/pathgridmode.hpp +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -27,13 +27,13 @@ namespace CSVRender virtual void secondarySelectPressed(const WorldspaceHitResult& hit); - virtual bool primaryEditStartDrag (const WorldspaceHitResult& hit); + virtual bool primaryEditStartDrag (const QPoint& pos); - virtual bool secondaryEditStartDrag (const WorldspaceHitResult& hit); + virtual bool secondaryEditStartDrag (const QPoint& pos); - virtual void drag (int diffX, int diffY, double speedFactor); + virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); - virtual void dragCompleted(const WorldspaceHitResult& hit); + virtual void dragCompleted(const QPoint& pos); /// \note dragAborted will not be called, if the drag is aborted via changing /// editing mode diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index ec40d880a..1859401dc 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -326,39 +326,64 @@ CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument() return mDocument; } -osg::Vec3f CSVRender::WorldspaceWidget::getIntersectionPoint (const QPoint& localPos, - unsigned int interactionMask, bool ignoreHidden) const +CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos, + unsigned int interactionMask) const { // (0,0) is considered the lower left corner of an OpenGL window int x = localPos.x(); int y = height() - localPos.y(); - osg::ref_ptr intersector ( - new osgUtil::LineSegmentIntersector (osgUtil::Intersector::WINDOW, x, y)); + // Get intersection + osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( + osgUtil::Intersector::WINDOW, x, y)); - intersector->setIntersectionLimit (osgUtil::LineSegmentIntersector::NO_LIMIT); - osgUtil::IntersectionVisitor visitor (intersector); - - unsigned int mask = interactionMask; - - if (ignoreHidden) - mask &= getVisibilityMask(); + intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); + osgUtil::IntersectionVisitor visitor(intersector); - visitor.setTraversalMask (mask); + visitor.setTraversalMask(interactionMask); - mView->getCamera()->accept (visitor); + mView->getCamera()->accept(visitor); - for (osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersector->getIntersections().begin(); - iter!=intersector->getIntersections().end(); ++iter) + // Get relevant data + for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + it != intersector->getIntersections().end(); ++it) { + osgUtil::LineSegmentIntersector::Intersection intersection = *it; + // reject back-facing polygons - osg::Vec3f normal = osg::Matrix::transform3x3 ( - iter->getWorldIntersectNormal(), mView->getCamera()->getViewMatrix()); + osg::Vec3f normal = intersection.getWorldIntersectNormal(); + normal = osg::Matrix::transform3x3(normal, mView->getCamera()->getViewMatrix()); + if (normal.z() < 0) + continue; - if (normal.z()>=0) - return iter->getWorldIntersectPoint(); + for (std::vector::iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) + { + osg::Node* node = *it; + if (osg::ref_ptr tag = dynamic_cast(node->getUserData())) + { + WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() }; + if (intersection.indexList.size() >= 3) + { + hit.index0 = intersection.indexList[0]; + hit.index1 = intersection.indexList[1]; + hit.index2 = intersection.indexList[2]; + } + return hit; + } + } + + // Something untagged, probably terrain + WorldspaceHitResult hit = { true, 0, 0, 0, 0, intersection.getWorldIntersectPoint() }; + if (intersection.indexList.size() >= 3) + { + hit.index0 = intersection.indexList[0]; + hit.index1 = intersection.indexList[1]; + hit.index2 = intersection.indexList[2]; + } + return hit; } + // Default placement osg::Matrixd matrix; matrix.preMult (mView->getCamera()->getViewport()->computeWindowMatrix()); matrix.preMult (mView->getCamera()->getProjectionMatrix()); @@ -368,10 +393,12 @@ osg::Vec3f CSVRender::WorldspaceWidget::getIntersectionPoint (const QPoint& loca osg::Vec3d start = matrix.preMult (intersector->getStart()); osg::Vec3d end = matrix.preMult (intersector->getEnd()); - osg::Vec3d direction = end-start; + osg::Vec3d direction = end - start; direction.normalize(); + direction *= CSMPrefs::get()["Scene Drops"]["distance"].toInt(); - return start + direction * CSMPrefs::get()["Scene Drops"]["distance"].toInt(); + WorldspaceHitResult hit = { false, 0, 0, 0, 0, start + direction }; + return hit; } void CSVRender::WorldspaceWidget::abortDrag() @@ -465,63 +492,6 @@ bool CSVRender::WorldspaceWidget::storeMappingSetting (const CSMPrefs::Setting * return SceneWidget::storeMappingSetting(setting); } -CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos) -{ - // (0,0) is considered the lower left corner of an OpenGL window - int x = localPos.x(); - int y = height() - localPos.y(); - - osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y)); - - intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); - osgUtil::IntersectionVisitor visitor(intersector); - - visitor.setTraversalMask(getInteractionMask()); - - mView->getCamera()->accept(visitor); - - for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); - it != intersector->getIntersections().end(); ++it) - { - osgUtil::LineSegmentIntersector::Intersection intersection = *it; - - // reject back-facing polygons - osg::Vec3f normal = intersection.getWorldIntersectNormal(); - normal = osg::Matrix::transform3x3(normal, mView->getCamera()->getViewMatrix()); - if (normal.z() < 0) - continue; - - for (std::vector::iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) - { - osg::Node* node = *it; - if (osg::ref_ptr tag = dynamic_cast(node->getUserData())) - { - WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() }; - if (intersection.indexList.size() >= 3) - { - hit.index0 = intersection.indexList[0]; - hit.index1 = intersection.indexList[1]; - hit.index2 = intersection.indexList[2]; - } - return hit; - } - } - - // Something untagged, probably terrain - WorldspaceHitResult hit = { true, 0, 0, 0, 0, intersection.getWorldIntersectPoint() }; - if (intersection.indexList.size() >= 3) - { - hit.index0 = intersection.indexList[0]; - hit.index1 = intersection.indexList[1]; - hit.index2 = intersection.indexList[2]; - } - return hit; - } - - WorldspaceHitResult hit = { false, 0, 0, 0, 0, osg::Vec3d() }; - return hit; -} - void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); @@ -606,7 +576,7 @@ void CSVRender::WorldspaceWidget::showToolTip() { QPoint pos = QCursor::pos(); - WorldspaceHitResult hit = mousePick (mapFromGlobal (pos)); + WorldspaceHitResult hit = mousePick (mapFromGlobal (pos), getInteractionMask()); if (hit.tag) { bool hideBasics = CSMPrefs::get()["Tooltips"]["scene-hide-basic"].isTrue(); @@ -643,22 +613,20 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); - editMode.drag (diffX, diffY, factor); + editMode.drag (event->pos(), diffX, diffY, factor); } else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="p-select" || mDragMode=="s-select") { - WorldspaceHitResult hit = mousePick (event->pos()); - EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); if (mDragMode=="p-edit") - mDragging = editMode.primaryEditStartDrag (hit); + mDragging = editMode.primaryEditStartDrag (event->pos()); else if (mDragMode=="s-edit") - mDragging = editMode.secondaryEditStartDrag (hit); + mDragging = editMode.secondaryEditStartDrag (event->pos()); else if (mDragMode=="p-select") - mDragging = editMode.primarySelectStartDrag (hit); + mDragging = editMode.primarySelectStartDrag (event->pos()); else if (mDragMode=="s-select") - mDragging = editMode.secondarySelectStartDrag (hit); + mDragging = editMode.secondarySelectStartDrag (event->pos()); if (mDragging) { @@ -710,14 +678,13 @@ void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event) if (mDragging) { EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); - WorldspaceHitResult hit = mousePick (event->pos()); - editMode.dragCompleted(hit); + editMode.dragCompleted(event->pos()); mDragging = false; } else { - WorldspaceHitResult hit = mousePick (event->pos()); + WorldspaceHitResult hit = mousePick(event->pos(), getInteractionMask()); handleMouseClick (hit, button, event->modifiers() & Qt::ShiftModifier); } diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index e6cd7aab0..92d31eb9e 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -149,16 +149,11 @@ namespace CSVRender /// \param elementMask Elements to be affected by the select operation virtual void selectAllWithSameParentId (int elementMask) = 0; - /// Return the next intersection point with scene elements matched by + /// Return the next intersection with scene elements matched by /// \a interactionMask based on \a localPos and the camera vector. - /// If there is no such point, instead a point "in front" of \a localPos will be + /// If there is no such intersection, instead a point "in front" of \a localPos will be /// returned. - /// - /// \param ignoreHidden ignore elements specified in interactionMask that are - /// flagged as not visible. - osg::Vec3f getIntersectionPoint (const QPoint& localPos, - unsigned int interactionMask = Mask_Reference | Mask_Terrain, - bool ignoreHidden = false) const; + WorldspaceHitResult mousePick (const QPoint& localPos, unsigned int interactionMask) const; virtual std::string getCellId (const osg::Vec3f& point) const = 0; @@ -225,8 +220,6 @@ namespace CSVRender void dragMoveEvent(QDragMoveEvent *event); - WorldspaceHitResult mousePick (const QPoint& localPos); - virtual std::string getStartupInstruction() = 0; private slots: From e8e915bcdecaf2c35389d92f57ce2867372b0a2e Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 18 May 2016 10:46:25 -0400 Subject: [PATCH 18/37] Share selection functionality with instance editing mode. --- apps/opencs/CMakeLists.txt | 2 +- .../view/render/instanceselectionmode.cpp | 102 ++++++------------ .../view/render/instanceselectionmode.hpp | 30 ++---- apps/opencs/view/render/pathgridmode.cpp | 82 ++------------ apps/opencs/view/render/pathgridmode.hpp | 18 +--- .../view/render/pathgridselectionmode.cpp | 71 ++++++++++++ .../view/render/pathgridselectionmode.hpp | 38 +++++++ apps/opencs/view/render/selectionmode.cpp | 77 +++++++++++++ apps/opencs/view/render/selectionmode.hpp | 51 +++++++++ 9 files changed, 293 insertions(+), 178 deletions(-) create mode 100644 apps/opencs/view/render/pathgridselectionmode.cpp create mode 100644 apps/opencs/view/render/pathgridselectionmode.hpp create mode 100644 apps/opencs/view/render/selectionmode.cpp create mode 100644 apps/opencs/view/render/selectionmode.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index b33dc1508..f8cf1e2d8 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -86,7 +86,7 @@ opencs_units (view/widget opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode - orbitcameramode pathgridmode + orbitcameramode pathgridmode selectionmode pathgridselectionmode ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/instanceselectionmode.cpp b/apps/opencs/view/render/instanceselectionmode.cpp index 5c3aaa8d1..bf8ede0eb 100644 --- a/apps/opencs/view/render/instanceselectionmode.cpp +++ b/apps/opencs/view/render/instanceselectionmode.cpp @@ -1,4 +1,3 @@ - #include "instanceselectionmode.hpp" #include @@ -10,84 +9,49 @@ #include "worldspacewidget.hpp" #include "object.hpp" -bool CSVRender::InstanceSelectionMode::createContextMenu (QMenu *menu) +namespace CSVRender { - if (menu) + InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) + : SelectionMode(parent, worldspaceWidget, Mask_Reference) { - menu->addAction (mSelectAll); - menu->addAction (mDeselectAll); - menu->addAction (mSelectSame); - menu->addAction (mDeleteSelection); - } + mSelectSame = new QAction("Extend selection to instances with same object ID", this); + mDeleteSelection = new QAction("Delete selected instances", this); - return true; -} + connect(mSelectSame, SIGNAL(triggered()), this, SLOT(selectSame())); + connect(mDeleteSelection, SIGNAL(triggered()), this, SLOT(deleteSelection())); + } -CSVRender::InstanceSelectionMode::InstanceSelectionMode (CSVWidget::SceneToolbar *parent, - WorldspaceWidget& worldspaceWidget) -: CSVWidget::SceneToolMode (parent, "Selection Mode"), mWorldspaceWidget (worldspaceWidget) -{ - addButton (":placeholder", "cube-centre", - "Centred cube" - "

    • Drag with primary (make instances the selection) or secondary (invert selection state) select button from the centre of the selection cube outwards
    • " - "
    • The selection cube is aligned to the word space axis
    • " - "
    • If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect
    • " - "
    " - "Not implemented yet"); - addButton (":placeholder", "cube-corner", - "Cube corner to corner" - "
    • Drag with primary (make instances the selection) or secondary (invert selection state) select button from one corner of the selection cube to the opposite corner
    • " - "
    • The selection cube is aligned to the word space axis
    • " - "
    • If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect
    • " - "
    " - "Not implemented yet"); - addButton (":placeholder", "sphere", - "Centred sphere" - "
    • Drag with primary (make instances the selection) or secondary (invert selection state) select button from the centre of the selection sphere outwards
    • " - "
    • If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect
    • " - "
    " - "Not implemented yet"); + bool InstanceSelectionMode::createContextMenu(QMenu* menu) + { + if (menu) + { + SelectionMode::createContextMenu(menu); - mSelectAll = new QAction ("Select all instances", this); - mDeselectAll = new QAction ("Clear selection", this); - mDeleteSelection = new QAction ("Delete selected instances", this); - mSelectSame = new QAction ("Extend selection to instances with same object ID", this); - connect (mSelectAll, SIGNAL (triggered ()), this, SLOT (selectAll())); - connect (mDeselectAll, SIGNAL (triggered ()), this, SLOT (clearSelection())); - connect (mDeleteSelection, SIGNAL (triggered ()), this, SLOT (deleteSelection())); - connect (mSelectSame, SIGNAL (triggered ()), this, SLOT (selectSame())); -} + menu->addAction(mSelectSame); + menu->addAction(mDeleteSelection); + } -void CSVRender::InstanceSelectionMode::selectAll() -{ - mWorldspaceWidget.selectAll (Mask_Reference); -} + return true; + } -void CSVRender::InstanceSelectionMode::clearSelection() -{ - mWorldspaceWidget.clearSelection (Mask_Reference); -} + void InstanceSelectionMode::selectSame() + { + getWorldspaceWidget().selectAllWithSameParentId(Mask_Reference); + } -void CSVRender::InstanceSelectionMode::deleteSelection() -{ - std::vector > selection = - mWorldspaceWidget.getSelection (Mask_Reference); + void InstanceSelectionMode::deleteSelection() + { + std::vector > selection = getWorldspaceWidget().getSelection(Mask_Reference); - CSMWorld::IdTable& referencesTable = - dynamic_cast (*mWorldspaceWidget.getDocument().getData(). - getTableModel (CSMWorld::UniversalId::Type_References)); + CSMWorld::IdTable& referencesTable = dynamic_cast( + *getWorldspaceWidget().getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_References)); - for (std::vector >::iterator iter (selection.begin()); - iter!=selection.end(); ++iter) - { - CSMWorld::DeleteCommand *command = new CSMWorld::DeleteCommand (referencesTable, - static_cast (iter->get())->mObject->getReferenceId()); + for (std::vector >::iterator iter = selection.begin(); iter != selection.end(); ++iter) + { + CSMWorld::DeleteCommand* command = new CSMWorld::DeleteCommand(referencesTable, + static_cast(iter->get())->mObject->getReferenceId()); - mWorldspaceWidget.getDocument().getUndoStack().push (command); + getWorldspaceWidget().getDocument().getUndoStack().push(command); + } } } - -void CSVRender::InstanceSelectionMode::selectSame() -{ - mWorldspaceWidget.selectAllWithSameParentId (Mask_Reference); -} diff --git a/apps/opencs/view/render/instanceselectionmode.hpp b/apps/opencs/view/render/instanceselectionmode.hpp index cac2ca8c2..a590240d9 100644 --- a/apps/opencs/view/render/instanceselectionmode.hpp +++ b/apps/opencs/view/render/instanceselectionmode.hpp @@ -1,23 +1,19 @@ #ifndef CSV_RENDER_INSTANCE_SELECTION_MODE_H #define CSV_RENDER_INSTANCE_SELECTION_MODE_H -#include "../widget/scenetoolmode.hpp" - -class QAction; +#include "selectionmode.hpp" namespace CSVRender { - class WorldspaceWidget; - - class InstanceSelectionMode : public CSVWidget::SceneToolMode + class InstanceSelectionMode : public SelectionMode { Q_OBJECT - WorldspaceWidget& mWorldspaceWidget; - QAction *mSelectAll; - QAction *mDeselectAll; - QAction *mDeleteSelection; - QAction *mSelectSame; + public: + + InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget); + + protected: /// Add context menu items to \a menu. /// @@ -25,20 +21,16 @@ namespace CSVRender /// /// \return Have there been any menu items to be added (if menu is 0 and there /// items to be added, the function must return true anyway. - virtual bool createContextMenu (QMenu *menu); + bool createContextMenu(QMenu* menu); - public: + private: - InstanceSelectionMode (CSVWidget::SceneToolbar *parent, WorldspaceWidget& worldspaceWidget); + QAction* mDeleteSelection; + QAction* mSelectSame; private slots: - void selectAll(); - - void clearSelection(); - void deleteSelection(); - void selectSame(); }; } diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 3a2cc600a..0dd458346 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -10,9 +10,12 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" +#include "../widget/scenetoolbar.hpp" + #include "cell.hpp" #include "mask.hpp" #include "pathgrid.hpp" +#include "pathgridselectionmode.hpp" #include "worldspacewidget.hpp" namespace CSVRender @@ -22,6 +25,7 @@ namespace CSVRender getTooltip(), parent) , mDragMode(DragMode_None) , mFromNode(0) + , mSelectionMode(0) { } @@ -39,33 +43,13 @@ namespace CSVRender void PathgridMode::activate(CSVWidget::SceneToolbar* toolbar) { - mSelectAll = new QAction("Select all other nodes in cell", this); - mInvertSelection = new QAction("Invert selection", this); - mClearSelection = new QAction("Clear selection", this); - mRemoveSelected = new QAction("Remove selected nodes", this); - mRemoveSelectedEdges = new QAction("Remove edges between selected nodes", this); - - connect(mSelectAll, SIGNAL(triggered()), this, SLOT(selectAll())); - connect(mInvertSelection, SIGNAL(triggered()), this, SLOT(invertSelection())); - connect(mClearSelection, SIGNAL(triggered()), this, SLOT(clearSelection())); - connect(mRemoveSelected, SIGNAL(triggered()), this, SLOT(removeSelected())); - connect(mRemoveSelectedEdges, SIGNAL(triggered()), this, SLOT(removeSelectedEdges())); - - EditMode::activate(toolbar); - } - - bool PathgridMode::createContextMenu(QMenu* menu) - { - if (menu) + if (!mSelectionMode) { - menu->addAction(mSelectAll); - menu->addAction(mInvertSelection); - menu->addAction(mClearSelection); - menu->addAction(mRemoveSelected); - menu->addAction(mRemoveSelectedEdges); + mSelectionMode = new PathgridSelectionMode(toolbar, getWorldspaceWidget()); } - return true; + EditMode::activate(toolbar); + toolbar->addTool(mSelectionMode); } void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult) @@ -245,54 +229,4 @@ namespace CSVRender { getWorldspaceWidget().reset(Mask_Pathgrid); } - - void PathgridMode::selectAll() - { - // Select rest of nodes in selected cell - getWorldspaceWidget().selectAll(Mask_Pathgrid); - } - - void PathgridMode::invertSelection() - { - getWorldspaceWidget().invertSelection(Mask_Pathgrid); - } - - void PathgridMode::clearSelection() - { - getWorldspaceWidget().clearSelection(Mask_Pathgrid); - } - - void PathgridMode::removeSelected() - { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); - - for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) - { - if (PathgridTag* tag = dynamic_cast(it->get())) - { - QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); - QString description = "Remove selected nodes"; - - CSMWorld::CommandMacro macro(undoStack, description); - tag->getPathgrid()->applyRemoveNodes(macro); - } - } - } - - void PathgridMode::removeSelectedEdges() - { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); - - for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) - { - if (PathgridTag* tag = dynamic_cast(it->get())) - { - QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); - QString description = "Remove edges between selected nodes"; - - CSMWorld::CommandMacro macro(undoStack, description); - tag->getPathgrid()->applyRemoveEdges(macro); - } - } - } } diff --git a/apps/opencs/view/render/pathgridmode.hpp b/apps/opencs/view/render/pathgridmode.hpp index b6933cb8e..908eefa5b 100644 --- a/apps/opencs/view/render/pathgridmode.hpp +++ b/apps/opencs/view/render/pathgridmode.hpp @@ -7,6 +7,8 @@ namespace CSVRender { + class PathgridSelectionMode; + class PathgridMode : public EditMode { Q_OBJECT @@ -17,8 +19,6 @@ namespace CSVRender virtual void activate(CSVWidget::SceneToolbar* toolbar); - virtual bool createContextMenu(QMenu* menu); - virtual void primaryEditPressed(const WorldspaceHitResult& hit); virtual void secondaryEditPressed(const WorldspaceHitResult& hit); @@ -52,21 +52,9 @@ namespace CSVRender std::string mLastId, mEdgeId; unsigned short mFromNode; - QAction* mSelectAll; - QAction* mInvertSelection; - QAction* mClearSelection; - QAction* mRemoveSelected; - QAction* mRemoveSelectedEdges; + PathgridSelectionMode* mSelectionMode; QString getTooltip(); - - private slots: - - void selectAll(); - void invertSelection(); - void clearSelection(); - void removeSelected(); - void removeSelectedEdges(); }; } diff --git a/apps/opencs/view/render/pathgridselectionmode.cpp b/apps/opencs/view/render/pathgridselectionmode.cpp new file mode 100644 index 000000000..db41faf50 --- /dev/null +++ b/apps/opencs/view/render/pathgridselectionmode.cpp @@ -0,0 +1,71 @@ +#include "pathgridselectionmode.hpp" + +#include +#include + +#include "../../model/world/idtable.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/commandmacro.hpp" + +#include "worldspacewidget.hpp" +#include "pathgrid.hpp" + +namespace CSVRender +{ + PathgridSelectionMode::PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) + : SelectionMode(parent, worldspaceWidget, Mask_Pathgrid) + { + mRemoveSelectedNodes = new QAction("Remove selected nodes", this); + mRemoveSelectedEdges = new QAction("Remove edges between selected nodes", this); + + connect(mRemoveSelectedNodes, SIGNAL(triggered()), this, SLOT(removeSelectedNodes())); + connect(mRemoveSelectedEdges, SIGNAL(triggered()), this, SLOT(removeSelectedEdges())); + } + + bool PathgridSelectionMode::createContextMenu(QMenu* menu) + { + if (menu) + { + SelectionMode::createContextMenu(menu); + + menu->addAction(mRemoveSelectedNodes); + menu->addAction(mRemoveSelectedEdges); + } + + return true; + } + + void PathgridSelectionMode::removeSelectedNodes() + { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + { + if (PathgridTag* tag = dynamic_cast(it->get())) + { + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Remove selected nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyRemoveNodes(macro); + } + } + } + + void PathgridSelectionMode::removeSelectedEdges() + { + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + { + if (PathgridTag* tag = dynamic_cast(it->get())) + { + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + QString description = "Remove edges between selected nodes"; + + CSMWorld::CommandMacro macro(undoStack, description); + tag->getPathgrid()->applyRemoveEdges(macro); + } + } + } +} diff --git a/apps/opencs/view/render/pathgridselectionmode.hpp b/apps/opencs/view/render/pathgridselectionmode.hpp new file mode 100644 index 000000000..e4cb1e044 --- /dev/null +++ b/apps/opencs/view/render/pathgridselectionmode.hpp @@ -0,0 +1,38 @@ +#ifndef CSV_RENDER_PATHGRID_SELECTION_MODE_H +#define CSV_RENDER_PATHGRID_SELECTION_MODE_H + +#include "selectionmode.hpp" + +namespace CSVRender +{ + class PathgridSelectionMode : public SelectionMode + { + Q_OBJECT + + public: + + PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget); + + protected: + + /// Add context menu items to \a menu. + /// + /// \attention menu can be a 0-pointer + /// + /// \return Have there been any menu items to be added (if menu is 0 and there + /// items to be added, the function must return true anyway. + bool createContextMenu(QMenu* menu); + + private: + + QAction* mRemoveSelectedNodes; + QAction* mRemoveSelectedEdges; + + private slots: + + void removeSelectedNodes(); + void removeSelectedEdges(); + }; +} + +#endif diff --git a/apps/opencs/view/render/selectionmode.cpp b/apps/opencs/view/render/selectionmode.cpp new file mode 100644 index 000000000..82a3c49e4 --- /dev/null +++ b/apps/opencs/view/render/selectionmode.cpp @@ -0,0 +1,77 @@ +#include "selectionmode.hpp" + +#include +#include + +#include "worldspacewidget.hpp" + +namespace CSVRender +{ + SelectionMode::SelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, + unsigned int interactionMask) + : SceneToolMode(parent, "Selection mode") + , mWorldspaceWidget(worldspaceWidget) + , mInteractionMask(interactionMask) + { + addButton(":placeholder", "cube-centre", + "Centred cube" + "
    • Drag with primary (make instances the selection) or secondary (invert selection state) select button from the centre of the selection cube outwards
    • " + "
    • The selection cube is aligned to the word space axis
    • " + "
    • If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect
    • " + "
    " + "Not implemented yet"); + addButton(":placeholder", "cube-corner", + "Cube corner to corner" + "
    • Drag with primary (make instances the selection) or secondary (invert selection state) select button from one corner of the selection cube to the opposite corner
    • " + "
    • The selection cube is aligned to the word space axis
    • " + "
    • If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect
    • " + "
    " + "Not implemented yet"); + addButton(":placeholder", "sphere", + "Centred sphere" + "
    • Drag with primary (make instances the selection) or secondary (invert selection state) select button from the centre of the selection sphere outwards
    • " + "
    • If context selection mode is enabled, a drag with primary/secondary edit not starting on an instance will have the same effect
    • " + "
    " + "Not implemented yet"); + + mSelectAll = new QAction("Select all", this); + mDeselectAll = new QAction("Clear selection", this); + mInvertSelection = new QAction("Invert selection", this); + + connect(mSelectAll, SIGNAL(triggered()), this, SLOT(selectAll())); + connect(mDeselectAll, SIGNAL(triggered()), this, SLOT(clearSelection())); + connect(mInvertSelection, SIGNAL(triggered()), this, SLOT(invertSelection())); + } + + WorldspaceWidget& SelectionMode::getWorldspaceWidget() + { + return mWorldspaceWidget; + } + + bool SelectionMode::createContextMenu (QMenu* menu) + { + if (menu) + { + menu->addAction(mSelectAll); + menu->addAction(mDeselectAll); + menu->addAction(mInvertSelection); + } + + return true; + } + + void SelectionMode::selectAll() + { + getWorldspaceWidget().selectAll(mInteractionMask); + } + + void SelectionMode::clearSelection() + { + getWorldspaceWidget().clearSelection(mInteractionMask); + } + + void SelectionMode::invertSelection() + { + getWorldspaceWidget().invertSelection(mInteractionMask); + } +} diff --git a/apps/opencs/view/render/selectionmode.hpp b/apps/opencs/view/render/selectionmode.hpp new file mode 100644 index 000000000..f28888bfd --- /dev/null +++ b/apps/opencs/view/render/selectionmode.hpp @@ -0,0 +1,51 @@ +#ifndef CSV_RENDER_SELECTION_MODE_H +#define CSV_RENDER_SELECTION_MODE_H + +#include "../widget/scenetoolmode.hpp" + +#include "mask.hpp" + +class QAction; + +namespace CSVRender +{ + class WorldspaceWidget; + + class SelectionMode : public CSVWidget::SceneToolMode + { + Q_OBJECT + + public: + + SelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, + unsigned int interactionMask); + + protected: + + WorldspaceWidget& getWorldspaceWidget(); + + /// Add context menu items to \a menu. + /// + /// \attention menu can be a 0-pointer + /// + /// \return Have there been any menu items to be added (if menu is 0 and there + /// items to be added, the function must return true anyway. + virtual bool createContextMenu (QMenu* menu); + + private: + + WorldspaceWidget& mWorldspaceWidget; + unsigned int mInteractionMask; + QAction* mSelectAll; + QAction* mDeselectAll; + QAction* mInvertSelection; + + protected slots: + + virtual void selectAll(); + virtual void clearSelection(); + virtual void invertSelection(); + }; +} + +#endif From 1781aaaa2721401c8b097a4e3c6c12529d2c9bb3 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 18 May 2016 12:17:26 -0400 Subject: [PATCH 19/37] Drag indicator. --- apps/opencs/view/render/cell.cpp | 2 +- apps/opencs/view/render/pathgrid.cpp | 69 ++++++++++++++++++++++-- apps/opencs/view/render/pathgrid.hpp | 9 +++- apps/opencs/view/render/pathgridmode.cpp | 30 ++++++++--- components/sceneutil/pathgridutil.cpp | 2 - components/sceneutil/pathgridutil.hpp | 3 ++ 6 files changed, 100 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index b408503d1..dc22fd511 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -433,5 +433,5 @@ void CSVRender::Cell::reset (unsigned int elementMask) iter!=mObjects.end(); ++iter) iter->second->reset(); if (elementMask & Mask_Pathgrid) - mPathgrid->resetMove(); + mPathgrid->resetIndicators(); } diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 5b2dd4f98..7c6c8eb9e 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -61,6 +62,7 @@ namespace CSVRender , mParent(parent) , mPathgridGeometry(0) , mSelectedGeometry(0) + , mConnectionGeometry(0) , mTag(new PathgridTag(this)) { const float CoordScalar = ESM::Land::REAL_SIZE; @@ -189,12 +191,50 @@ namespace CSVRender createSelectedGeometry(); } - void Pathgrid::resetMove() + void Pathgrid::adjustConnectionIndicator(unsigned short node) + { + if (mConnectionIndicator) + { + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + const CSMWorld::Pathgrid::Point& pointA = source->mPoints[mConnectionNode]; + const CSMWorld::Pathgrid::Point& pointB = source->mPoints[node]; + + osg::Vec3f start = osg::Vec3f(pointA.mX, pointA.mY, pointA.mZ + SceneUtil::DiamondHalfHeight); + osg::Vec3f end = osg::Vec3f(pointB.mX, pointB.mY, pointB.mZ + SceneUtil::DiamondHalfHeight); + + createConnectionGeometry(start, end); + } + } + } + + void Pathgrid::adjustConnectionIndicator(const osg::Vec3d& pos) + { + if (mConnectionIndicator) + { + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) + { + const CSMWorld::Pathgrid::Point& point = source->mPoints[mConnectionNode]; + + osg::Vec3f start = osg::Vec3f(point.mX, point.mY, point.mZ + SceneUtil::DiamondHalfHeight); + osg::Vec3f end = pos - mBaseNode->getPosition(); + createConnectionGeometry(start, end); + } + } + } + + void Pathgrid::resetIndicators() { mSelectedNode->setPosition(osg::Vec3f(0,0,0)); if (mConnectionIndicator) { mConnectionIndicator = false; + + mPathgridGeode->removeDrawable(mConnectionGeometry); + mConnectionGeometry = 0; + createSelectedGeometry(); } } @@ -379,8 +419,6 @@ namespace CSVRender void Pathgrid::removeGeometry() { mRemoveGeometry = true; - - } void Pathgrid::update() @@ -471,6 +509,31 @@ namespace CSVRender } } + void Pathgrid::createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end) + { + if (mConnectionGeometry) + mPathgridGeode->removeDrawable(mConnectionGeometry); + + mConnectionGeometry = new osg::Geometry(); + + osg::ref_ptr vertices = new osg::Vec3Array(2); + osg::ref_ptr colors = new osg::Vec4Array(1); + osg::ref_ptr indices = new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, 2); + + (*vertices)[0] = start; + (*vertices)[1] = end; + (*colors)[0] = osg::Vec4f(0.4f, 1.f, 0.4f, 1.f); + indices->setElement(0, 0); + indices->setElement(1, 1); + + mConnectionGeometry->setVertexArray(vertices); + mConnectionGeometry->setColorArray(colors, osg::Array::BIND_OVERALL); + mConnectionGeometry->addPrimitiveSet(indices); + mConnectionGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + mPathgridGeode->addDrawable(mConnectionGeometry); + } + const CSMWorld::Pathgrid* Pathgrid::getPathgridSource() { int index = mPathgridCollection.searchId(mId); diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index 7cab806d3..9f6cf2686 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -69,9 +69,11 @@ namespace CSVRender void clearSelected(); void moveSelected(const osg::Vec3d& offset); - void resetMove(); - void setupConnectionIndicator(unsigned short node); + void adjustConnectionIndicator(unsigned short node); + void adjustConnectionIndicator(const osg::Vec3d& pos); + + void resetIndicators(); void applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos); void applyPosition(CSMWorld::CommandMacro& commands); @@ -109,6 +111,7 @@ namespace CSVRender osg::ref_ptr mSelectedGeode; osg::ref_ptr mPathgridGeometry; osg::ref_ptr mSelectedGeometry; + osg::ref_ptr mConnectionGeometry; osg::ref_ptr mTag; @@ -118,6 +121,8 @@ namespace CSVRender void removePathgridGeometry(); void removeSelectedGeometry(); + void createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end); + const CSMWorld::Pathgrid* getPathgridSource(); int edgeExists(const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 0dd458346..423497a24 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -156,13 +156,13 @@ namespace CSVRender void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor) { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); - - for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) + if (mDragMode == DragMode_Move) { - if (PathgridTag* tag = dynamic_cast(it->get())) + std::vector > selection = getWorldspaceWidget().getSelection(Mask_Pathgrid); + + for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { - if (mDragMode == DragMode_Move) + if (PathgridTag* tag = dynamic_cast(it->get())) { osg::Vec3d eye, center, up, offset; getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, center, up); @@ -171,10 +171,26 @@ namespace CSVRender tag->getPathgrid()->moveSelected(offset); } - else if (mDragMode == DragMode_Edge) + } + } + else if (mDragMode == DragMode_Edge) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + + Cell* cell = getWorldspaceWidget().getCell(hit.worldPos); + if (cell) + { + PathgridTag* tag = 0; + if (hit.tag && (tag = dynamic_cast(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId) + { + unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.index0)); + cell->getPathgrid()->adjustConnectionIndicator(node); + } + else { - // TODO Add indicator, need raytrace + cell->getPathgrid()->adjustConnectionIndicator(hit.worldPos); } + } } } diff --git a/components/sceneutil/pathgridutil.cpp b/components/sceneutil/pathgridutil.cpp index 5078b1edb..48e398132 100644 --- a/components/sceneutil/pathgridutil.cpp +++ b/components/sceneutil/pathgridutil.cpp @@ -15,8 +15,6 @@ namespace SceneUtil const unsigned short DiamondTotalVertexCount = DiamondVertexCount + DiamondConnectorVertexCount; - const float DiamondHalfHeight = 40.f; - const float DiamondHalfWidth = 16.f; const float DiamondWireframeScalar = 1.1f; const osg::Vec3f DiamondPoints[DiamondVertexCount] = diff --git a/components/sceneutil/pathgridutil.hpp b/components/sceneutil/pathgridutil.hpp index 9a93ddf91..de0ff35ed 100644 --- a/components/sceneutil/pathgridutil.hpp +++ b/components/sceneutil/pathgridutil.hpp @@ -11,6 +11,9 @@ namespace ESM namespace SceneUtil { + const float DiamondHalfHeight = 40.f; + const float DiamondHalfWidth = 16.f; + osg::ref_ptr createPathgridGeometry(const ESM::Pathgrid& pathgrid); osg::ref_ptr createPathgridSelectedWireframe(const ESM::Pathgrid& pathgrid, From b86250036c0417c5cf0fe9a3dae1d3c57921254b Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 19 May 2016 18:33:15 -0400 Subject: [PATCH 20/37] Add ability to add pathgrids. --- apps/opencs/model/world/commands.cpp | 31 ++++++++++++++++++++++++++++ apps/opencs/model/world/commands.hpp | 9 ++++++++ apps/opencs/view/render/pathgrid.cpp | 18 +++++++++------- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index f8c72a390..602d32f0f 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -8,9 +8,11 @@ #include #include +#include "idcollection.hpp" #include "idtable.hpp" #include "idtree.hpp" #include "nestedtablewrapper.hpp" +#include "pathgrid.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) @@ -235,6 +237,35 @@ void CSMWorld::CloneCommand::undo() mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } +CSMWorld::CreatePathgridCommand::CreatePathgridCommand(IdTable& model, const std::string& id, QUndoCommand *parent) + : CreateCommand(model, id, parent) +{ + setType(UniversalId::Type_Pathgrid); +} + +void CSMWorld::CreatePathgridCommand::redo() +{ + CreateCommand::redo(); + + Record record = static_cast& >(mModel.getRecord(mId)); + record.get().blank(); + record.get().mCell = mId; + + if (!mId.empty() && mId[0]=='#') + { + int x, y; + char ignore; + + std::istringstream stream (mId); + if (stream >> ignore >> x >> y) + { + record.get().mData.mX = x; + record.get().mData.mY = y; + } + } + + mModel.setRecord(mId, record, mType); +} CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent) : QUndoCommand (parent), mModel (model), mRow (row) diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 23ffccbd7..b54a1d5ac 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -153,6 +153,15 @@ namespace CSMWorld virtual void undo(); }; + class CreatePathgridCommand : public CreateCommand + { + public: + + CreatePathgridCommand(IdTable& model, const std::string& id, QUndoCommand *parent = 0); + + virtual void redo(); + }; + /// \brief Update cell ID according to x/y-coordinates /// /// \note The new value will be calculated in the first call to redo instead of the diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 7c6c8eb9e..368fde5ee 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -241,6 +241,10 @@ namespace CSVRender void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos) { + + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( + CSMWorld::UniversalId::Type_Pathgrids)); + const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { @@ -250,9 +254,6 @@ namespace CSVRender int posY = clampToCell(static_cast(localCoords.y())); int posZ = clampToCell(static_cast(localCoords.z())); - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( - CSMWorld::UniversalId::Type_Pathgrids)); - int recordIndex = mPathgridCollection.getIndex (mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); @@ -269,14 +270,15 @@ namespace CSVRender int row = static_cast(source->mPoints.size()); // Add node - commands.push (new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX)); - commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY)); - commands.push (new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), posZ)); + commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), posZ)); } else { - // Create pathgrid TODO + CSMWorld::CreatePathgridCommand* createCmd = new CSMWorld::CreatePathgridCommand(*model, mId); + commands.push(createCmd); } } From 799ed300eaa85a6592b6bfe17e5ccfa5e725eef3 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Sat, 21 May 2016 14:36:07 -0400 Subject: [PATCH 21/37] Use cell coordinates class, fix undo description. --- apps/opencs/model/world/commands.cpp | 15 +++++---------- apps/opencs/view/render/pathgridmode.cpp | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 602d32f0f..5f9422376 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -8,6 +8,7 @@ #include #include +#include "cellcoordinates.hpp" #include "idcollection.hpp" #include "idtable.hpp" #include "idtree.hpp" @@ -251,17 +252,11 @@ void CSMWorld::CreatePathgridCommand::redo() record.get().blank(); record.get().mCell = mId; - if (!mId.empty() && mId[0]=='#') + std::pair coords = CellCoordinates::fromId(mId); + if (coords.second) { - int x, y; - char ignore; - - std::istringstream stream (mId); - if (stream >> ignore >> x >> y) - { - record.get().mData.mX = x; - record.get().mData.mY = y; - } + record.get().mData.mX = coords.first.getX(); + record.get().mData.mY = coords.first.getY(); } mModel.setRecord(mId, record, mType); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 423497a24..a4ab39497 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -60,7 +60,7 @@ namespace CSVRender { // Add node QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); - QString description = "Connect node to selected nodes"; + QString description = "Add node"; CSMWorld::CommandMacro macro(undoStack, description); cell->getPathgrid()->applyPoint(macro, hitResult.worldPos); From 6199663bd84c50366ba046545fe4f2de0b093b0a Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Sat, 21 May 2016 18:59:43 -0400 Subject: [PATCH 22/37] Fix data corruption issues. - Point connection count not being set - Nested undo not restoring table (for pathgrid scene editing, editor nested undo is still broken) --- apps/opencs/model/world/commands.cpp | 34 +++++++ apps/opencs/model/world/commands.hpp | 27 ++++++ .../model/world/nestedcoladapterimp.cpp | 96 +++++++++++++++---- apps/opencs/view/render/pathgrid.cpp | 38 +++----- 4 files changed, 155 insertions(+), 40 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5f9422376..b48d6eba2 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -301,6 +301,40 @@ void CSMWorld::UpdateCellCommand::undo() mModel.setData (mIndex, mOld); } +CSMWorld::ModifyNestedCommand::ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, + int nestedColumn, int parentColumn, const QVariant& new_, QUndoCommand* parent) + : QUndoCommand(parent) + , NestedTableStoring(model, id, parentColumn) + , mModel(model) + , mId(id) + , mNestedRow(nestedRow) + , mNestedColumn(nestedColumn) + , mParentColumn(parentColumn) + , mNew(new_) +{ + std::string title = model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); + setText (("Modify " + title + " sub-table of " + mId).c_str()); + + QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); + mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this); +} + +void CSMWorld::ModifyNestedCommand::redo() +{ + QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); + QModelIndex nestedIndex = mModel.index(mNestedRow, mNestedColumn, parentIndex); + mModel.setData(nestedIndex, mNew); + mModifyParentCommand->redo(); +} + + +void CSMWorld::ModifyNestedCommand::undo() +{ + QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); + mModel.setNestedTable(parentIndex, getOld()); + mModifyParentCommand->undo(); +} + CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model, const std::string& id, diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index b54a1d5ac..b51720df8 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -199,6 +199,33 @@ namespace CSMWorld const NestedTableWrapperBase& getOld() const; }; + class ModifyNestedCommand : public QUndoCommand, private NestedTableStoring + { + IdTree& mModel; + + std::string mId; + + int mNestedRow; + + int mNestedColumn; + + int mParentColumn; + + QVariant mNew; + + // The command to redo/undo the Modified status of a record + ModifyCommand *mModifyParentCommand; + + public: + + ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, int nestedColumn, + int parentColumn, const QVariant& new_, QUndoCommand* parent = 0); + + virtual void redo(); + + virtual void undo(); + }; + class DeleteNestedCommand : public QUndoCommand, private NestedTableStoring { IdTree& mModel; diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 3189f76ed..814406a7c 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -33,10 +33,10 @@ namespace CSMWorld std::vector::iterator iter = pathgrid.mEdges.begin(); for (;iter != pathgrid.mEdges.end(); ++iter) { - if ((*iter).mV0 >= position) - (*iter).mV0++; - if ((*iter).mV1 >= position) - (*iter).mV1++; + if (iter->mV0 >= position) + ++iter->mV0; + if (iter->mV1 >= position) + ++iter->mV1; } points.insert(points.begin()+position, point); @@ -61,15 +61,20 @@ namespace CSMWorld std::vector::iterator iter = pathgrid.mEdges.begin(); for (; iter != pathgrid.mEdges.end();) { - if (((*iter).mV0 == rowToRemove) || ((*iter).mV1 == rowToRemove)) + if (iter->mV0 == rowToRemove || iter->mV1 == rowToRemove) + { + if (static_cast(iter->mV0) < points.size()) + --points[iter->mV0].mConnectionNum; + iter = pathgrid.mEdges.erase(iter); + } else { - if ((*iter).mV0 > rowToRemove) - (*iter).mV0--; + if (iter->mV0 > rowToRemove) + --iter->mV0; - if ((*iter).mV1 > rowToRemove) - (*iter).mV1--; + if (iter->mV1 > rowToRemove) + --iter->mV1; ++iter; } @@ -152,8 +157,13 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); + ESM::Pathgrid::PointList& points = pathgrid.mPoints; ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; + // Can only add valid point indices + if (points.empty()) + return; + // blank row ESM::Pathgrid::Edge edge; edge.mV0 = 0; @@ -164,7 +174,12 @@ namespace CSMWorld // // Currently the code assumes that the end user to know what he/she is doing. // e.g. Edges come in pairs, from points a->b and b->a - edges.insert(edges.begin()+position, edge); + + // Even edges between the same node and itself need to be counted + ++points[edge.mV0].mConnectionNum; + + // Edge needs to be ordered, since edge.mV0 is 0, add at start + edges.insert(edges.begin(), edge); record.setModified (pathgrid); } @@ -173,11 +188,16 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); + ESM::Pathgrid::PointList& points = pathgrid.mPoints; ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; if (rowToRemove < 0 || rowToRemove >= static_cast (edges.size())) throw std::runtime_error ("index out of range"); + ESM::Pathgrid::Edge& edge = edges[rowToRemove]; + if (static_cast(edge.mV0) < points.size()) + --pathgrid.mPoints[edge.mV0].mConnectionNum; + edges.erase(edges.begin()+rowToRemove); record.setModified (pathgrid); @@ -188,8 +208,13 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); + // Set points because point data (edge count) is tied to edges + pathgrid.mPoints = + static_cast(nestedTable).mRecord.mPoints; + pathgrid.mData.mS2 = + static_cast(nestedTable).mRecord.mData.mS2; pathgrid.mEdges = - static_cast &>(nestedTable).mNestedTable; + static_cast(nestedTable).mRecord.mEdges; record.setModified (pathgrid); } @@ -197,7 +222,7 @@ namespace CSMWorld NestedTableWrapperBase* PathgridEdgeListAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring - return new NestedTableWrapper(record.get().mEdges); + return new PathgridPointsWrap(record.get()); } QVariant PathgridEdgeListAdapter::getData(const Record& record, @@ -224,20 +249,57 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); - if (subRowIndex < 0 || subRowIndex >= static_cast (pathgrid.mEdges.size())) + ESM::Pathgrid::PointList& points = pathgrid.mPoints; + ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; + + if (subRowIndex < 0 || subRowIndex >= static_cast (edges.size())) throw std::runtime_error ("index out of range"); + // Point indices must exist + if (value.toInt() < 0 || value.toUInt() >= points.size()) + return; + ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; switch (subColIndex) { case 0: return; // return without saving - case 1: edge.mV0 = value.toInt(); break; - case 2: edge.mV1 = value.toInt(); break; + case 1: + { + edges.erase(edges.begin()+subRowIndex); + + if (static_cast(edge.mV0) < points.size()) + --points[edge.mV0].mConnectionNum; + + edge.mV0 = value.toInt(); + + // Place in correct order + if (static_cast(edge.mV0) < points.size()) + ++points[edge.mV0].mConnectionNum; + + ESM::Pathgrid::EdgeList::iterator it = edges.begin(); + for (; it != edges.end(); ++it) + { + if (edge.mV0 <= it->mV0) + { + edges.insert(it, edge); + break; + } + } + + if (it == edges.end()) + edges.push_back(edge); + + break; + } + case 2: + + edge.mV1 = value.toInt(); + edges[subRowIndex] = edge; + break; + default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); } - pathgrid.mEdges[subRowIndex] = edge; - record.setModified (pathgrid); } diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 368fde5ee..939558be9 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -254,7 +254,6 @@ namespace CSVRender int posY = clampToCell(static_cast(localCoords.y())); int posZ = clampToCell(static_cast(localCoords.z())); - int recordIndex = mPathgridCollection.getIndex (mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -266,14 +265,13 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); - QModelIndex parent = model->index(recordIndex, parentColumn); int row = static_cast(source->mPoints.size()); // Add node commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), posZ)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, posX)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, posY)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, posZ)); } else { @@ -293,9 +291,9 @@ namespace CSVRender int offsetY = static_cast(localCoords.y()); int offsetZ = static_cast(localCoords.z()); - QAbstractItemModel* model = mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids); + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( + CSMWorld::UniversalId::Type_Pathgrids)); - int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -307,20 +305,18 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); - QModelIndex parent = model->index(recordIndex, parentColumn); - for (size_t i = 0; i < mSelected.size(); ++i) { const CSMWorld::Pathgrid::Point& point = source->mPoints[mSelected[i]]; int row = static_cast(mSelected[i]); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, clampToCell(point.mX + offsetX))); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, clampToCell(point.mY + offsetY))); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, clampToCell(point.mZ + offsetZ))); } } @@ -564,7 +560,6 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); - int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -573,22 +568,19 @@ namespace CSVRender int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridEdge1); - QModelIndex parent = model->index(recordIndex, parentColumn); - int row = static_cast(source.mEdges.size()); - if (edgeExists(source, node1, node2) == -1) { - commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node1)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node2)); - ++row; + // Set first edge last since that reorders the edge list + commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node2)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node1)); } if (edgeExists(source, node2, node1) == -1) { - commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node2)); - commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node1)); + commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node1)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node2)); } } From 774e1497b6b058cc103af6426b883d94860e48e0 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 23 May 2016 15:51:36 -0400 Subject: [PATCH 23/37] Fix editor undo for nested data. --- apps/opencs/model/world/commands.cpp | 96 +++++++++++++++++----------- apps/opencs/model/world/commands.hpp | 15 ++--- apps/opencs/view/render/pathgrid.cpp | 28 +++++--- 3 files changed, 83 insertions(+), 56 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index b48d6eba2..308b5386e 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -17,7 +17,13 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) - : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) + : QUndoCommand (parent) + , mModel (&model) + , mIndex (index) + , mNew (new_) + , mHasRecordState(false) + , mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) + , mModifyNestedCommand(0) { if (QAbstractProxyModel *proxy = dynamic_cast (&model)) { @@ -28,40 +34,57 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI if (mIndex.parent().isValid()) { - setText ("Modify " + dynamic_cast(mModel)->nestedHeaderData ( - mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + IdTree& tree = static_cast(*mModel); + + mModifyNestedCommand = new ModifyNestedCommand(tree, mIndex, new_, this); + setText(mModifyNestedCommand->text()); } else { setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); - } - - // Remember record state before the modification - if (CSMWorld::IdTable *table = dynamic_cast(mModel)) - { - mHasRecordState = true; - int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); - int rowIndex = mIndex.row(); - if (mIndex.parent().isValid()) + // Remember record state before the modification + if (CSMWorld::IdTable *table = dynamic_cast(mModel)) { - rowIndex = mIndex.parent().row(); - } + mHasRecordState = true; + int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); + + int rowIndex = mIndex.row(); + if (mIndex.parent().isValid()) + { + rowIndex = mIndex.parent().row(); + } - mRecordStateIndex = table->index(rowIndex, stateColumnIndex); - mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); + mRecordStateIndex = table->index(rowIndex, stateColumnIndex); + mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); + } } } void CSMWorld::ModifyCommand::redo() { - mOld = mModel->data (mIndex, Qt::EditRole); - mModel->setData (mIndex, mNew); + if (mModifyNestedCommand) + { + mModifyNestedCommand->redo(); + } + else + { + mOld = mModel->data (mIndex, Qt::EditRole); + mModel->setData (mIndex, mNew); + } } void CSMWorld::ModifyCommand::undo() { - mModel->setData (mIndex, mOld); + if (mModifyNestedCommand) + { + mModifyNestedCommand->undo(); + } + else + { + mModel->setData (mIndex, mOld); + } + if (mHasRecordState) { mModel->setData(mRecordStateIndex, mOldRecordState); @@ -301,37 +324,29 @@ void CSMWorld::UpdateCellCommand::undo() mModel.setData (mIndex, mOld); } -CSMWorld::ModifyNestedCommand::ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, - int nestedColumn, int parentColumn, const QVariant& new_, QUndoCommand* parent) +CSMWorld::ModifyNestedCommand::ModifyNestedCommand (IdTree& model, const QModelIndex& index, const QVariant& new_, + QUndoCommand* parent) : QUndoCommand(parent) - , NestedTableStoring(model, id, parentColumn) + , NestedTableStoring(model, index.parent()) , mModel(model) - , mId(id) - , mNestedRow(nestedRow) - , mNestedColumn(nestedColumn) - , mParentColumn(parentColumn) + , mIndex(index) , mNew(new_) { - std::string title = model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); - setText (("Modify " + title + " sub-table of " + mId).c_str()); + setText("Modify " + model.nestedHeaderData(mIndex.parent().column(), mIndex.column(), Qt::Horizontal, + Qt::DisplayRole).toString()); - QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); - mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this); + mModifyParentCommand = new ModifyCommand(mModel, mIndex.parent(), mIndex.parent().data(Qt::EditRole), this); } void CSMWorld::ModifyNestedCommand::redo() { - QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); - QModelIndex nestedIndex = mModel.index(mNestedRow, mNestedColumn, parentIndex); - mModel.setData(nestedIndex, mNew); + mModel.setData(mIndex, mNew); mModifyParentCommand->redo(); } - void CSMWorld::ModifyNestedCommand::undo() { - QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); - mModel.setNestedTable(parentIndex, getOld()); + mModel.setNestedTable(mIndex.parent(), getOld()); mModifyParentCommand->undo(); } @@ -402,7 +417,14 @@ void CSMWorld::AddNestedCommand::undo() } CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn) - : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) {} + : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) +{ +} + +CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const QModelIndex& parentIndex) + : mOld(model.nestedTable(parentIndex)) +{ +} CSMWorld::NestedTableStoring::~NestedTableStoring() { diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index b51720df8..d0aed97c7 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -23,6 +23,7 @@ namespace CSMWorld class IdTree; struct RecordBase; struct NestedTableWrapperBase; + class ModifyNestedCommand; class ModifyCommand : public QUndoCommand { @@ -35,6 +36,8 @@ namespace CSMWorld QModelIndex mRecordStateIndex; CSMWorld::RecordBase::State mOldRecordState; + ModifyNestedCommand* mModifyNestedCommand; + public: ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, @@ -191,6 +194,7 @@ namespace CSMWorld public: NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn); + NestedTableStoring(const IdTree& model, const QModelIndex& parentIndex); ~NestedTableStoring(); @@ -203,13 +207,7 @@ namespace CSMWorld { IdTree& mModel; - std::string mId; - - int mNestedRow; - - int mNestedColumn; - - int mParentColumn; + QModelIndex mIndex; QVariant mNew; @@ -218,8 +216,7 @@ namespace CSMWorld public: - ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, int nestedColumn, - int parentColumn, const QVariant& new_, QUndoCommand* parent = 0); + ModifyNestedCommand (IdTree& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent = 0); virtual void redo(); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 939558be9..b3b582b7a 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -254,6 +254,7 @@ namespace CSVRender int posY = clampToCell(static_cast(localCoords.y())); int posZ = clampToCell(static_cast(localCoords.z())); + int recordIndex = mPathgridCollection.getIndex (mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -265,13 +266,14 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); + QModelIndex parent = model->index(recordIndex, parentColumn); int row = static_cast(source->mPoints.size()); // Add node commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, posX)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, posY)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, posZ)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posXColumn, parent), posX)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posYColumn, parent), posY)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posZColumn, parent), posZ)); } else { @@ -294,6 +296,7 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -305,18 +308,20 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); + QModelIndex parent = model->index(recordIndex, parentColumn); + for (size_t i = 0; i < mSelected.size(); ++i) { const CSMWorld::Pathgrid::Point& point = source->mPoints[mSelected[i]]; int row = static_cast(mSelected[i]); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posXColumn, parent), clampToCell(point.mX + offsetX))); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posYColumn, parent), clampToCell(point.mY + offsetY))); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posZColumn, parent), clampToCell(point.mZ + offsetZ))); } } @@ -560,6 +565,7 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -568,19 +574,21 @@ namespace CSVRender int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridEdge1); + QModelIndex parent = model->index(recordIndex, parentColumn); + if (edgeExists(source, node1, node2) == -1) { // Set first edge last since that reorders the edge list commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node2)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node1)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge1Column, parent), node2)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge0Column, parent), node1)); } if (edgeExists(source, node2, node1) == -1) { commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node1)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node2)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge1Column, parent), node1)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge0Column, parent), node2)); } } From 5eaaed05fc0ad2bb66233bfb3450efb456dbfcf1 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Tue, 24 May 2016 17:31:00 -0400 Subject: [PATCH 24/37] Improve mouse pick culling. --- apps/opencs/view/render/worldspacewidget.cpp | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 1859401dc..f8c3af2bb 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -1,7 +1,6 @@ #include "worldspacewidget.hpp" #include -#include #include #include @@ -333,9 +332,20 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo int x = localPos.x(); int y = height() - localPos.y(); + // Convert from screen space to world space + osg::Matrixd wpvMat; + wpvMat.preMult (mView->getCamera()->getViewport()->computeWindowMatrix()); + wpvMat.preMult (mView->getCamera()->getProjectionMatrix()); + wpvMat.preMult (mView->getCamera()->getViewMatrix()); + wpvMat = osg::Matrixd::inverse (wpvMat); + + osg::Vec3d start = wpvMat.preMult (osg::Vec3d(x, y, 0)); + osg::Vec3d end = wpvMat.preMult (osg::Vec3d(x, y, 1)); + osg::Vec3d direction = end - start; + // Get intersection osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( - osgUtil::Intersector::WINDOW, x, y)); + osgUtil::Intersector::MODEL, start, end)); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); @@ -351,10 +361,10 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo osgUtil::LineSegmentIntersector::Intersection intersection = *it; // reject back-facing polygons - osg::Vec3f normal = intersection.getWorldIntersectNormal(); - normal = osg::Matrix::transform3x3(normal, mView->getCamera()->getViewMatrix()); - if (normal.z() < 0) + if (direction * intersection.getWorldIntersectNormal() > 0) + { continue; + } for (std::vector::iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { @@ -384,16 +394,6 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo } // Default placement - osg::Matrixd matrix; - matrix.preMult (mView->getCamera()->getViewport()->computeWindowMatrix()); - matrix.preMult (mView->getCamera()->getProjectionMatrix()); - matrix.preMult (mView->getCamera()->getViewMatrix()); - matrix = osg::Matrixd::inverse (matrix); - - osg::Vec3d start = matrix.preMult (intersector->getStart()); - osg::Vec3d end = matrix.preMult (intersector->getEnd()); - - osg::Vec3d direction = end - start; direction.normalize(); direction *= CSMPrefs::get()["Scene Drops"]["distance"].toInt(); From cc4655e9c7f2b54edd58a179513bd7b8f9957b91 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Tue, 24 May 2016 18:19:05 -0400 Subject: [PATCH 25/37] Cleanup and slight change to pathgrid editing controls. Now that there is a drag indicator, it is easy to tell if an operation is active or not. --- apps/opencs/model/world/commands.cpp | 6 +----- apps/opencs/model/world/nestedcoladapterimp.cpp | 3 ++- apps/opencs/view/render/pagedworldspacewidget.cpp | 2 +- apps/opencs/view/render/pathgridmode.cpp | 6 ++++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 308b5386e..d53d3f17c 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -36,6 +36,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI { IdTree& tree = static_cast(*mModel); + // ModifyNestedCommand will add its own command to change the modify status if needed mModifyNestedCommand = new ModifyNestedCommand(tree, mIndex, new_, this); setText(mModifyNestedCommand->text()); } @@ -48,12 +49,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI { mHasRecordState = true; int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); - int rowIndex = mIndex.row(); - if (mIndex.parent().isValid()) - { - rowIndex = mIndex.parent().row(); - } mRecordStateIndex = table->index(rowIndex, stateColumnIndex); mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 814406a7c..1fad55329 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -265,6 +265,7 @@ namespace CSMWorld case 0: return; // return without saving case 1: { + // Remove current and add new while adjusting each points connection count edges.erase(edges.begin()+subRowIndex); if (static_cast(edge.mV0) < points.size()) @@ -272,10 +273,10 @@ namespace CSMWorld edge.mV0 = value.toInt(); - // Place in correct order if (static_cast(edge.mV0) < points.size()) ++points[edge.mV0].mConnectionNum; + // Place in correct order ESM::Pathgrid::EdgeList::iterator it = edges.begin(); for (; it != edges.end(); ++it) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index a01df4392..1dba1306e 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -332,7 +332,7 @@ void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelInd void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, int start, int end) { - const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); if (!parent.isValid()) { diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index a4ab39497..fc16d7d99 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -131,9 +131,10 @@ namespace CSVRender if (!selection.empty()) { mDragMode = DragMode_Move; + return true; } - return true; + return false; } bool PathgridMode::secondaryEditStartDrag(const QPoint& pos) @@ -148,10 +149,11 @@ namespace CSVRender mFromNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); tag->getPathgrid()->setupConnectionIndicator(mFromNode); + return true; } } - return true; + return false; } void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor) From bb81e89c08498130f5605424515d6fd99990df27 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 25 May 2016 22:48:43 -0400 Subject: [PATCH 26/37] Revert "Cleanup and slight change to pathgrid editing controls." This reverts commit cc4655e9c7f2b54edd58a179513bd7b8f9957b91. --- apps/opencs/model/world/commands.cpp | 6 +++++- apps/opencs/model/world/nestedcoladapterimp.cpp | 3 +-- apps/opencs/view/render/pagedworldspacewidget.cpp | 2 +- apps/opencs/view/render/pathgridmode.cpp | 6 ++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index d53d3f17c..308b5386e 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -36,7 +36,6 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI { IdTree& tree = static_cast(*mModel); - // ModifyNestedCommand will add its own command to change the modify status if needed mModifyNestedCommand = new ModifyNestedCommand(tree, mIndex, new_, this); setText(mModifyNestedCommand->text()); } @@ -49,7 +48,12 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI { mHasRecordState = true; int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); + int rowIndex = mIndex.row(); + if (mIndex.parent().isValid()) + { + rowIndex = mIndex.parent().row(); + } mRecordStateIndex = table->index(rowIndex, stateColumnIndex); mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 1fad55329..814406a7c 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -265,7 +265,6 @@ namespace CSMWorld case 0: return; // return without saving case 1: { - // Remove current and add new while adjusting each points connection count edges.erase(edges.begin()+subRowIndex); if (static_cast(edge.mV0) < points.size()) @@ -273,10 +272,10 @@ namespace CSMWorld edge.mV0 = value.toInt(); + // Place in correct order if (static_cast(edge.mV0) < points.size()) ++points[edge.mV0].mConnectionNum; - // Place in correct order ESM::Pathgrid::EdgeList::iterator it = edges.begin(); for (; it != edges.end(); ++it) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 1dba1306e..a01df4392 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -332,7 +332,7 @@ void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelInd void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, int start, int end) { - const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); if (!parent.isValid()) { diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index fc16d7d99..a4ab39497 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -131,10 +131,9 @@ namespace CSVRender if (!selection.empty()) { mDragMode = DragMode_Move; - return true; } - return false; + return true; } bool PathgridMode::secondaryEditStartDrag(const QPoint& pos) @@ -149,11 +148,10 @@ namespace CSVRender mFromNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); tag->getPathgrid()->setupConnectionIndicator(mFromNode); - return true; } } - return false; + return true; } void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor) From ec90da731c34f697e8c8f1e77522333556a1fd56 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 25 May 2016 22:52:02 -0400 Subject: [PATCH 27/37] Redo slight change to pathgrid editing controls, lost in revert --- apps/opencs/view/render/pathgridmode.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index a4ab39497..fc16d7d99 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -131,9 +131,10 @@ namespace CSVRender if (!selection.empty()) { mDragMode = DragMode_Move; + return true; } - return true; + return false; } bool PathgridMode::secondaryEditStartDrag(const QPoint& pos) @@ -148,10 +149,11 @@ namespace CSVRender mFromNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); tag->getPathgrid()->setupConnectionIndicator(mFromNode); + return true; } } - return true; + return false; } void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor) From 9a567b3712c9a9575f03266884cbeece5c2764b5 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 25 May 2016 22:52:41 -0400 Subject: [PATCH 28/37] Revert "Fix editor undo for nested data." This reverts commit 774e1497b6b058cc103af6426b883d94860e48e0. --- apps/opencs/model/world/commands.cpp | 96 +++++++++++----------------- apps/opencs/model/world/commands.hpp | 15 +++-- apps/opencs/view/render/pathgrid.cpp | 28 +++----- 3 files changed, 56 insertions(+), 83 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 308b5386e..b48d6eba2 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -17,13 +17,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) - : QUndoCommand (parent) - , mModel (&model) - , mIndex (index) - , mNew (new_) - , mHasRecordState(false) - , mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) - , mModifyNestedCommand(0) + : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) { if (QAbstractProxyModel *proxy = dynamic_cast (&model)) { @@ -34,57 +28,40 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI if (mIndex.parent().isValid()) { - IdTree& tree = static_cast(*mModel); - - mModifyNestedCommand = new ModifyNestedCommand(tree, mIndex, new_, this); - setText(mModifyNestedCommand->text()); + setText ("Modify " + dynamic_cast(mModel)->nestedHeaderData ( + mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } else { setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + } - // Remember record state before the modification - if (CSMWorld::IdTable *table = dynamic_cast(mModel)) - { - mHasRecordState = true; - int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); - - int rowIndex = mIndex.row(); - if (mIndex.parent().isValid()) - { - rowIndex = mIndex.parent().row(); - } + // Remember record state before the modification + if (CSMWorld::IdTable *table = dynamic_cast(mModel)) + { + mHasRecordState = true; + int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); - mRecordStateIndex = table->index(rowIndex, stateColumnIndex); - mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); + int rowIndex = mIndex.row(); + if (mIndex.parent().isValid()) + { + rowIndex = mIndex.parent().row(); } + + mRecordStateIndex = table->index(rowIndex, stateColumnIndex); + mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); } } void CSMWorld::ModifyCommand::redo() { - if (mModifyNestedCommand) - { - mModifyNestedCommand->redo(); - } - else - { - mOld = mModel->data (mIndex, Qt::EditRole); - mModel->setData (mIndex, mNew); - } + mOld = mModel->data (mIndex, Qt::EditRole); + mModel->setData (mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { - if (mModifyNestedCommand) - { - mModifyNestedCommand->undo(); - } - else - { - mModel->setData (mIndex, mOld); - } - + mModel->setData (mIndex, mOld); if (mHasRecordState) { mModel->setData(mRecordStateIndex, mOldRecordState); @@ -324,29 +301,37 @@ void CSMWorld::UpdateCellCommand::undo() mModel.setData (mIndex, mOld); } -CSMWorld::ModifyNestedCommand::ModifyNestedCommand (IdTree& model, const QModelIndex& index, const QVariant& new_, - QUndoCommand* parent) +CSMWorld::ModifyNestedCommand::ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, + int nestedColumn, int parentColumn, const QVariant& new_, QUndoCommand* parent) : QUndoCommand(parent) - , NestedTableStoring(model, index.parent()) + , NestedTableStoring(model, id, parentColumn) , mModel(model) - , mIndex(index) + , mId(id) + , mNestedRow(nestedRow) + , mNestedColumn(nestedColumn) + , mParentColumn(parentColumn) , mNew(new_) { - setText("Modify " + model.nestedHeaderData(mIndex.parent().column(), mIndex.column(), Qt::Horizontal, - Qt::DisplayRole).toString()); + std::string title = model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); + setText (("Modify " + title + " sub-table of " + mId).c_str()); - mModifyParentCommand = new ModifyCommand(mModel, mIndex.parent(), mIndex.parent().data(Qt::EditRole), this); + QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); + mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this); } void CSMWorld::ModifyNestedCommand::redo() { - mModel.setData(mIndex, mNew); + QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); + QModelIndex nestedIndex = mModel.index(mNestedRow, mNestedColumn, parentIndex); + mModel.setData(nestedIndex, mNew); mModifyParentCommand->redo(); } + void CSMWorld::ModifyNestedCommand::undo() { - mModel.setNestedTable(mIndex.parent(), getOld()); + QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); + mModel.setNestedTable(parentIndex, getOld()); mModifyParentCommand->undo(); } @@ -417,14 +402,7 @@ void CSMWorld::AddNestedCommand::undo() } CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn) - : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) -{ -} - -CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const QModelIndex& parentIndex) - : mOld(model.nestedTable(parentIndex)) -{ -} + : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) {} CSMWorld::NestedTableStoring::~NestedTableStoring() { diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index d0aed97c7..b51720df8 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -23,7 +23,6 @@ namespace CSMWorld class IdTree; struct RecordBase; struct NestedTableWrapperBase; - class ModifyNestedCommand; class ModifyCommand : public QUndoCommand { @@ -36,8 +35,6 @@ namespace CSMWorld QModelIndex mRecordStateIndex; CSMWorld::RecordBase::State mOldRecordState; - ModifyNestedCommand* mModifyNestedCommand; - public: ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, @@ -194,7 +191,6 @@ namespace CSMWorld public: NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn); - NestedTableStoring(const IdTree& model, const QModelIndex& parentIndex); ~NestedTableStoring(); @@ -207,7 +203,13 @@ namespace CSMWorld { IdTree& mModel; - QModelIndex mIndex; + std::string mId; + + int mNestedRow; + + int mNestedColumn; + + int mParentColumn; QVariant mNew; @@ -216,7 +218,8 @@ namespace CSMWorld public: - ModifyNestedCommand (IdTree& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent = 0); + ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, int nestedColumn, + int parentColumn, const QVariant& new_, QUndoCommand* parent = 0); virtual void redo(); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index b3b582b7a..939558be9 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -254,7 +254,6 @@ namespace CSVRender int posY = clampToCell(static_cast(localCoords.y())); int posZ = clampToCell(static_cast(localCoords.z())); - int recordIndex = mPathgridCollection.getIndex (mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -266,14 +265,13 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); - QModelIndex parent = model->index(recordIndex, parentColumn); int row = static_cast(source->mPoints.size()); // Add node commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posXColumn, parent), posX)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posYColumn, parent), posY)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posZColumn, parent), posZ)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, posX)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, posY)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, posZ)); } else { @@ -296,7 +294,6 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); - int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -308,20 +305,18 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); - QModelIndex parent = model->index(recordIndex, parentColumn); - for (size_t i = 0; i < mSelected.size(); ++i) { const CSMWorld::Pathgrid::Point& point = source->mPoints[mSelected[i]]; int row = static_cast(mSelected[i]); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posXColumn, parent), + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, clampToCell(point.mX + offsetX))); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posYColumn, parent), + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, clampToCell(point.mY + offsetY))); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(row, posZColumn, parent), + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, clampToCell(point.mZ + offsetZ))); } } @@ -565,7 +560,6 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); - int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -574,21 +568,19 @@ namespace CSVRender int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridEdge1); - QModelIndex parent = model->index(recordIndex, parentColumn); - if (edgeExists(source, node1, node2) == -1) { // Set first edge last since that reorders the edge list commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge1Column, parent), node2)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge0Column, parent), node1)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node2)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node1)); } if (edgeExists(source, node2, node1) == -1) { commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge1Column, parent), node1)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, model->index(0, edge0Column, parent), node2)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node1)); + commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node2)); } } From 239727531fa228ab95e6b84b3ff373cb0a167d9d Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 25 May 2016 22:53:17 -0400 Subject: [PATCH 29/37] Revert "Fix data corruption issues." This reverts commit 6199663bd84c50366ba046545fe4f2de0b093b0a. --- apps/opencs/model/world/commands.cpp | 34 ------- apps/opencs/model/world/commands.hpp | 27 ------ .../model/world/nestedcoladapterimp.cpp | 96 ++++--------------- apps/opencs/view/render/pathgrid.cpp | 38 +++++--- 4 files changed, 40 insertions(+), 155 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index b48d6eba2..5f9422376 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -301,40 +301,6 @@ void CSMWorld::UpdateCellCommand::undo() mModel.setData (mIndex, mOld); } -CSMWorld::ModifyNestedCommand::ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, - int nestedColumn, int parentColumn, const QVariant& new_, QUndoCommand* parent) - : QUndoCommand(parent) - , NestedTableStoring(model, id, parentColumn) - , mModel(model) - , mId(id) - , mNestedRow(nestedRow) - , mNestedColumn(nestedColumn) - , mParentColumn(parentColumn) - , mNew(new_) -{ - std::string title = model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); - setText (("Modify " + title + " sub-table of " + mId).c_str()); - - QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); - mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this); -} - -void CSMWorld::ModifyNestedCommand::redo() -{ - QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); - QModelIndex nestedIndex = mModel.index(mNestedRow, mNestedColumn, parentIndex); - mModel.setData(nestedIndex, mNew); - mModifyParentCommand->redo(); -} - - -void CSMWorld::ModifyNestedCommand::undo() -{ - QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); - mModel.setNestedTable(parentIndex, getOld()); - mModifyParentCommand->undo(); -} - CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model, const std::string& id, diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index b51720df8..b54a1d5ac 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -199,33 +199,6 @@ namespace CSMWorld const NestedTableWrapperBase& getOld() const; }; - class ModifyNestedCommand : public QUndoCommand, private NestedTableStoring - { - IdTree& mModel; - - std::string mId; - - int mNestedRow; - - int mNestedColumn; - - int mParentColumn; - - QVariant mNew; - - // The command to redo/undo the Modified status of a record - ModifyCommand *mModifyParentCommand; - - public: - - ModifyNestedCommand (IdTree& model, const std::string& id, int nestedRow, int nestedColumn, - int parentColumn, const QVariant& new_, QUndoCommand* parent = 0); - - virtual void redo(); - - virtual void undo(); - }; - class DeleteNestedCommand : public QUndoCommand, private NestedTableStoring { IdTree& mModel; diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 814406a7c..3189f76ed 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -33,10 +33,10 @@ namespace CSMWorld std::vector::iterator iter = pathgrid.mEdges.begin(); for (;iter != pathgrid.mEdges.end(); ++iter) { - if (iter->mV0 >= position) - ++iter->mV0; - if (iter->mV1 >= position) - ++iter->mV1; + if ((*iter).mV0 >= position) + (*iter).mV0++; + if ((*iter).mV1 >= position) + (*iter).mV1++; } points.insert(points.begin()+position, point); @@ -61,20 +61,15 @@ namespace CSMWorld std::vector::iterator iter = pathgrid.mEdges.begin(); for (; iter != pathgrid.mEdges.end();) { - if (iter->mV0 == rowToRemove || iter->mV1 == rowToRemove) - { - if (static_cast(iter->mV0) < points.size()) - --points[iter->mV0].mConnectionNum; - + if (((*iter).mV0 == rowToRemove) || ((*iter).mV1 == rowToRemove)) iter = pathgrid.mEdges.erase(iter); - } else { - if (iter->mV0 > rowToRemove) - --iter->mV0; + if ((*iter).mV0 > rowToRemove) + (*iter).mV0--; - if (iter->mV1 > rowToRemove) - --iter->mV1; + if ((*iter).mV1 > rowToRemove) + (*iter).mV1--; ++iter; } @@ -157,13 +152,8 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); - ESM::Pathgrid::PointList& points = pathgrid.mPoints; ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; - // Can only add valid point indices - if (points.empty()) - return; - // blank row ESM::Pathgrid::Edge edge; edge.mV0 = 0; @@ -174,12 +164,7 @@ namespace CSMWorld // // Currently the code assumes that the end user to know what he/she is doing. // e.g. Edges come in pairs, from points a->b and b->a - - // Even edges between the same node and itself need to be counted - ++points[edge.mV0].mConnectionNum; - - // Edge needs to be ordered, since edge.mV0 is 0, add at start - edges.insert(edges.begin(), edge); + edges.insert(edges.begin()+position, edge); record.setModified (pathgrid); } @@ -188,16 +173,11 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); - ESM::Pathgrid::PointList& points = pathgrid.mPoints; ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; if (rowToRemove < 0 || rowToRemove >= static_cast (edges.size())) throw std::runtime_error ("index out of range"); - ESM::Pathgrid::Edge& edge = edges[rowToRemove]; - if (static_cast(edge.mV0) < points.size()) - --pathgrid.mPoints[edge.mV0].mConnectionNum; - edges.erase(edges.begin()+rowToRemove); record.setModified (pathgrid); @@ -208,13 +188,8 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); - // Set points because point data (edge count) is tied to edges - pathgrid.mPoints = - static_cast(nestedTable).mRecord.mPoints; - pathgrid.mData.mS2 = - static_cast(nestedTable).mRecord.mData.mS2; pathgrid.mEdges = - static_cast(nestedTable).mRecord.mEdges; + static_cast &>(nestedTable).mNestedTable; record.setModified (pathgrid); } @@ -222,7 +197,7 @@ namespace CSMWorld NestedTableWrapperBase* PathgridEdgeListAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring - return new PathgridPointsWrap(record.get()); + return new NestedTableWrapper(record.get().mEdges); } QVariant PathgridEdgeListAdapter::getData(const Record& record, @@ -249,57 +224,20 @@ namespace CSMWorld { Pathgrid pathgrid = record.get(); - ESM::Pathgrid::PointList& points = pathgrid.mPoints; - ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; - - if (subRowIndex < 0 || subRowIndex >= static_cast (edges.size())) + if (subRowIndex < 0 || subRowIndex >= static_cast (pathgrid.mEdges.size())) throw std::runtime_error ("index out of range"); - // Point indices must exist - if (value.toInt() < 0 || value.toUInt() >= points.size()) - return; - ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; switch (subColIndex) { case 0: return; // return without saving - case 1: - { - edges.erase(edges.begin()+subRowIndex); - - if (static_cast(edge.mV0) < points.size()) - --points[edge.mV0].mConnectionNum; - - edge.mV0 = value.toInt(); - - // Place in correct order - if (static_cast(edge.mV0) < points.size()) - ++points[edge.mV0].mConnectionNum; - - ESM::Pathgrid::EdgeList::iterator it = edges.begin(); - for (; it != edges.end(); ++it) - { - if (edge.mV0 <= it->mV0) - { - edges.insert(it, edge); - break; - } - } - - if (it == edges.end()) - edges.push_back(edge); - - break; - } - case 2: - - edge.mV1 = value.toInt(); - edges[subRowIndex] = edge; - break; - + case 1: edge.mV0 = value.toInt(); break; + case 2: edge.mV1 = value.toInt(); break; default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); } + pathgrid.mEdges[subRowIndex] = edge; + record.setModified (pathgrid); } diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 939558be9..368fde5ee 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -254,6 +254,7 @@ namespace CSVRender int posY = clampToCell(static_cast(localCoords.y())); int posZ = clampToCell(static_cast(localCoords.z())); + int recordIndex = mPathgridCollection.getIndex (mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -265,13 +266,14 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); + QModelIndex parent = model->index(recordIndex, parentColumn); int row = static_cast(source->mPoints.size()); // Add node commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, posX)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, posY)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, posZ)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), posZ)); } else { @@ -291,9 +293,9 @@ namespace CSVRender int offsetY = static_cast(localCoords.y()); int offsetZ = static_cast(localCoords.z()); - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( - CSMWorld::UniversalId::Type_Pathgrids)); + QAbstractItemModel* model = mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids); + int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); int posXColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -305,18 +307,20 @@ namespace CSVRender int posZColumn = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridPosZ); + QModelIndex parent = model->index(recordIndex, parentColumn); + for (size_t i = 0; i < mSelected.size(); ++i) { const CSMWorld::Pathgrid::Point& point = source->mPoints[mSelected[i]]; int row = static_cast(mSelected[i]); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posXColumn, parentColumn, + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), clampToCell(point.mX + offsetX))); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posYColumn, parentColumn, + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), clampToCell(point.mY + offsetY))); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, row, posZColumn, parentColumn, + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posZColumn, parent), clampToCell(point.mZ + offsetZ))); } } @@ -560,6 +564,7 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, @@ -568,19 +573,22 @@ namespace CSVRender int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, CSMWorld::Columns::ColumnId_PathgridEdge1); + QModelIndex parent = model->index(recordIndex, parentColumn); + int row = static_cast(source.mEdges.size()); + if (edgeExists(source, node1, node2) == -1) { - // Set first edge last since that reorders the edge list - commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node2)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node1)); + commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node1)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node2)); + ++row; } if (edgeExists(source, node2, node1) == -1) { - commands.push(new CSMWorld::AddNestedCommand(*model, mId, 0, parentColumn)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge1Column, parentColumn, node1)); - commands.push(new CSMWorld::ModifyNestedCommand(*model, mId, 0, edge0Column, parentColumn, node2)); + commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge0Column, parent), node2)); + commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, edge1Column, parent), node1)); } } From 564d0db68cda16af7c1c8bdd2779f8046aa339bd Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 26 May 2016 22:11:27 -0400 Subject: [PATCH 30/37] Move pathgrid abstraction handling to save code. --- .../model/world/nestedcoladapterimp.cpp | 52 +++---------------- .../model/world/nestedcoladapterimp.hpp | 15 ------ components/esm/loadpgrd.cpp | 32 ++++++++++-- 3 files changed, 34 insertions(+), 65 deletions(-) diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 3189f76ed..464757cd4 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -27,20 +27,8 @@ namespace CSMWorld point.mConnectionNum = 0; point.mUnknown = 0; - // inserting a point should trigger re-indexing of the edges - // - // FIXME: does not auto refresh edges table view - std::vector::iterator iter = pathgrid.mEdges.begin(); - for (;iter != pathgrid.mEdges.end(); ++iter) - { - if ((*iter).mV0 >= position) - (*iter).mV0++; - if ((*iter).mV1 >= position) - (*iter).mV1++; - } - points.insert(points.begin()+position, point); - pathgrid.mData.mS2 += 1; // increment the number of points + pathgrid.mData.mS2 = pathgrid.mPoints.size(); record.setModified (pathgrid); } @@ -54,28 +42,10 @@ namespace CSMWorld if (rowToRemove < 0 || rowToRemove >= static_cast (points.size())) throw std::runtime_error ("index out of range"); - // deleting a point should trigger re-indexing of the edges - // dangling edges are not allowed and hence removed - // - // FIXME: does not auto refresh edges table view - std::vector::iterator iter = pathgrid.mEdges.begin(); - for (; iter != pathgrid.mEdges.end();) - { - if (((*iter).mV0 == rowToRemove) || ((*iter).mV1 == rowToRemove)) - iter = pathgrid.mEdges.erase(iter); - else - { - if ((*iter).mV0 > rowToRemove) - (*iter).mV0--; - - if ((*iter).mV1 > rowToRemove) - (*iter).mV1--; - - ++iter; - } - } + // Do not remove dangling edges, does not work with current undo mechanism + // Do not automatically adjust indices, what would be done with dangling edges? points.erase(points.begin()+rowToRemove); - pathgrid.mData.mS2 -= 1; // decrement the number of points + pathgrid.mData.mS2 = pathgrid.mPoints.size(); record.setModified (pathgrid); } @@ -84,14 +54,8 @@ namespace CSMWorld const NestedTableWrapperBase& nestedTable) const { Pathgrid pathgrid = record.get(); - - pathgrid.mPoints = - static_cast(nestedTable).mRecord.mPoints; - pathgrid.mData.mS2 = - static_cast(nestedTable).mRecord.mData.mS2; - // also update edges in case points were added/removed - pathgrid.mEdges = - static_cast(nestedTable).mRecord.mEdges; + pathgrid.mPoints = static_cast &>(nestedTable).mNestedTable; + pathgrid.mData.mS2 = pathgrid.mPoints.size(); record.setModified (pathgrid); } @@ -99,7 +63,7 @@ namespace CSMWorld NestedTableWrapperBase* PathgridPointListAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring - return new PathgridPointsWrap(record.get()); + return new NestedTableWrapper(record.get().mPoints); } QVariant PathgridPointListAdapter::getData(const Record& record, @@ -147,7 +111,6 @@ namespace CSMWorld PathgridEdgeListAdapter::PathgridEdgeListAdapter () {} - // ToDo: seems to be auto-sorted in the dialog table display after insertion void PathgridEdgeListAdapter::addRow(Record& record, int position) const { Pathgrid pathgrid = record.get(); @@ -218,7 +181,6 @@ namespace CSMWorld } } - // ToDo: detect duplicates in mEdges void PathgridEdgeListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index e24bcb134..131f547a5 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -25,21 +25,6 @@ namespace CSMWorld struct Pathgrid; struct Info; - struct PathgridPointsWrap : public NestedTableWrapperBase - { - ESM::Pathgrid mRecord; - - PathgridPointsWrap(ESM::Pathgrid pathgrid) - : mRecord(pathgrid) {} - - virtual ~PathgridPointsWrap() {} - - virtual int size() const - { - return mRecord.mPoints.size(); // used in IdTree::setNestedTable() - } - }; - class PathgridPointListAdapter : public NestedColumnAdapter { public: diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 15accd95b..708685e72 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -127,6 +127,28 @@ namespace ESM void Pathgrid::save(ESMWriter &esm, bool isDeleted) const { + // Correct connection count and sort edges by point + // Can probably be optimized + PointList correctedPoints = mPoints; + std::vector sortedEdges; + + sortedEdges.reserve(mEdges.size()); + + for (size_t point = 0; point < correctedPoints.size(); ++point) + { + correctedPoints[point].mConnectionNum = 0; + + for (EdgeList::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) + { + if (static_cast(it->mV0) == point) + { + sortedEdges.push_back(it->mV1); + ++correctedPoints[point].mConnectionNum; + } + } + } + + // Save esm.writeHNCString("NAME", mCell); esm.writeHNT("DATA", mData, 12); @@ -136,22 +158,22 @@ namespace ESM return; } - if (!mPoints.empty()) + if (!correctedPoints.empty()) { esm.startSubRecord("PGRP"); - for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) + for (PointList::const_iterator it = correctedPoints.begin(); it != correctedPoints.end(); ++it) { esm.writeT(*it); } esm.endRecord("PGRP"); } - if (!mEdges.empty()) + if (!sortedEdges.empty()) { esm.startSubRecord("PGRC"); - for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) + for (std::vector::const_iterator it = sortedEdges.begin(); it != sortedEdges.end(); ++it) { - esm.writeT(it->mV1); + esm.writeT(*it); } esm.endRecord("PGRC"); } From d0ef95a1e172ea591d8d4e99c6a83b47d70fde45 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 26 May 2016 22:13:47 -0400 Subject: [PATCH 31/37] Make adjustments to pathgrid modification code. --- apps/opencs/view/render/pathgrid.cpp | 68 ++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 368fde5ee..b1c3be854 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -269,7 +269,7 @@ namespace CSVRender QModelIndex parent = model->index(recordIndex, parentColumn); int row = static_cast(source->mPoints.size()); - // Add node + // Add node to end of list commands.push(new CSMWorld::AddNestedCommand(*model, mId, row, parentColumn)); commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posXColumn, parent), posX)); commands.push(new CSMWorld::ModifyCommand(*model, model->index(row, posYColumn, parent), posY)); @@ -349,22 +349,80 @@ namespace CSVRender void Pathgrid::applyRemoveNodes(CSMWorld::CommandMacro& commands) { - // Source is aquired here to ensure a pathgrid exists const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { - // Want to remove from end of row first - std::sort(mSelected.begin(), mSelected.end(), std::greater()); - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + // Want to remove nodes from end of list first + std::sort(mSelected.begin(), mSelected.end(), std::greater()); + + int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); for (std::vector::iterator row = mSelected.begin(); row != mSelected.end(); ++row) { commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, static_cast(*row), parentColumn)); } + + // Fix/remove edges + std::set > edgeRowsToRemove; + + parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); + + int edge0Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridEdge0); + + int edge1Column = mPathgridCollection.searchNestedColumnIndex(parentColumn, + CSMWorld::Columns::ColumnId_PathgridEdge1); + + QModelIndex parent = model->index(recordIndex, parentColumn); + + for (size_t edge = 0; edge < source->mEdges.size(); ++edge) + { + int adjustment0 = 0; + int adjustment1 = 0; + + // Determine necessary adjustment + for (std::vector::iterator point = mSelected.begin(); point != mSelected.end(); ++point) + { + if (source->mEdges[edge].mV0 == *point || source->mEdges[edge].mV1 == *point) + { + edgeRowsToRemove.insert(static_cast(edge)); + + adjustment0 = 0; // No need to adjust, its getting removed + adjustment1 = 0; + break; + } + + if (source->mEdges[edge].mV0 > *point) + --adjustment0; + + if (source->mEdges[edge].mV1 > *point) + --adjustment1; + } + + if (adjustment0 != 0) + { + int adjustedEdge = source->mEdges[edge].mV0 + adjustment0; + commands.push(new CSMWorld::ModifyCommand(*model, model->index(edge, edge0Column, parent), + adjustedEdge)); + } + + if (adjustment1 != 0) + { + int adjustedEdge = source->mEdges[edge].mV1 + adjustment1; + commands.push(new CSMWorld::ModifyCommand(*model, model->index(edge, edge1Column, parent), + adjustedEdge)); + } + } + + std::set >::iterator row; + for (row = edgeRowsToRemove.begin(); row != edgeRowsToRemove.end(); ++row) + { + commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, *row, parentColumn)); + } } clearSelected(); From ded8862e9dc3c45f2d89e9c4cb8fbff63f2e8c7a Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 26 May 2016 22:44:02 -0400 Subject: [PATCH 32/37] Insert nested rows after selected row, or at start if none selected. --- apps/opencs/view/world/nestedtable.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 23d566439..0ccb88ce5 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -119,9 +119,14 @@ void CSVWorld::NestedTable::removeRowActionTriggered() void CSVWorld::NestedTable::addNewRowActionTriggered() { + int row = 0; + + if (!selectionModel()->selectedRows().empty()) + row = selectionModel()->selectedRows().back().row() + 1; + mDocument.getUndoStack().push(new CSMWorld::AddNestedCommand(*(mModel->model()), mModel->getParentId(), - selectionModel()->selectedRows().size(), + row, mModel->getParentColumn())); } From bded4f86f05898fcd91f4f98d01ef2c9df19df42 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Fri, 27 May 2016 13:13:49 -0400 Subject: [PATCH 33/37] Remove connection number check --- apps/opencs/model/tools/pathgridcheck.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 69ee5a809..3cd4a1b09 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -70,20 +70,6 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) { - // check the connection number for each point matches the edge connections - if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum) - { - std::ostringstream ss; - ss << " has has less edges than expected for point " << i; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); - } - else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum) - { - std::ostringstream ss; - ss << " has has more edges than expected for point " << i; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); - } - // check that edges are bidirectional bool foundReverse = false; for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j) From 90620081c77758df86baf70c566984a39e575624 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Fri, 27 May 2016 13:15:35 -0400 Subject: [PATCH 34/37] Fix handling of deleted pathgrids. --- apps/opencs/view/render/pathgrid.cpp | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index b1c3be854..f2920d7fb 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -277,8 +277,31 @@ namespace CSVRender } else { - CSMWorld::CreatePathgridCommand* createCmd = new CSMWorld::CreatePathgridCommand(*model, mId); - commands.push(createCmd); + int index = mPathgridCollection.searchId(mId); + if (index == -1) + { + // Does not exist + commands.push(new CSMWorld::CreatePathgridCommand(*model, mId)); + } + else + { + source = &mPathgridCollection.getRecord(index).get(); + + // Deleted, so revert and remove all data + commands.push(new CSMWorld::RevertCommand(*model, mId)); + + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + for (int row = source->mPoints.size() - 1; row >= 0; --row) + { + commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, row, parentColumn)); + } + + parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); + for (int row = source->mEdges.size() - 1; row >= 0; --row) + { + commands.push(new CSMWorld::DeleteNestedCommand(*model, mId, row, parentColumn)); + } + } } } @@ -510,7 +533,8 @@ namespace CSVRender } else { - removeGeometry(); + removePathgridGeometry(); + removeSelectedGeometry(); } } From b83f522ecdca5d388843a2a23e5e4f8c62b9454c Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Sun, 29 May 2016 13:26:29 -0400 Subject: [PATCH 35/37] Changes to color scheme, slight change to drag indicator, change tooltip description. --- apps/opencs/view/render/pathgrid.cpp | 17 +++++++++++++---- apps/opencs/view/render/pathgrid.hpp | 2 +- apps/opencs/view/render/pathgridmode.cpp | 1 - components/sceneutil/pathgridutil.cpp | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index f2920d7fb..a28471782 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -204,7 +204,7 @@ namespace CSVRender osg::Vec3f start = osg::Vec3f(pointA.mX, pointA.mY, pointA.mZ + SceneUtil::DiamondHalfHeight); osg::Vec3f end = osg::Vec3f(pointB.mX, pointB.mY, pointB.mZ + SceneUtil::DiamondHalfHeight); - createConnectionGeometry(start, end); + createConnectionGeometry(start, end, true); } } } @@ -220,7 +220,7 @@ namespace CSVRender osg::Vec3f start = osg::Vec3f(point.mX, point.mY, point.mZ + SceneUtil::DiamondHalfHeight); osg::Vec3f end = pos - mBaseNode->getPosition(); - createConnectionGeometry(start, end); + createConnectionGeometry(start, end, false); } } } @@ -593,7 +593,7 @@ namespace CSVRender } } - void Pathgrid::createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end) + void Pathgrid::createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid) { if (mConnectionGeometry) mPathgridGeode->removeDrawable(mConnectionGeometry); @@ -606,7 +606,16 @@ namespace CSVRender (*vertices)[0] = start; (*vertices)[1] = end; - (*colors)[0] = osg::Vec4f(0.4f, 1.f, 0.4f, 1.f); + + if (valid) + { + (*colors)[0] = osg::Vec4f(0.91f, 0.66f, 1.f, 1.f); + } + else + { + (*colors)[0] = osg::Vec4f(1.f, 0.f, 0.f, 1.f); + } + indices->setElement(0, 0); indices->setElement(1, 1); diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index 9f6cf2686..415b252e0 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -121,7 +121,7 @@ namespace CSVRender void removePathgridGeometry(); void removeSelectedGeometry(); - void createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end); + void createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid); const CSMWorld::Pathgrid* getPathgridSource(); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index fc16d7d99..e9d608099 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -37,7 +37,6 @@ namespace CSVRender "
  • Secondary edit: Connect selected nodes to node
  • " "
  • Primary drag: Move selected nodes
  • " "
  • Secondary drag: Connect one node to another
  • " - "
  • Other operations may be done with the context menu
  • " "

    Note: Only a single cell's pathgrid may be edited at a time"); } diff --git a/components/sceneutil/pathgridutil.cpp b/components/sceneutil/pathgridutil.cpp index 48e398132..1497beb2b 100644 --- a/components/sceneutil/pathgridutil.cpp +++ b/components/sceneutil/pathgridutil.cpp @@ -71,8 +71,8 @@ namespace SceneUtil }; const osg::Vec4f DiamondEdgeColor = osg::Vec4f(0.5f, 1.f, 1.f, 1.f); - const osg::Vec4f DiamondWireColor = osg::Vec4f(0.8f, 1.f, 0.9f, 1.f); - const osg::Vec4f DiamondFocusWireColor = osg::Vec4f(0.4f, 1.f, 0.4f, 1.f); + const osg::Vec4f DiamondWireColor = osg::Vec4f(0.72f, 0.f, 0.96f, 1.f); + const osg::Vec4f DiamondFocusWireColor = osg::Vec4f(0.91f, 0.66f, 1.f, 1.f); osg::ref_ptr createPathgridGeometry(const ESM::Pathgrid& pathgrid) { From 83c86cd1bee3fafa48ae2336591678b13e0eb044 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Sun, 29 May 2016 13:26:48 -0400 Subject: [PATCH 36/37] Implement context-select functionality. --- apps/opencs/view/render/pathgridmode.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index e9d608099..714c548a7 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -5,6 +5,8 @@ #include +#include "../../model/prefs/state.hpp" + #include "../../model/world/commands.hpp" #include "../../model/world/commandmacro.hpp" #include "../../model/world/idtable.hpp" @@ -53,9 +55,12 @@ namespace CSVRender void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult) { - // Get pathgrid cell - Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos); - if (cell) + if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue() && + dynamic_cast(hitResult.tag.get())) + { + primarySelectPressed(hitResult); + } + else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos)) { // Add node QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); @@ -127,6 +132,17 @@ namespace CSVRender { std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) + { + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + + if (dynamic_cast(hit.tag.get())) + { + primarySelectPressed(hit); + selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + } + } + if (!selection.empty()) { mDragMode = DragMode_Move; From 3409d8a9227564b9498e6331789650be9355e214 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Sat, 4 Jun 2016 12:38:17 -0400 Subject: [PATCH 37/37] Change pathgrid node movement to be visually consistent, some renaming --- apps/opencs/view/render/pathgrid.cpp | 131 ++++++++++------------- apps/opencs/view/render/pathgrid.hpp | 19 ++-- apps/opencs/view/render/pathgridmode.cpp | 6 +- 3 files changed, 68 insertions(+), 88 deletions(-) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index a28471782..9eb2765d3 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -55,14 +55,13 @@ namespace CSVRender , mId(pathgridId) , mCoords(coordinates) , mInterior(false) - , mConnectionIndicator(false) - , mConnectionNode(0) + , mDragOrigin(0) , mChangeGeometry(true) , mRemoveGeometry(false) + , mUseOffset(true) , mParent(parent) , mPathgridGeometry(0) - , mSelectedGeometry(0) - , mConnectionGeometry(0) + , mDragGeometry(0) , mTag(new PathgridTag(this)) { const float CoordScalar = ESM::Land::REAL_SIZE; @@ -74,15 +73,9 @@ namespace CSVRender mBaseNode->setNodeMask(Mask_Pathgrid); mParent->addChild(mBaseNode); - mSelectedNode = new osg::PositionAttitudeTransform(); - mBaseNode->addChild(mSelectedNode); - mPathgridGeode = new osg::Geode(); mBaseNode->addChild(mPathgridGeode); - mSelectedGeode = new osg::Geode(); - mSelectedNode->addChild(mSelectedGeode); - recreateGeometry(); int index = mData.getCells().searchId(mId); @@ -181,67 +174,56 @@ namespace CSVRender void Pathgrid::moveSelected(const osg::Vec3d& offset) { - mSelectedNode->setPosition(mSelectedNode->getPosition() + offset); + mUseOffset = true; + mMoveOffset += offset; + + recreateGeometry(); } - void Pathgrid::setupConnectionIndicator(unsigned short node) + void Pathgrid::setDragOrigin(unsigned short node) { - mConnectionIndicator = true; - mConnectionNode = node; - createSelectedGeometry(); + mDragOrigin = node; } - void Pathgrid::adjustConnectionIndicator(unsigned short node) + void Pathgrid::setDragEndpoint(unsigned short node) { - if (mConnectionIndicator) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid* source = getPathgridSource(); - if (source) - { - const CSMWorld::Pathgrid::Point& pointA = source->mPoints[mConnectionNode]; - const CSMWorld::Pathgrid::Point& pointB = source->mPoints[node]; + const CSMWorld::Pathgrid::Point& pointA = source->mPoints[mDragOrigin]; + const CSMWorld::Pathgrid::Point& pointB = source->mPoints[node]; - osg::Vec3f start = osg::Vec3f(pointA.mX, pointA.mY, pointA.mZ + SceneUtil::DiamondHalfHeight); - osg::Vec3f end = osg::Vec3f(pointB.mX, pointB.mY, pointB.mZ + SceneUtil::DiamondHalfHeight); + osg::Vec3f start = osg::Vec3f(pointA.mX, pointA.mY, pointA.mZ + SceneUtil::DiamondHalfHeight); + osg::Vec3f end = osg::Vec3f(pointB.mX, pointB.mY, pointB.mZ + SceneUtil::DiamondHalfHeight); - createConnectionGeometry(start, end, true); - } + createDragGeometry(start, end, true); } } - void Pathgrid::adjustConnectionIndicator(const osg::Vec3d& pos) + void Pathgrid::setDragEndpoint(const osg::Vec3d& pos) { - if (mConnectionIndicator) + const CSMWorld::Pathgrid* source = getPathgridSource(); + if (source) { - const CSMWorld::Pathgrid* source = getPathgridSource(); - if (source) - { - const CSMWorld::Pathgrid::Point& point = source->mPoints[mConnectionNode]; + const CSMWorld::Pathgrid::Point& point = source->mPoints[mDragOrigin]; - osg::Vec3f start = osg::Vec3f(point.mX, point.mY, point.mZ + SceneUtil::DiamondHalfHeight); - osg::Vec3f end = pos - mBaseNode->getPosition(); - createConnectionGeometry(start, end, false); - } + osg::Vec3f start = osg::Vec3f(point.mX, point.mY, point.mZ + SceneUtil::DiamondHalfHeight); + osg::Vec3f end = pos - mBaseNode->getPosition(); + createDragGeometry(start, end, false); } } void Pathgrid::resetIndicators() { - mSelectedNode->setPosition(osg::Vec3f(0,0,0)); - if (mConnectionIndicator) - { - mConnectionIndicator = false; + mUseOffset = false; + mMoveOffset.set(0, 0, 0); - mPathgridGeode->removeDrawable(mConnectionGeometry); - mConnectionGeometry = 0; - - createSelectedGeometry(); - } + mPathgridGeode->removeDrawable(mDragGeometry); + mDragGeometry = 0; } void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); @@ -310,7 +292,7 @@ namespace CSVRender const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { - osg::Vec3d localCoords = mSelectedNode->getPosition(); + osg::Vec3d localCoords = mMoveOffset; int offsetX = static_cast(localCoords.x()); int offsetY = static_cast(localCoords.y()); @@ -525,6 +507,21 @@ namespace CSVRender const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { + CSMWorld::Pathgrid temp; + if (mUseOffset) + { + temp = *source; + + for (NodeList::iterator it = mSelected.begin(); it != mSelected.end(); ++it) + { + temp.mPoints[*it].mX += mMoveOffset.x(); + temp.mPoints[*it].mY += mMoveOffset.y(); + temp.mPoints[*it].mZ += mMoveOffset.z(); + } + + source = &temp; + } + removePathgridGeometry(); mPathgridGeometry = SceneUtil::createPathgridGeometry(*source); mPathgridGeode->addDrawable(mPathgridGeometry); @@ -555,24 +552,8 @@ namespace CSVRender { removeSelectedGeometry(); - if (mConnectionIndicator) - { - NodeList tempList = NodeList(mSelected); - - NodeList::iterator searchResult = std::find(tempList.begin(), tempList.end(), mConnectionNode); - if (searchResult != tempList.end()) - tempList.erase(searchResult); - - tempList.push_back(mConnectionNode); - - mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, tempList); - mSelectedGeode->addDrawable(mSelectedGeometry); - } - else - { - mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, mSelected); - mSelectedGeode->addDrawable(mSelectedGeometry); - } + mSelectedGeometry = SceneUtil::createPathgridSelectedWireframe(source, mSelected); + mPathgridGeode->addDrawable(mSelectedGeometry); } void Pathgrid::removePathgridGeometry() @@ -588,17 +569,17 @@ namespace CSVRender { if (mSelectedGeometry) { - mSelectedGeode->removeDrawable(mSelectedGeometry); + mPathgridGeode->removeDrawable(mSelectedGeometry); mSelectedGeometry = 0; } } - void Pathgrid::createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid) + void Pathgrid::createDragGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid) { - if (mConnectionGeometry) - mPathgridGeode->removeDrawable(mConnectionGeometry); + if (mDragGeometry) + mPathgridGeode->removeDrawable(mDragGeometry); - mConnectionGeometry = new osg::Geometry(); + mDragGeometry = new osg::Geometry(); osg::ref_ptr vertices = new osg::Vec3Array(2); osg::ref_ptr colors = new osg::Vec4Array(1); @@ -619,12 +600,12 @@ namespace CSVRender indices->setElement(0, 0); indices->setElement(1, 1); - mConnectionGeometry->setVertexArray(vertices); - mConnectionGeometry->setColorArray(colors, osg::Array::BIND_OVERALL); - mConnectionGeometry->addPrimitiveSet(indices); - mConnectionGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + mDragGeometry->setVertexArray(vertices); + mDragGeometry->setColorArray(colors, osg::Array::BIND_OVERALL); + mDragGeometry->addPrimitiveSet(indices); + mDragGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - mPathgridGeode->addDrawable(mConnectionGeometry); + mPathgridGeode->addDrawable(mDragGeometry); } const CSMWorld::Pathgrid* Pathgrid::getPathgridSource() diff --git a/apps/opencs/view/render/pathgrid.hpp b/apps/opencs/view/render/pathgrid.hpp index 415b252e0..181a62b44 100644 --- a/apps/opencs/view/render/pathgrid.hpp +++ b/apps/opencs/view/render/pathgrid.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "../../model/world/cellcoordinates.hpp" #include "../../model/world/idcollection.hpp" @@ -18,7 +19,6 @@ namespace osg class Geometry; class Group; class PositionAttitudeTransform; - class Vec3d; } namespace CSMWorld @@ -69,9 +69,9 @@ namespace CSVRender void clearSelected(); void moveSelected(const osg::Vec3d& offset); - void setupConnectionIndicator(unsigned short node); - void adjustConnectionIndicator(unsigned short node); - void adjustConnectionIndicator(const osg::Vec3d& pos); + void setDragOrigin(unsigned short node); + void setDragEndpoint(unsigned short node); + void setDragEndpoint(const osg::Vec3d& pos); void resetIndicators(); @@ -98,20 +98,19 @@ namespace CSVRender bool mInterior; NodeList mSelected; - bool mConnectionIndicator; - unsigned short mConnectionNode; + osg::Vec3d mMoveOffset; + unsigned short mDragOrigin; bool mChangeGeometry; bool mRemoveGeometry; + bool mUseOffset; osg::Group* mParent; osg::ref_ptr mBaseNode; - osg::ref_ptr mSelectedNode; osg::ref_ptr mPathgridGeode; - osg::ref_ptr mSelectedGeode; osg::ref_ptr mPathgridGeometry; osg::ref_ptr mSelectedGeometry; - osg::ref_ptr mConnectionGeometry; + osg::ref_ptr mDragGeometry; osg::ref_ptr mTag; @@ -121,7 +120,7 @@ namespace CSVRender void removePathgridGeometry(); void removeSelectedGeometry(); - void createConnectionGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid); + void createDragGeometry(const osg::Vec3f& start, const osg::Vec3f& end, bool valid); const CSMWorld::Pathgrid* getPathgridSource(); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 714c548a7..87ec80556 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -163,7 +163,7 @@ namespace CSVRender mEdgeId = tag->getPathgrid()->getId(); mFromNode = SceneUtil::getPathgridNode(static_cast(hit.index0)); - tag->getPathgrid()->setupConnectionIndicator(mFromNode); + tag->getPathgrid()->setDragOrigin(mFromNode); return true; } } @@ -201,11 +201,11 @@ namespace CSVRender if (hit.tag && (tag = dynamic_cast(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId) { unsigned short node = SceneUtil::getPathgridNode(static_cast(hit.index0)); - cell->getPathgrid()->adjustConnectionIndicator(node); + cell->getPathgrid()->setDragEndpoint(node); } else { - cell->getPathgrid()->adjustConnectionIndicator(hit.worldPos); + cell->getPathgrid()->setDragEndpoint(hit.worldPos); } }