diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6401e4222..ce0216066 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -85,7 +85,7 @@ opencs_units (view/widget opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget - previewwidget editmode instancemode instanceselectionmode + previewwidget editmode instancemode instanceselectionmode instancemovemode ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index e7b135891..d666304fa 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -338,3 +338,16 @@ std::vector > CSVRender::Cell::getSelection (un return result; } + +std::vector > CSVRender::Cell::getEdited (unsigned int elementMask) const +{ + std::vector > result; + + if (elementMask & Mask_Reference) + for (std::map::const_iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + if (iter->second->isEdited()) + result.push_back (iter->second->getTag()); + + return result; +} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 22f9872e3..6cdafbf36 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -116,6 +116,8 @@ namespace CSVRender bool isDeleted() const; std::vector > getSelection (unsigned int elementMask) const; + + std::vector > getEdited (unsigned int elementMask) const; }; } diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4b6b2e41f..f2fb552b0 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -18,10 +18,11 @@ #include "worldspacewidget.hpp" #include "pagedworldspacewidget.hpp" #include "instanceselectionmode.hpp" +#include "instancemovemode.hpp" CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing", - parent), mSubMode (0), mSelectionMode (0) + parent), mSubMode (0), mSelectionMode (0), mDragMode (DragMode_None) { } @@ -30,12 +31,7 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) if (!mSubMode) { mSubMode = new CSVWidget::SceneToolMode (toolbar, "Edit Sub-Mode"); - mSubMode->addButton (":placeholder", "move", - "Move selected instances" - "
  • Use primary edit to move instances around freely
  • " - "
  • Use secondary edit to move instances around within the grid
  • " - "
