diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0cce952..fa33b6647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Feature #2386: Distant Statics in the form of Object Paging Feature #2404: Levelled List can not be placed into a container Feature #2686: Timestamps in openmw.log + Feature #3171: OpenMW-CS: Instance drag selection Feature #4894: Consider actors as obstacles for pathfinding Feature #5043: Head Bobbing Feature #5199: Improve Scene Colors diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 69ad0cc1b..fdf27dc93 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -21,7 +21,7 @@ New Features: - Basics of Collada animations are now supported via osgAnimation plugin (#5456) New Editor Features: -- ? +- Instance selection modes are now implemented (centred cube, corner-dragged cube, sphere) with four user-configurable actions (select only, add to selection, remove from selection, invert selection) (#3171) Bug Fixes: - NiParticleColorModifier in NIF files is now properly handled which solves issues regarding particle effects, e.g., smoke and fire (#1952, #3676) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 588be9ccb..32b1ee33f 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -247,6 +247,15 @@ void CSMPrefs::State::declare() EnumValues landeditOutsideVisibleCell; landeditOutsideVisibleCell.add (showAndLandEdit).add (dontLandEdit); + EnumValue SelectOnly ("Select only"); + EnumValue SelectAdd ("Add to selection"); + EnumValue SelectRemove ("Remove from selection"); + EnumValue selectInvert ("Invert selection"); + EnumValues primarySelectAction; + primarySelectAction.add (SelectOnly).add (SelectAdd).add (SelectRemove).add (selectInvert); + EnumValues secondarySelectAction; + secondarySelectAction.add (SelectOnly).add (SelectAdd).add (SelectRemove).add (selectInvert); + declareCategory ("3D Scene Editing"); declareInt ("distance", "Drop Distance", 50). setTooltip ("If an instance drop can not be placed against another object at the " @@ -276,6 +285,12 @@ void CSMPrefs::State::declare() declareBool ("open-list-view", "Open displays list view", false). setTooltip ("When opening a reference from the scene view, it will open the" " instance list view instead of the individual instance record view."); + declareEnum ("primary-select-action", "Action for primary select", SelectOnly). + setTooltip("Selection can be chosen between select only, add to selection, remove from selection and invert selection."). + addValues (primarySelectAction); + declareEnum ("secondary-select-action", "Action for secondary select", SelectAdd). + setTooltip("Selection can be chosen between select only, add to selection, remove from selection and invert selection."). + addValues (secondarySelectAction); declareCategory ("Key Bindings"); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 75d83cf63..2502dc1fd 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -1,5 +1,7 @@ #include "cell.hpp" +#include + #include #include #include @@ -25,6 +27,7 @@ #include "pathgrid.hpp" #include "terrainstorage.hpp" #include "object.hpp" +#include "instancedragmodes.hpp" namespace CSVRender { @@ -496,6 +499,50 @@ void CSVRender::Cell::selectAllWithSameParentId (int elementMask) } } +void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode) +{ + if (dragMode == DragMode_Select_Only || dragMode == DragMode_Select_Add) + object->setSelected(true); + + else if (dragMode == DragMode_Select_Remove) + object->setSelected(false); + + else if (dragMode == DragMode_Select_Invert) + object->setSelected (!object->getSelected()); +} + +void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) +{ + for (auto& object : mObjects) + { + if (dragMode == DragMode_Select_Only) object.second->setSelected (false); + + if ( ( object.second->getPosition().pos[0] > pointA[0] && object.second->getPosition().pos[0] < pointB[0] ) || + ( object.second->getPosition().pos[0] > pointB[0] && object.second->getPosition().pos[0] < pointA[0] )) + { + if ( ( object.second->getPosition().pos[1] > pointA[1] && object.second->getPosition().pos[1] < pointB[1] ) || + ( object.second->getPosition().pos[1] > pointB[1] && object.second->getPosition().pos[1] < pointA[1] )) + { + if ( ( object.second->getPosition().pos[2] > pointA[2] && object.second->getPosition().pos[2] < pointB[2] ) || + ( object.second->getPosition().pos[2] > pointB[2] && object.second->getPosition().pos[2] < pointA[2] )) + handleSelectDrag(object.second, dragMode); + } + + } + } +} + +void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) +{ + for (auto& object : mObjects) + { + if (dragMode == DragMode_Select_Only) object.second->setSelected (false); + + float distanceFromObject = (point - object.second->getPosition().asVec3()).length(); + if (distanceFromObject < distance) handleSelectDrag(object.second, dragMode); + } +} + void CSVRender::Cell::setCellArrows (int mask) { for (int i=0; i<4; ++i) diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 281ac6735..5998a4ee6 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -10,6 +10,7 @@ #include "../../model/world/cellcoordinates.hpp" #include "terrainstorage.hpp" +#include "instancedragmodes.hpp" class QModelIndex; @@ -152,6 +153,12 @@ namespace CSVRender // already selected void selectAllWithSameParentId (int elementMask); + void handleSelectDrag(Object* object, DragMode dragMode); + + void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode); + + void selectWithinDistance(const osg::Vec3d& pointA, float distance, DragMode dragMode); + void setCellArrows (int mask); /// \brief Set marker for this cell. diff --git a/apps/opencs/view/render/instancedragmodes.hpp b/apps/opencs/view/render/instancedragmodes.hpp new file mode 100644 index 000000000..01547545a --- /dev/null +++ b/apps/opencs/view/render/instancedragmodes.hpp @@ -0,0 +1,18 @@ +#ifndef CSV_WIDGET_INSTANCEDRAGMODES_H +#define CSV_WIDGET_INSTANCEDRAGMODES_H + +namespace CSVRender +{ + enum DragMode + { + DragMode_None, + DragMode_Move, + DragMode_Rotate, + DragMode_Scale, + DragMode_Select_Only, + DragMode_Select_Add, + DragMode_Select_Remove, + DragMode_Select_Invert + }; +} +#endif diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 19018fae6..4f6759cdb 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -96,6 +96,33 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos) return pos * combined; } +osg::Vec3f CSVRender::InstanceMode::getProjectionSpaceCoords(const osg::Vec3f& pos) +{ + osg::Matrix viewMatrix = getWorldspaceWidget().getCamera()->getViewMatrix(); + osg::Matrix projMatrix = getWorldspaceWidget().getCamera()->getProjectionMatrix(); + osg::Matrix combined = viewMatrix * projMatrix; + + return pos * combined; +} + +osg::Vec3f CSVRender::InstanceMode::getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart) +{ + osg::Matrix viewMatrix; + viewMatrix.invert(getWorldspaceWidget().getCamera()->getViewMatrix()); + osg::Matrix projMatrix; + projMatrix.invert(getWorldspaceWidget().getCamera()->getProjectionMatrix()); + osg::Matrix combined = projMatrix * viewMatrix; + + /* calculate viewport normalized coordinates + note: is there a reason to use getCamera()->getViewport()->computeWindowMatrix() instead? */ + float x = (point.x() * 2) / getWorldspaceWidget().getCamera()->getViewport()->width() - 1.0f; + float y = 1.0f - (point.y() * 2) / getWorldspaceWidget().getCamera()->getViewport()->height(); + + osg::Vec3f mousePlanePoint = osg::Vec3f(x, y, dragStart.z()) * combined; + + return mousePlanePoint; +} + CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr parentNode, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), Mask_Reference | Mask_Terrain, "Instance editing", parent), mSubMode (nullptr), mSubModeId ("move"), mSelectionMode (nullptr), mDragMode (DragMode_None), @@ -146,7 +173,7 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) } if (!mSelectionMode) - mSelectionMode = new InstanceSelectionMode (toolbar, getWorldspaceWidget()); + mSelectionMode = new InstanceSelectionMode (toolbar, getWorldspaceWidget(), mParentNode); mDragMode = DragMode_None; @@ -322,6 +349,42 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag (const QPoint& pos) return false; } +bool CSVRender::InstanceMode::primarySelectStartDrag (const QPoint& pos) +{ + if (mDragMode!=DragMode_None || mLocked) + return false; + + std::string primarySelectAction = CSMPrefs::get()["3D Scene Editing"]["primary-select-action"].toString(); + + if ( primarySelectAction == "Select only" ) mDragMode = DragMode_Select_Only; + else if ( primarySelectAction == "Add to selection" ) mDragMode = DragMode_Select_Add; + else if ( primarySelectAction == "Remove from selection" ) mDragMode = DragMode_Select_Remove; + else if ( primarySelectAction == "Invert selection" ) mDragMode = DragMode_Select_Invert; + + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + mSelectionMode->setDragStart(hit.worldPos); + + return true; +} + +bool CSVRender::InstanceMode::secondarySelectStartDrag (const QPoint& pos) +{ + if (mDragMode!=DragMode_None || mLocked) + return false; + + std::string secondarySelectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString(); + + if ( secondarySelectAction == "Select only" ) mDragMode = DragMode_Select_Only; + else if ( secondarySelectAction == "Add to selection" ) mDragMode = DragMode_Select_Add; + else if ( secondarySelectAction == "Remove from selection" ) mDragMode = DragMode_Select_Remove; + else if ( secondarySelectAction == "Invert selection" ) mDragMode = DragMode_Select_Invert; + + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); + mSelectionMode->setDragStart(hit.worldPos); + + return true; +} + void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) { osg::Vec3f offset; @@ -432,6 +495,24 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou // Only uniform scaling is currently supported offset = osg::Vec3f(scale, scale, scale); } + else if (mSelectionMode->getCurrentId() == "cube-centre") + { + osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart())); + mSelectionMode->drawSelectionCubeCentre (mousePlanePoint); + return; + } + else if (mSelectionMode->getCurrentId() == "cube-corner") + { + osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart())); + mSelectionMode->drawSelectionCubeCorner (mousePlanePoint); + return; + } + else if (mSelectionMode->getCurrentId() == "sphere") + { + osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart())); + mSelectionMode->drawSelectionSphere (mousePlanePoint); + return; + } // Apply for (std::vector >::iterator iter (selection.begin()); iter!=selection.end(); ++iter) @@ -495,6 +576,22 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) case DragMode_Move: description = "Move Instances"; break; case DragMode_Rotate: description = "Rotate Instances"; break; case DragMode_Scale: description = "Scale Instances"; break; + case DragMode_Select_Only : + handleSelectDrag(pos); + return; + break; + case DragMode_Select_Add : + handleSelectDrag(pos); + return; + break; + case DragMode_Select_Remove : + handleSelectDrag(pos); + return; + break; + case DragMode_Select_Invert : + handleSelectDrag(pos); + return; + break; case DragMode_None: break; } @@ -680,6 +777,13 @@ void CSVRender::InstanceMode::subModeChanged (const std::string& id) getWorldspaceWidget().setSubMode (getSubModeFromId (id), Mask_Reference); } +void CSVRender::InstanceMode::handleSelectDrag(const QPoint& pos) +{ + osg::Vec3f mousePlanePoint = getMousePlaneCoords(pos, getProjectionSpaceCoords(mSelectionMode->getDragStart())); + mSelectionMode->dragEnded (mousePlanePoint, mDragMode); + mDragMode = DragMode_None; +} + void CSVRender::InstanceMode::deleteSelectedInstances(bool active) { std::vector > selection = getWorldspaceWidget().getSelection (Mask_Reference); diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 32dd4ac67..0a4f2e478 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -9,6 +9,7 @@ #include #include "editmode.hpp" +#include "instancedragmodes.hpp" namespace CSVWidget { @@ -25,14 +26,6 @@ namespace CSVRender { Q_OBJECT - enum DragMode - { - DragMode_None, - DragMode_Move, - DragMode_Rotate, - DragMode_Scale - }; - enum DropMode { Collision, @@ -57,6 +50,9 @@ namespace CSVRender osg::Vec3f getSelectionCenter(const std::vector >& selection) const; osg::Vec3f getScreenCoords(const osg::Vec3f& pos); + osg::Vec3f getProjectionSpaceCoords(const osg::Vec3f& pos); + osg::Vec3f getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart); + void handleSelectDrag(const QPoint& pos); void dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight); float getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight); @@ -84,6 +80,10 @@ namespace CSVRender bool secondaryEditStartDrag (const QPoint& pos) override; + bool primarySelectStartDrag(const QPoint& pos) override; + + bool secondarySelectStartDrag(const QPoint& pos) override; + void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override; void dragCompleted(const QPoint& pos) override; diff --git a/apps/opencs/view/render/instanceselectionmode.cpp b/apps/opencs/view/render/instanceselectionmode.cpp index bf8ede0eb..9b5fb759c 100644 --- a/apps/opencs/view/render/instanceselectionmode.cpp +++ b/apps/opencs/view/render/instanceselectionmode.cpp @@ -2,17 +2,24 @@ #include #include +#include + +#include +#include +#include +#include #include "../../model/world/idtable.hpp" #include "../../model/world/commands.hpp" +#include "instancedragmodes.hpp" #include "worldspacewidget.hpp" #include "object.hpp" namespace CSVRender { - InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) - : SelectionMode(parent, worldspaceWidget, Mask_Reference) + InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, osg::Group *cellNode) + : SelectionMode(parent, worldspaceWidget, Mask_Reference), mParentNode(cellNode) { mSelectSame = new QAction("Extend selection to instances with same object ID", this); mDeleteSelection = new QAction("Delete selected instances", this); @@ -21,6 +28,342 @@ namespace CSVRender connect(mDeleteSelection, SIGNAL(triggered()), this, SLOT(deleteSelection())); } + InstanceSelectionMode::~InstanceSelectionMode() + { + mParentNode->removeChild(mBaseNode); + } + + void InstanceSelectionMode::setDragStart(const osg::Vec3d& dragStart) + { + mDragStart = dragStart; + } + + const osg::Vec3d& InstanceSelectionMode::getDragStart() + { + return mDragStart; + } + + void InstanceSelectionMode::dragEnded(const osg::Vec3d& dragEndPoint, DragMode dragMode) + { + float dragDistance = (mDragStart - dragEndPoint).length(); + if (mBaseNode) mParentNode->removeChild (mBaseNode); + if (getCurrentId() == "cube-centre") + { + osg::Vec3d pointA(mDragStart[0] - dragDistance, mDragStart[1] - dragDistance, mDragStart[2] - dragDistance); + osg::Vec3d pointB(mDragStart[0] + dragDistance, mDragStart[1] + dragDistance, mDragStart[2] + dragDistance); + getWorldspaceWidget().selectInsideCube(pointA, pointB, dragMode); + } + else if (getCurrentId() == "cube-corner") + { + getWorldspaceWidget().selectInsideCube(mDragStart, dragEndPoint, dragMode); + } + else if (getCurrentId() == "sphere") + { + getWorldspaceWidget().selectWithinDistance(mDragStart, dragDistance, dragMode); + } + } + + void InstanceSelectionMode::drawSelectionCubeCentre(const osg::Vec3f& mousePlanePoint) + { + float dragDistance = (mDragStart - mousePlanePoint).length(); + drawSelectionCube(mDragStart, dragDistance); + } + + void InstanceSelectionMode::drawSelectionCubeCorner(const osg::Vec3f& mousePlanePoint) + { + drawSelectionBox(mDragStart, mousePlanePoint); + } + + void InstanceSelectionMode::drawSelectionBox(const osg::Vec3d& pointA, const osg::Vec3d& pointB) + { + if (mBaseNode) mParentNode->removeChild (mBaseNode); + mBaseNode = new osg::PositionAttitudeTransform; + mBaseNode->setPosition(pointA); + + osg::ref_ptr geometry (new osg::Geometry); + + osg::Vec3Array *vertices = new osg::Vec3Array; + vertices->push_back (osg::Vec3f (0.0f, 0.0f, 0.0f)); + vertices->push_back (osg::Vec3f (0.0f, 0.0f, pointB[2] - pointA[2])); + vertices->push_back (osg::Vec3f (0.0f, pointB[1] - pointA[1], 0.0f)); + vertices->push_back (osg::Vec3f (0.0f, pointB[1] - pointA[1], pointB[2] - pointA[2])); + + vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], 0.0f, 0.0f)); + vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], 0.0f, pointB[2] - pointA[2])); + vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], pointB[1] - pointA[1], 0.0f)); + vertices->push_back (osg::Vec3f (pointB[0] - pointA[0], pointB[1] - pointA[1], pointB[2] - pointA[2])); + + geometry->setVertexArray (vertices); + + osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0); + + // top + primitives->push_back (2); + primitives->push_back (1); + primitives->push_back (0); + + primitives->push_back (3); + primitives->push_back (1); + primitives->push_back (2); + + // bottom + primitives->push_back (4); + primitives->push_back (5); + primitives->push_back (6); + + primitives->push_back (6); + primitives->push_back (5); + primitives->push_back (7); + + // sides + primitives->push_back (1); + primitives->push_back (4); + primitives->push_back (0); + + primitives->push_back (4); + primitives->push_back (1); + primitives->push_back (5); + + primitives->push_back (4); + primitives->push_back (2); + primitives->push_back (0); + + primitives->push_back (6); + primitives->push_back (2); + primitives->push_back (4); + + primitives->push_back (6); + primitives->push_back (3); + primitives->push_back (2); + + primitives->push_back (7); + primitives->push_back (3); + primitives->push_back (6); + + primitives->push_back (1); + primitives->push_back (3); + primitives->push_back (5); + + primitives->push_back (5); + primitives->push_back (3); + primitives->push_back (7); + + geometry->addPrimitiveSet (primitives); + + osg::Vec4Array *colours = new osg::Vec4Array; + + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.5f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + + geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX); + + geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setMode (GL_BLEND, osg::StateAttribute::ON); + geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + mBaseNode->addChild (geometry); + mParentNode->addChild(mBaseNode); + } + + void InstanceSelectionMode::drawSelectionCube(const osg::Vec3d& point, float radius) + { + if (mBaseNode) mParentNode->removeChild (mBaseNode); + mBaseNode = new osg::PositionAttitudeTransform; + mBaseNode->setPosition(point); + + osg::ref_ptr geometry (new osg::Geometry); + + osg::Vec3Array *vertices = new osg::Vec3Array; + for (int i = 0; i < 2; ++i) + { + float height = i ? -radius : radius; + vertices->push_back (osg::Vec3f (height, -radius, -radius)); + vertices->push_back (osg::Vec3f (height, -radius, radius)); + vertices->push_back (osg::Vec3f (height, radius, -radius)); + vertices->push_back (osg::Vec3f (height, radius, radius)); + } + + geometry->setVertexArray (vertices); + + osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0); + + // top + primitives->push_back (2); + primitives->push_back (1); + primitives->push_back (0); + + primitives->push_back (3); + primitives->push_back (1); + primitives->push_back (2); + + // bottom + primitives->push_back (4); + primitives->push_back (5); + primitives->push_back (6); + + primitives->push_back (6); + primitives->push_back (5); + primitives->push_back (7); + + // sides + primitives->push_back (1); + primitives->push_back (4); + primitives->push_back (0); + + primitives->push_back (4); + primitives->push_back (1); + primitives->push_back (5); + + primitives->push_back (4); + primitives->push_back (2); + primitives->push_back (0); + + primitives->push_back (6); + primitives->push_back (2); + primitives->push_back (4); + + primitives->push_back (6); + primitives->push_back (3); + primitives->push_back (2); + + primitives->push_back (7); + primitives->push_back (3); + primitives->push_back (6); + + primitives->push_back (1); + primitives->push_back (3); + primitives->push_back (5); + + primitives->push_back (5); + primitives->push_back (3); + primitives->push_back (7); + + geometry->addPrimitiveSet (primitives); + + osg::Vec4Array *colours = new osg::Vec4Array; + + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.5f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + colours->push_back (osg::Vec4f (0.3f, 0.3f, 0.4f, 0.2f)); + colours->push_back (osg::Vec4f (0.9f, 0.9f, 1.0f, 0.2f)); + + geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX); + + geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setMode (GL_BLEND, osg::StateAttribute::ON); + geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + mBaseNode->addChild (geometry); + mParentNode->addChild(mBaseNode); + } + + void InstanceSelectionMode::drawSelectionSphere(const osg::Vec3f& mousePlanePoint) + { + float dragDistance = (mDragStart - mousePlanePoint).length(); + drawSelectionSphere(mDragStart, dragDistance); + } + + void InstanceSelectionMode::drawSelectionSphere(const osg::Vec3d& point, float radius) + { + if (mBaseNode) mParentNode->removeChild (mBaseNode); + mBaseNode = new osg::PositionAttitudeTransform; + mBaseNode->setPosition(point); + + osg::ref_ptr geometry (new osg::Geometry); + + osg::Vec3Array *vertices = new osg::Vec3Array; + int resolution = 32; + float radiusPerResolution = radius / resolution; + float reciprocalResolution = 1.0f / resolution; + float doubleReciprocalRes = reciprocalResolution * 2; + + osg::Vec4Array *colours = new osg::Vec4Array; + + for (float i = 0.0; i <= resolution; i += 2) + { + float iShifted = (static_cast(i) - resolution / 2.0f); // i - 16 = -16 ... 16 + float xPercentile = iShifted * doubleReciprocalRes; + float x = xPercentile * radius; + float thisRadius = sqrt (radius * radius - x * x); + + //the next row + float iShifted2 = (static_cast(i + 1) - resolution / 2.0f); + float xPercentile2 = iShifted2 * doubleReciprocalRes; + float x2 = xPercentile2 * radius; + float thisRadius2 = sqrt (radius * radius - x2 * x2); + + for (int j = 0; j < resolution; ++j) + { + float vertexX = thisRadius * sin(j * reciprocalResolution * osg::PI * 2); + float vertexY = i * radiusPerResolution * 2 - radius; + float vertexZ = thisRadius * cos(j * reciprocalResolution * osg::PI * 2); + float heightPercentage = (vertexZ + radius) / (radius * 2); + vertices->push_back (osg::Vec3f (vertexX, vertexY, vertexZ)); + colours->push_back (osg::Vec4f (heightPercentage, heightPercentage, heightPercentage, 0.3f)); + + float vertexNextRowX = thisRadius2 * sin(j * reciprocalResolution * osg::PI * 2); + float vertexNextRowY = (i + 1) * radiusPerResolution * 2 - radius; + float vertexNextRowZ = thisRadius2 * cos(j * reciprocalResolution * osg::PI * 2); + float heightPercentageNextRow = (vertexZ + radius) / (radius * 2); + vertices->push_back (osg::Vec3f (vertexNextRowX, vertexNextRowY, vertexNextRowZ)); + colours->push_back (osg::Vec4f (heightPercentageNextRow, heightPercentageNextRow, heightPercentageNextRow, 0.3f)); + } + } + + geometry->setVertexArray (vertices); + + osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLE_STRIP, 0); + + for (int i = 0; i < resolution; ++i) + { + //Even + for (int j = 0; j < resolution * 2; ++j) + { + if (i * resolution * 2 + j > static_cast(vertices->size()) - 1) continue; + primitives->push_back (i * resolution * 2 + j); + } + if (i * resolution * 2 > static_cast(vertices->size()) - 1) continue; + primitives->push_back (i * resolution * 2); + primitives->push_back (i * resolution * 2 + 1); + + //Odd + for (int j = 1; j < resolution * 2 - 2; j += 2) + { + if ((i + 1) * resolution * 2 + j - 1 > static_cast(vertices->size()) - 1) continue; + primitives->push_back ((i + 1) * resolution * 2 + j - 1); + primitives->push_back (i * resolution * 2 + j + 2); + } + if ((i + 2) * resolution * 2 - 2 > static_cast(vertices->size()) - 1) continue; + primitives->push_back ((i + 2) * resolution * 2 - 2); + primitives->push_back (i * resolution * 2 + 1); + primitives->push_back ((i + 1) * resolution * 2); + } + + geometry->addPrimitiveSet (primitives); + + geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX); + + geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setMode (GL_BLEND, osg::StateAttribute::ON); + geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + mBaseNode->addChild (geometry); + mParentNode->addChild(mBaseNode); + } + bool InstanceSelectionMode::createContextMenu(QMenu* menu) { if (menu) diff --git a/apps/opencs/view/render/instanceselectionmode.hpp b/apps/opencs/view/render/instanceselectionmode.hpp index a23811671..81795d5d3 100644 --- a/apps/opencs/view/render/instanceselectionmode.hpp +++ b/apps/opencs/view/render/instanceselectionmode.hpp @@ -1,7 +1,13 @@ #ifndef CSV_RENDER_INSTANCE_SELECTION_MODE_H #define CSV_RENDER_INSTANCE_SELECTION_MODE_H +#include + +#include +#include + #include "selectionmode.hpp" +#include "instancedragmodes.hpp" namespace CSVRender { @@ -11,8 +17,25 @@ namespace CSVRender public: - InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget); + InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, osg::Group *cellNode); + + ~InstanceSelectionMode(); + + /// Store the worldspace-coordinate when drag begins + void setDragStart(const osg::Vec3d& dragStart); + /// Store the worldspace-coordinate when drag begins + const osg::Vec3d& getDragStart(); + + /// Store the screen-coordinate when drag begins + void setScreenDragStart(const QPoint& dragStartPoint); + + /// Apply instance selection changes + void dragEnded(const osg::Vec3d& dragEndPoint, DragMode dragMode); + + void drawSelectionCubeCentre(const osg::Vec3f& mousePlanePoint ); + void drawSelectionCubeCorner(const osg::Vec3f& mousePlanePoint ); + void drawSelectionSphere(const osg::Vec3f& mousePlanePoint ); protected: /// Add context menu items to \a menu. @@ -25,8 +48,15 @@ namespace CSVRender private: + void drawSelectionBox(const osg::Vec3d& pointA, const osg::Vec3d& pointB); + void drawSelectionCube(const osg::Vec3d& point, float radius); + void drawSelectionSphere(const osg::Vec3d& point, float radius); + QAction* mDeleteSelection; QAction* mSelectSame; + osg::Vec3d mDragStart; + osg::Group* mParentNode; + osg::ref_ptr mBaseNode; private slots: diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index dca5549af..ed3558422 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -768,6 +768,22 @@ void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMas flagAsModified(); } +void CSVRender::PagedWorldspaceWidget::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) +{ + for (auto& cell : mCells) + { + cell.second->selectInsideCube (pointA, pointB, dragMode); + } +} + +void CSVRender::PagedWorldspaceWidget::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) +{ + for (auto& cell : mCells) + { + cell.second->selectWithinDistance (point, distance, dragMode); + } +} + std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const { CSMWorld::CellCoordinates cellCoordinates ( diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index d17670cfa..beab0c575 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -7,6 +7,7 @@ #include "worldspacewidget.hpp" #include "cell.hpp" +#include "instancedragmodes.hpp" namespace CSVWidget { @@ -120,6 +121,10 @@ namespace CSVRender /// \param elementMask Elements to be affected by the select operation void selectAllWithSameParentId (int elementMask) override; + void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) override; + + void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) override; + std::string getCellId (const osg::Vec3f& point) const override; Cell* getCell(const osg::Vec3d& point) const override; diff --git a/apps/opencs/view/render/selectionmode.cpp b/apps/opencs/view/render/selectionmode.cpp index b5ccda5ad..e7e7d47b5 100644 --- a/apps/opencs/view/render/selectionmode.cpp +++ b/apps/opencs/view/render/selectionmode.cpp @@ -15,30 +15,27 @@ namespace CSVRender { addButton(":scenetoolbar/selection-mode-cube", "cube-centre", "Centred cube" - "
  • Drag with {scene-select-primary} (make instances the selection) or {scene-select-secondary} " - "(invert selection state) from the centre of the selection cube outwards
  • " + "
    • Drag with {scene-select-primary} for primary select or {scene-select-secondary} for secondary select " + "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 {scene-edit-primary} or {scene-edit-secondary} not " "starting on an instance will have the same effect
    • " - "
    " - "Not implemented yet"); + "
