From 7dd7be7f0ec646a8691877ad0e51defd103f00a5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 10 Jan 2016 08:56:15 +0100 Subject: [PATCH 1/4] make scenes drop target for referenceables --- apps/opencs/view/render/instancemode.cpp | 84 +++++++++++++++++++ apps/opencs/view/render/instancemode.hpp | 4 + .../view/render/pagedworldspacewidget.cpp | 11 +++ .../view/render/pagedworldspacewidget.hpp | 2 + .../view/render/unpagedworldspacewidget.cpp | 5 ++ .../view/render/unpagedworldspacewidget.hpp | 2 + apps/opencs/view/render/worldspacewidget.cpp | 48 +++++++++++ apps/opencs/view/render/worldspacewidget.hpp | 13 +++ 8 files changed, 169 insertions(+) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index a4d147bb4..6c3593369 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -1,8 +1,13 @@ #include "instancemode.hpp" +#include + #include "../../model/prefs/state.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/commands.hpp" + #include "elements.hpp" #include "object.hpp" #include "worldspacewidget.hpp" @@ -55,3 +60,82 @@ void CSVRender::InstanceMode::secondarySelectPressed (osg::ref_ptr tag) } } } + +void CSVRender::InstanceMode::dragEnterEvent (QDragEnterEvent *event) +{ + if (const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData())) + { + if (!mime->fromDocument (getWorldspaceWidget().getDocument())) + return; + + /// \todo document check + if (mime->holdsType (CSMWorld::UniversalId::Type_Referenceable)) + event->accept(); + } +} + +void CSVRender::InstanceMode::dropEvent (QDropEvent* event) +{ + if (const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData())) + { + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + + if (!mime->fromDocument (document)) + return; + + osg::Vec3f insertPoint = getWorldspaceWidget().getIntersectionPoint (event->pos()); + + std::string cellId = getWorldspaceWidget().getCellId (insertPoint); + + bool dropped = false; + + std::vector ids = mime->getData(); + + CSMWorld::IdTable& referencesTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_References)); + + CSMWorld::IdTable& cellTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + for (std::vector::const_iterator iter (ids.begin()); + iter!=ids.end(); ++iter) + if (mime->isReferencable (iter->getType())) + { + // create reference + std::auto_ptr createCommand ( + new CSMWorld::CreateCommand ( + referencesTable, document.getData().getReferences().getNewId())); + + createCommand->addValue (referencesTable.findColumnIndex ( + CSMWorld::Columns::ColumnId_Cell), QString::fromUtf8 (cellId.c_str())); + createCommand->addValue (referencesTable.findColumnIndex ( + CSMWorld::Columns::ColumnId_PositionXPos), insertPoint.x()); + createCommand->addValue (referencesTable.findColumnIndex ( + CSMWorld::Columns::ColumnId_PositionYPos), insertPoint.y()); + createCommand->addValue (referencesTable.findColumnIndex ( + CSMWorld::Columns::ColumnId_PositionZPos), insertPoint.z()); + createCommand->addValue (referencesTable.findColumnIndex ( + CSMWorld::Columns::ColumnId_ReferenceableId), + QString::fromUtf8 (iter->getId().c_str())); + + // increase reference count in cell + QModelIndex countIndex = cellTable.getModelIndex (cellId, + cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_RefNumCounter)); + + int count = cellTable.data (countIndex).toInt(); + + std::auto_ptr incrementCommand ( + new CSMWorld::ModifyCommand (cellTable, countIndex, count+1)); + + document.getUndoStack().beginMacro (createCommand->text()); + document.getUndoStack().push (createCommand.release()); + document.getUndoStack().push (incrementCommand.release()); + document.getUndoStack().endMacro(); + + dropped = true; + } + + if (dropped) + event->accept(); + } +} diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 66451bd99..7649c241c 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -20,6 +20,10 @@ namespace CSVRender virtual void primarySelectPressed (osg::ref_ptr tag); virtual void secondarySelectPressed (osg::ref_ptr tag); + + virtual void dragEnterEvent (QDragEnterEvent *event); + + virtual void dropEvent (QDropEvent* event); }; } diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index dae5990e6..e30f238a2 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -504,6 +504,17 @@ void CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask) flagAsModified(); } +std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const +{ + const int cellSize = 8192; + + CSMWorld::CellCoordinates cellCoordinates ( + static_cast (std::floor (point.x()/cellSize)), + static_cast (std::floor (point.y()/cellSize))); + + return cellCoordinates.getId (mWorldspace); +} + CSVWidget::SceneToolToggle *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector ( CSVWidget::SceneToolbar *parent) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index e983fddd5..bc8e0da64 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -96,6 +96,8 @@ namespace CSVRender /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask); + virtual std::string getCellId (const osg::Vec3f& point) const; + protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 68f068dac..701f50825 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -108,6 +108,11 @@ void CSVRender::UnpagedWorldspaceWidget::clearSelection (int elementMask) flagAsModified(); } +std::string CSVRender::UnpagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const +{ + return mCellId; +} + void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index 9e6fa97f4..70a20c216 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -46,6 +46,8 @@ namespace CSVRender /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask); + virtual std::string getCellId (const osg::Vec3f& point) const; + private: virtual void referenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index f0d398641..0383b0bcf 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -315,6 +315,54 @@ CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument() return mDocument; } +osg::Vec3f CSVRender::WorldspaceWidget::getIntersectionPoint (const QPoint& localPos, + unsigned int interactionMask, bool ignoreHidden) 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)); + + intersector->setIntersectionLimit (osgUtil::LineSegmentIntersector::NO_LIMIT); + osgUtil::IntersectionVisitor visitor (intersector); + + unsigned int mask = interactionMask; + + if (ignoreHidden) + mask &= getVisibilityMask(); + + visitor.setTraversalMask (mask << 1); + + mView->getCamera()->accept (visitor); + + for (osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersector->getIntersections().begin(); + iter!=intersector->getIntersections().end(); ++iter) + { + // reject back-facing polygons + osg::Vec3f normal = osg::Matrix::transform3x3 ( + iter->getWorldIntersectNormal(), mView->getCamera()->getViewMatrix()); + + if (normal.z()>=0) + return iter->getWorldIntersectPoint(); + } + + 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(); + + return start+direction * 50; +} + void CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 54376cee4..0edf9f0b2 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -127,6 +127,19 @@ namespace CSVRender /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask) = 0; + /// Return the next intersection point 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 + /// returned. + /// + /// \param ignoreHidden ignore elements specified in interactionMask that are + /// flagged as not visible. + osg::Vec3f getIntersectionPoint (const QPoint& localPos, + unsigned int interactionMask = Element_Reference | Element_Terrain, + bool ignoreHidden = false) const; + + virtual std::string getCellId (const osg::Vec3f& point) const = 0; + protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); From 5b9d6cce989720aa65828bcba8e13b78d10758af Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 11 Jan 2016 09:03:02 +0100 Subject: [PATCH 2/4] made drop distance configurable --- apps/opencs/model/prefs/state.cpp | 5 +++++ apps/opencs/view/render/worldspacewidget.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 8b827d0a2..d2ad0f6ef 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -190,6 +190,11 @@ void CSMPrefs::State::declare() declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false); declareInt ("scene-delay", "Tooltip delay in milliseconds", 500). setMin (1); + + declareCategory ("Scene Drops"); + declareInt ("distance", "Drop Distance", 50). + setTooltip ("If an instance drop can not be placed against another object at the " + "insert point, it will be placed by this distance from the insert point instead"); } void CSMPrefs::State::declareCategory (const std::string& key) diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 0383b0bcf..536126689 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -360,7 +360,7 @@ osg::Vec3f CSVRender::WorldspaceWidget::getIntersectionPoint (const QPoint& loca osg::Vec3d direction = end-start; direction.normalize(); - return start+direction * 50; + return start + direction * CSMPrefs::get()["Scene Drops"]["distance"].toInt(); } void CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event) From 6eba647a9d4b88925997aba9f2495f367857a519 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 14 Jan 2016 13:19:37 +0100 Subject: [PATCH 3/4] added accessor function for cell selection in paged worldspace --- apps/opencs/view/render/pagedworldspacewidget.cpp | 5 +++++ apps/opencs/view/render/pagedworldspacewidget.hpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index e30f238a2..414076cea 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -432,6 +432,11 @@ void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSel emit cellSelectionChanged (mSelection); } +const CSMWorld::CellSelection& CSVRender::PagedWorldspaceWidget::getCellSelection() const +{ + return mSelection; +} + std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (const std::string& record) const { std::istringstream stream (record.c_str()); diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index bc8e0da64..647341d1f 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -80,6 +80,8 @@ namespace CSVRender void setCellSelection(const CSMWorld::CellSelection& selection); + const CSMWorld::CellSelection& getCellSelection() const; + /// \return Drop handled? virtual bool handleDrop (const std::vector& data, DropType type); From 55627c08532356e348e115af58da90f9bb55af7d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 14 Jan 2016 13:20:01 +0100 Subject: [PATCH 4/4] handling drops into cells that do not exist or are not shown --- apps/opencs/model/prefs/state.cpp | 13 ++++ apps/opencs/view/render/instancemode.cpp | 84 ++++++++++++++++++++---- 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index d2ad0f6ef..859fabd11 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -191,10 +191,23 @@ void CSMPrefs::State::declare() declareInt ("scene-delay", "Tooltip delay in milliseconds", 500). setMin (1); + EnumValue createAndInsert ("Create cell and insert"); + EnumValue showAndInsert ("Show cell and insert"); + EnumValue dontInsert ("Discard"); + EnumValue insertAnyway ("Insert anyway"); + EnumValues insertOutsideCell; + insertOutsideCell.add (createAndInsert).add (dontInsert).add (insertAnyway); + EnumValues insertOutsideVisibleCell; + insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway); + declareCategory ("Scene Drops"); declareInt ("distance", "Drop Distance", 50). setTooltip ("If an instance drop can not be placed against another object at the " "insert point, it will be placed by this distance from the insert point instead"); + declareEnum ("outside-drop", "Handling drops outside of cells", createAndInsert). + addValues (insertOutsideCell); + declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). + addValues (insertOutsideVisibleCell); } void CSMPrefs::State::declareCategory (const std::string& key) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 6c3593369..b6430036b 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -6,11 +6,13 @@ #include "../../model/prefs/state.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" #include "elements.hpp" #include "object.hpp" #include "worldspacewidget.hpp" +#include "pagedworldspacewidget.hpp" CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Element_Reference, "Instance editing", @@ -68,7 +70,6 @@ void CSVRender::InstanceMode::dragEnterEvent (QDragEnterEvent *event) if (!mime->fromDocument (getWorldspaceWidget().getDocument())) return; - /// \todo document check if (mime->holdsType (CSMWorld::UniversalId::Type_Referenceable)) event->accept(); } @@ -87,15 +88,68 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) std::string cellId = getWorldspaceWidget().getCellId (insertPoint); - bool dropped = false; + CSMWorld::IdTree& cellTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); - std::vector ids = mime->getData(); + bool noCell = document.getData().getCells().searchId (cellId)==-1; + + if (noCell) + { + std::string mode = CSMPrefs::get()["Scene Drops"]["outside-drop"].toString(); + + // target cell does not exist + if (mode=="Discard") + return; + + if (mode=="Create cell and insert") + { + std::auto_ptr createCommand ( + new CSMWorld::CreateCommand (cellTable, cellId)); + + int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); + int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior); + createCommand->addNestedValue (parentIndex, index, false); + + document.getUndoStack().push (createCommand.release()); + + if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + + noCell = false; + } + } + else if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first)) + { + // target cell exists, but is not shown + std::string mode = + CSMPrefs::get()["Scene Drops"]["outside-visible-drop"].toString(); + + if (mode=="Discard") + return; + + if (mode=="Show cell and insert") + { + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + } + } CSMWorld::IdTable& referencesTable = dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_References)); - CSMWorld::IdTable& cellTable = dynamic_cast ( - *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + bool dropped = false; + + std::vector ids = mime->getData(); for (std::vector::const_iterator iter (ids.begin()); iter!=ids.end(); ++iter) @@ -118,18 +172,24 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) CSMWorld::Columns::ColumnId_ReferenceableId), QString::fromUtf8 (iter->getId().c_str())); - // increase reference count in cell - QModelIndex countIndex = cellTable.getModelIndex (cellId, - cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_RefNumCounter)); + std::auto_ptr incrementCommand; + + if (!noCell) + { + // increase reference count in cell + QModelIndex countIndex = cellTable.getModelIndex (cellId, + cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_RefNumCounter)); - int count = cellTable.data (countIndex).toInt(); + int count = cellTable.data (countIndex).toInt(); - std::auto_ptr incrementCommand ( - new CSMWorld::ModifyCommand (cellTable, countIndex, count+1)); + incrementCommand.reset ( + new CSMWorld::ModifyCommand (cellTable, countIndex, count+1)); + } document.getUndoStack().beginMacro (createCommand->text()); document.getUndoStack().push (createCommand.release()); - document.getUndoStack().push (incrementCommand.release()); + if (incrementCommand.get()) + document.getUndoStack().push (incrementCommand.release()); document.getUndoStack().endMacro(); dropped = true;