" - "Not implemented yet"); + mSubMode->addButton (new InstanceMoveMode (this), "move"); mSubMode->addButton (":placeholder", "rotate", "Rotate selected instances" "
  • Use primary edit to rotate instances freely
  • " @@ -53,6 +49,8 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) if (!mSelectionMode) mSelectionMode = new InstanceSelectionMode (toolbar, getWorldspaceWidget()); + mDragMode = DragMode_None; + EditMode::activate (toolbar); toolbar->addTool (mSubMode); @@ -120,6 +118,154 @@ void CSVRender::InstanceMode::secondarySelectPressed (osg::ref_ptr tag) } } +bool CSVRender::InstanceMode::primaryEditStartDrag (osg::ref_ptr tag) +{ + if (mDragMode!=DragMode_None) + return false; + + if (tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) + { + getWorldspaceWidget().clearSelection (Mask_Reference); + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + CSVRender::Object* object = objectTag->mObject; + object->setSelected (true); + } + } + + std::vector > selection = + getWorldspaceWidget().getSelection (Mask_Reference); + + if (selection.empty()) + return false; + + // \todo check for sub-mode + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + objectTag->mObject->setEdited (Object::Override_Position); + } + } + + mDragMode = DragMode_Move; + + return true; +} + +bool CSVRender::InstanceMode::secondaryEditStartDrag (osg::ref_ptr tag) +{ + + return false; +} + +void CSVRender::InstanceMode::drag (int diffX, int diffY, double speedFactor) +{ + switch (mDragMode) + { + case DragMode_Move: + { + osg::Vec3f eye; + osg::Vec3f centre; + osg::Vec3f up; + + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); + + osg::Vec3f offset; + + if (diffY) + offset += up * diffY * speedFactor; + + if (diffX) + offset += ((centre-eye) ^ up) * diffX * speedFactor; + + std::vector > selection = + getWorldspaceWidget().getEdited (Mask_Reference); + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + ESM::Position position = objectTag->mObject->getPosition(); + for (int i=0; i<3; ++i) + position.pos[i] += offset[i]; + objectTag->mObject->setPosition (position.pos); + } + } + + break; + } + + case DragMode_None: break; + } +} + +void CSVRender::InstanceMode::dragCompleted() +{ + std::vector > selection = + getWorldspaceWidget().getEdited (Mask_Reference); + + QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); + + QString description; + + switch (mDragMode) + { + case DragMode_Move: description = "Move Instances"; break; + + case DragMode_None: break; + } + + undoStack.beginMacro (description); + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + objectTag->mObject->apply (undoStack); + } + } + + undoStack.endMacro(); + + mDragMode = DragMode_None; +} + +void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor) +{ + if (mDragMode==DragMode_Move) + { + osg::Vec3f eye; + osg::Vec3f centre; + osg::Vec3f up; + + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); + + osg::Vec3f offset = centre - eye; + offset.normalize(); + offset *= diff * speedFactor; + + std::vector > selection = + getWorldspaceWidget().getEdited (Mask_Reference); + + for (std::vector >::iterator iter (selection.begin()); + iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + ESM::Position position = objectTag->mObject->getPosition(); + for (int i=0; i<3; ++i) + position.pos[i] += offset[i]; + objectTag->mObject->setPosition (position.pos); + } + } + } +} + void CSVRender::InstanceMode::dragEnterEvent (QDragEnterEvent *event) { if (const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData())) diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 78836878a..7662a0cdc 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -15,8 +15,17 @@ namespace CSVRender class InstanceMode : public EditMode { Q_OBJECT + + enum DragMode + { + DragMode_None, + DragMode_Move + + }; + CSVWidget::SceneToolMode *mSubMode; InstanceSelectionMode *mSelectionMode; + DragMode mDragMode; public: @@ -34,6 +43,16 @@ namespace CSVRender virtual void secondarySelectPressed (osg::ref_ptr tag); + virtual bool primaryEditStartDrag (osg::ref_ptr tag); + + virtual bool secondaryEditStartDrag (osg::ref_ptr tag); + + virtual void drag (int diffX, int diffY, double speedFactor); + + virtual void dragCompleted(); + + virtual void dragWheel (int diff, double speedFactor); + virtual void dragEnterEvent (QDragEnterEvent *event); virtual void dropEvent (QDropEvent* event); diff --git a/apps/opencs/view/render/instancemovemode.cpp b/apps/opencs/view/render/instancemovemode.cpp new file mode 100644 index 000000000..fd42b9f69 --- /dev/null +++ b/apps/opencs/view/render/instancemovemode.cpp @@ -0,0 +1,12 @@ + +#include "instancemovemode.hpp" + +CSVRender::InstanceMoveMode::InstanceMoveMode (QWidget *parent) +: ModeButton (QIcon (QPixmap (":placeholder")), + "Move selected instances" + "
    • Use primary edit to move instances around freely
    • " + "
    • Use secondary edit to move instances around within the grid
    • " + "
    " + "Not implemented yet", + parent) +{} diff --git a/apps/opencs/view/render/instancemovemode.hpp b/apps/opencs/view/render/instancemovemode.hpp new file mode 100644 index 000000000..bd0e28dac --- /dev/null +++ b/apps/opencs/view/render/instancemovemode.hpp @@ -0,0 +1,18 @@ +#ifndef CSV_RENDER_INSTANCEMOVEMODE_H +#define CSV_RENDER_INSTANCEMOVEMODE_H + +#include "../widget/modebutton.hpp" + +namespace CSVRender +{ + class InstanceMoveMode : public CSVWidget::ModeButton + { + Q_OBJECT + + public: + + InstanceMoveMode (QWidget *parent = 0); + }; +} + +#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 33939625d..1e2fd2640 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -15,6 +15,8 @@ #include "../../model/world/data.hpp" #include "../../model/world/ref.hpp" #include "../../model/world/refidcollection.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/universalid.hpp" #include #include @@ -130,18 +132,20 @@ void CSVRender::Object::adjustTransform() if (mReferenceId.empty()) return; - const CSMWorld::CellRef& reference = getReference(); + ESM::Position position = getPosition(); // position - mBaseNode->setPosition(mForceBaseToZero ? osg::Vec3() : osg::Vec3f(reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2])); + mBaseNode->setPosition(mForceBaseToZero ? osg::Vec3() : osg::Vec3f(position.pos[0], position.pos[1], position.pos[2])); // orientation - osg::Quat xr (-reference.mPos.rot[0], osg::Vec3f(1,0,0)); - osg::Quat yr (-reference.mPos.rot[1], osg::Vec3f(0,1,0)); - osg::Quat zr (-reference.mPos.rot[2], osg::Vec3f(0,0,1)); + osg::Quat xr (-position.rot[0], osg::Vec3f(1,0,0)); + osg::Quat yr (-position.rot[1], osg::Vec3f(0,1,0)); + osg::Quat zr (-position.rot[2], osg::Vec3f(0,0,1)); mBaseNode->setAttitude(zr*yr*xr); - mBaseNode->setScale(osg::Vec3(reference.mScale, reference.mScale, reference.mScale)); + float scale = getScale(); + + mBaseNode->setScale(osg::Vec3(scale, scale, scale)); } const CSMWorld::CellRef& CSVRender::Object::getReference() const @@ -154,7 +158,8 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero) -: mData (data), mBaseNode(0), mSelected(false), mParentNode(parentNode), mResourceSystem(data.getResourceSystem().get()), mForceBaseToZero (forceBaseToZero) +: mData (data), mBaseNode(0), mSelected(false), mParentNode(parentNode), mResourceSystem(data.getResourceSystem().get()), mForceBaseToZero (forceBaseToZero), + mScaleOverride (1), mOverrideFlags (0) { mBaseNode = new osg::PositionAttitudeTransform; mBaseNode->addCullCallback(new SceneUtil::LightListCallback); @@ -290,3 +295,121 @@ osg::ref_ptr CSVRender::Object::getTag() const { return static_cast (mBaseNode->getUserData()); } + +bool CSVRender::Object::isEdited() const +{ + return mOverrideFlags; +} + +void CSVRender::Object::setEdited (int flags) +{ + bool discard = mOverrideFlags & ~flags; + int added = flags & ~mOverrideFlags; + + mOverrideFlags = flags; + + if (added & Override_Position) + for (int i=0; i<3; ++i) + mPositionOverride.pos[i] = getReference().mPos.pos[i]; + + if (added & Override_Rotation) + for (int i=0; i<3; ++i) + mPositionOverride.rot[i] = getReference().mPos.rot[i]; + + if (added & Override_Scale) + mScaleOverride = getReference().mScale; + + if (discard) + adjustTransform(); +} + +ESM::Position CSVRender::Object::getPosition() const +{ + ESM::Position position = getReference().mPos; + + if (mOverrideFlags & Override_Position) + for (int i=0; i<3; ++i) + position.pos[i] = mPositionOverride.pos[i]; + + if (mOverrideFlags & Override_Rotation) + for (int i=0; i<3; ++i) + position.rot[i] = mPositionOverride.rot[i]; + + return position; +} + +float CSVRender::Object::getScale() const +{ + return mOverrideFlags & Override_Scale ? mScaleOverride : getReference().mScale; +} + +void CSVRender::Object::setPosition (const float position[3]) +{ + mOverrideFlags |= Override_Position; + + for (int i=0; i<3; ++i) + mPositionOverride.pos[i] = position[i]; + + adjustTransform(); +} + +void CSVRender::Object::setRotation (const float rotation[3]) +{ + mOverrideFlags |= Override_Rotation; + + for (int i=0; i<3; ++i) + mPositionOverride.rot[i] = rotation[i]; + + adjustTransform(); +} + +void CSVRender::Object::setScale (float scale) +{ + mOverrideFlags |= Override_Scale; + + mScaleOverride = scale; + + adjustTransform(); +} + +void CSVRender::Object::apply (QUndoStack& undoStack) +{ + const CSMWorld::RefCollection& collection = mData.getReferences(); + QAbstractItemModel *model = mData.getTableModel (CSMWorld::UniversalId::Type_References); + + int recordIndex = collection.getIndex (mReferenceId); + + if (mOverrideFlags & Override_Position) + { + for (int i=0; i<3; ++i) + { + int column = collection.findColumnIndex (static_cast ( + CSMWorld::Columns::ColumnId_PositionXPos+i)); + + undoStack.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), mPositionOverride.pos[i])); + } + } + + if (mOverrideFlags & Override_Rotation) + { + for (int i=0; i<3; ++i) + { + int column = collection.findColumnIndex (static_cast ( + CSMWorld::Columns::ColumnId_PositionXRot+i)); + + undoStack.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), mPositionOverride.rot[i])); + } + } + + if (mOverrideFlags & Override_Scale) + { + int column = collection.findColumnIndex (CSMWorld::Columns::ColumnId_Scale); + + undoStack.push (new CSMWorld::ModifyCommand (*model, + model->index (recordIndex, column), mScaleOverride)); + } + + mOverrideFlags = 0; +} diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 4a89fe201..accdb93e0 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -8,9 +8,12 @@ #include #include +#include + #include "tagbase.hpp" class QModelIndex; +class QUndoStack; namespace osg { @@ -53,7 +56,18 @@ namespace CSVRender class Object { - const CSMWorld::Data& mData; + public: + + enum OverrideFlags + { + Override_Position = 1, + Override_Rotation = 2, + Override_Scale = 4 + }; + + private: + + CSMWorld::Data& mData; std::string mReferenceId; std::string mReferenceableId; osg::ref_ptr mBaseNode; @@ -62,6 +76,9 @@ namespace CSVRender osg::Group* mParentNode; Resource::ResourceSystem* mResourceSystem; bool mForceBaseToZero; + ESM::Position mPositionOverride; + int mScaleOverride; + int mOverrideFlags; /// Not implemented Object (const Object&); @@ -116,6 +133,27 @@ namespace CSVRender std::string getReferenceableId() const; osg::ref_ptr getTag() const; + + /// Is there currently an editing operation running on this object? + bool isEdited() const; + + void setEdited (int flags); + + ESM::Position getPosition() const; + + float getScale() const; + + /// Set override position. + void setPosition (const float position[3]); + + /// Set override rotation + void setRotation (const float rotation[3]); + + /// Set override scale + void setScale (float scale); + + /// Apply override changes via command and end edit mode + void apply (QUndoStack& undoStack); }; } diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index d71446d0b..cbba9ef5d 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -555,6 +555,23 @@ std::vector > CSVRender::PagedWorldspaceWidget: return result; } +std::vector > CSVRender::PagedWorldspaceWidget::getEdited ( + unsigned int elementMask) const +{ + std::vector > result; + + for (std::map::const_iterator iter = mCells.begin(); + iter!=mCells.end(); ++iter) + { + std::vector > cellResult = + iter->second->getEdited (elementMask); + + result.insert (result.end(), cellResult.begin(), cellResult.end()); + } + + return result; +} + 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 419a2a46a..baec96b03 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -112,6 +112,9 @@ namespace CSVRender virtual std::vector > getSelection (unsigned int elementMask) const; + virtual std::vector > getEdited (unsigned int elementMask) + const; + protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index ba31c9b8b..55d9c8195 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -120,6 +120,12 @@ bool RenderWidget::eventFilter(QObject* obj, QEvent* event) return QObject::eventFilter(obj, event); } +osg::Camera *RenderWidget::getCamera() +{ + return mView->getCamera(); +} + + // -------------------------------------------------- CompositeViewer::CompositeViewer() diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 63da01a45..07a7d7600 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -21,6 +21,7 @@ namespace Resource namespace osg { class Group; + class Camera; } namespace CSVWidget @@ -47,6 +48,8 @@ namespace CSVRender bool eventFilter(QObject *, QEvent *); + osg::Camera *getCamera(); + protected: osg::ref_ptr mView; diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 8d65c3694..4a7723d2f 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -131,6 +131,12 @@ std::vector > CSVRender::UnpagedWorldspaceWidge return mCell->getSelection (elementMask); } +std::vector > CSVRender::UnpagedWorldspaceWidget::getEdited ( + unsigned int elementMask) const +{ + return mCell->getEdited (elementMask); +} + 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 a4c517948..f7b583f3d 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -60,6 +60,9 @@ namespace CSVRender virtual std::vector > getSelection (unsigned int elementMask) const; + virtual std::vector > getEdited (unsigned int elementMask) + const; + private: virtual void referenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index ac6426b42..67f585e03 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -152,6 +152,9 @@ namespace CSVRender virtual std::vector > getSelection (unsigned int elementMask) const = 0; + virtual std::vector > getEdited (unsigned int elementMask) + const = 0; + protected: /// Visual elements in a scene