"); addButton(":scenetoolbar/selection-mode-cube-corner", "cube-corner", "Cube corner to corner" - "
  • Drag with {scene-select-primary} (make instances the selection) or {scene-select-secondary} " - "(invert selection state) from one corner of the selection cube to the opposite corner
  • " + "
    • Drag with {scene-select-primary} for primary select or {scene-select-secondary} for secondary select " + "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 {scene-edit-primary} or {scene-edit-secondary} not " "starting on an instance will have the same effect
    • " - "
    " - "Not implemented yet"); + "
"); addButton(":scenetoolbar/selection-mode-cube-sphere", "sphere", "Centred sphere" - "
  • Drag with {scene-select-primary} (make instances the selection) or {scene-select-secondary} " - "(invert selection state) from the centre of the selection sphere outwards
  • " + "
    • Drag with {scene-select-primary} for primary select or {scene-select-secondary} for secondary select " + "from the centre of the selection sphere outwards
    • " "
    • If context selection mode is enabled, a drag with {scene-edit-primary} or {scene-edit-secondary} 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); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index b1088aa60..c4fef45dd 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -140,6 +140,16 @@ void CSVRender::UnpagedWorldspaceWidget::selectAllWithSameParentId (int elementM flagAsModified(); } +void CSVRender::UnpagedWorldspaceWidget::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) +{ + mCell->selectInsideCube (pointA, pointB, dragMode); +} + +void CSVRender::UnpagedWorldspaceWidget::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) +{ + mCell->selectWithinDistance (point, distance, dragMode); +} + std::string CSVRender::UnpagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const { return mCellId; diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index eec1b01f3..83233c327 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -60,6 +60,10 @@ namespace CSVRender /// \param elementMask Elements to be affected by the select operation void selectAllWithSameParentId (int elementMask) override; + void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) override; + + void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) override; + std::string getCellId (const osg::Vec3f& point) const override; Cell* getCell(const osg::Vec3d& point) const override; diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 3b8cf70c2..5e224b380 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -7,6 +7,7 @@ #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" +#include "instancedragmodes.hpp" #include "scenewidget.hpp" #include "mask.hpp" @@ -160,6 +161,10 @@ namespace CSVRender /// \param elementMask Elements to be affected by the select operation virtual void selectAllWithSameParentId (int elementMask) = 0; + virtual void selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode) = 0; + + virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0; + /// Return the next intersection with scene elements matched by /// \a interactionMask based on \a localPos and the camera vector. /// If there is no such intersection, instead a point "in front" of \a localPos will be