From 6d7c8f25fc05eb6dd1c9f7073205f7db06d318b3 Mon Sep 17 00:00:00 2001 From: Vidi_Aquam <89811652+VidiAquam@users.noreply.github.com> Date: Sat, 2 Apr 2022 19:25:57 -0500 Subject: [PATCH 1/4] Implement grid snapping and angle snapping in OpenMW-CS Adds configurable snap settings for instance movement, rotation and scaling, used with the secondary edit button --- apps/opencs/model/prefs/state.cpp | 3 + apps/opencs/view/render/instancedragmodes.hpp | 5 +- apps/opencs/view/render/instancemode.cpp | 123 ++++++++++++++++-- apps/opencs/view/render/instancemode.hpp | 2 + apps/opencs/view/render/instancemovemode.cpp | 3 +- 5 files changed, 119 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 0981ae4401..97039df2eb 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -243,6 +243,9 @@ void CSMPrefs::State::declare() secondarySelectAction.add (SelectOnly).add (SelectAdd).add (SelectRemove).add (selectInvert); declareCategory ("3D Scene Editing"); + declareDouble("gridsnap-movement", "Grid snap size", 16); + declareDouble("gridsnap-rotation", "Angle snap size", 15); + declareDouble("gridsnap-scale", "Scale snap size", 0.25); 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"); diff --git a/apps/opencs/view/render/instancedragmodes.hpp b/apps/opencs/view/render/instancedragmodes.hpp index 01547545ae..2629a9d2f9 100644 --- a/apps/opencs/view/render/instancedragmodes.hpp +++ b/apps/opencs/view/render/instancedragmodes.hpp @@ -12,7 +12,10 @@ namespace CSVRender DragMode_Select_Only, DragMode_Select_Add, DragMode_Select_Remove, - DragMode_Select_Invert + DragMode_Select_Invert, + DragMode_Move_Snap, + DragMode_Rotate_Snap, + DragMode_Scale_Snap }; } #endif diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index ebb7f46fa5..f19d1051b1 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -64,6 +64,11 @@ osg::Quat CSVRender::InstanceMode::eulerToQuat(const osg::Vec3f& euler) const return zr * yr * xr; } +float CSVRender::InstanceMode::roundFloatToMult(const float val, const double mult) const +{ + return round(val / mult) * mult; +} + osg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector >& selection) const { osg::Vec3f center = osg::Vec3f(0, 0, 0); @@ -156,15 +161,13 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) "" - "Grid rotate not implemented yet"); + ""); mSubMode->addButton (":scenetoolbar/transform-scale", "scale", "Scale selected instances" "" - "Grid scale not implemented yet"); + ""); mSubMode->setButton (mSubModeId); @@ -351,10 +354,81 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) bool CSVRender::InstanceMode::secondaryEditStartDrag (const QPoint& pos) { - if (mLocked) + if (mDragMode != DragMode_None || mLocked) return false; - return false; + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask()); + + std::vector > selection = getWorldspaceWidget().getSelection(Mask_Reference); + if (selection.empty()) + { + // Only change selection at the start of drag if no object is already selected + if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) + { + getWorldspaceWidget().clearSelection(Mask_Reference); + if (CSVRender::ObjectTag* objectTag = dynamic_cast (hit.tag.get())) + { + CSVRender::Object* object = objectTag->mObject; + object->setSelected(true); + } + } + + selection = getWorldspaceWidget().getSelection(Mask_Reference); + if (selection.empty()) + return false; + } + + mObjectsAtDragStart.clear(); + + for (std::vector >::iterator iter(selection.begin()); + iter != selection.end(); ++iter) + { + if (CSVRender::ObjectTag* objectTag = dynamic_cast (iter->get())) + { + if (mSubModeId == "move") + { + objectTag->mObject->setEdited(Object::Override_Position); + float x = objectTag->mObject->getPosition().pos[0]; + float y = objectTag->mObject->getPosition().pos[1]; + float z = objectTag->mObject->getPosition().pos[2]; + osg::Vec3f thisPoint(x, y, z); + + mDragStart = getMousePlaneCoords(pos, getProjectionSpaceCoords(thisPoint)); + mObjectsAtDragStart.emplace_back(thisPoint); + mDragMode = DragMode_Move_Snap; + } + else if (mSubModeId == "rotate") + { + objectTag->mObject->setEdited(Object::Override_Rotation); + mDragMode = DragMode_Rotate_Snap; + } + else if (mSubModeId == "scale") + { + objectTag->mObject->setEdited(Object::Override_Scale); + mDragMode = DragMode_Scale_Snap; + + // Calculate scale factor + std::vector > editedSelection = getWorldspaceWidget().getEdited(Mask_Reference); + osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection)); + + int widgetHeight = getWorldspaceWidget().height(); + + float dx = pos.x() - center.x(); + float dy = (widgetHeight - pos.y()) - center.y(); + + mUnitScaleDist = std::sqrt(dx * dx + dy * dy); + } + } + } + + if (CSVRender::ObjectMarkerTag* objectTag = dynamic_cast (hit.tag.get())) + { + mDragAxis = objectTag->mAxis; + } + else + mDragAxis = -1; + + return true; } bool CSVRender::InstanceMode::primarySelectStartDrag (const QPoint& pos) @@ -400,8 +474,8 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); - if (mDragMode == DragMode_Move) {} - else if (mDragMode == DragMode_Rotate) + if (mDragMode == DragMode_Move || mDragMode == DragMode_Move_Snap) {} + else if (mDragMode == DragMode_Rotate || mDragMode == DragMode_Rotate_Snap) { osg::Vec3f eye, centre, up; getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); @@ -465,7 +539,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou rotation = osg::Quat(angle, axis); } - else if (mDragMode == DragMode_Scale) + else if (mDragMode == DragMode_Scale || mDragMode == DragMode_Scale_Snap) { osg::Vec3f center = getScreenCoords(getSelectionCenter(selection)); @@ -507,7 +581,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou { if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) { - if (mDragMode == DragMode_Move) + if (mDragMode == DragMode_Move || mDragMode == DragMode_Move_Snap) { ESM::Position position = objectTag->mObject->getPosition(); osg::Vec3f mousePos = getMousePlaneCoords(pos, getProjectionSpaceCoords(mDragStart)); @@ -518,6 +592,13 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou position.pos[1] = mObjectsAtDragStart[i].y() + addToY; position.pos[2] = mObjectsAtDragStart[i].z() + addToZ; + if (mDragMode == DragMode_Move_Snap) + { + position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble()); + position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble()); + position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble()); + } + // XYZ-locking if (mDragAxis != -1) { @@ -530,7 +611,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou objectTag->mObject->setPosition(position.pos); } - else if (mDragMode == DragMode_Rotate) + else if (mDragMode == DragMode_Rotate || mDragMode == DragMode_Rotate_Snap) { ESM::Position position = objectTag->mObject->getPosition(); @@ -546,9 +627,16 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou position.rot[2] = euler.z(); } + if (mDragMode == DragMode_Rotate_Snap) + { + position.rot[0] = CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble())); + position.rot[1] = CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble())); + position.rot[2] = CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble())); + } + objectTag->mObject->setRotation(position.rot); } - else if (mDragMode == DragMode_Scale) + else if (mDragMode == DragMode_Scale || mDragMode == DragMode_Scale_Snap) { // Reset scale objectTag->mObject->setEdited(0); @@ -557,6 +645,11 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou float scale = objectTag->mObject->getScale(); scale *= offset.x(); + if (mDragMode == DragMode_Scale_Snap) + { + scale = CSVRender::InstanceMode::roundFloatToMult(scale, CSMPrefs::get()["3D Scene Editing"]["gridsnap-scale"].toDouble()); + } + objectTag->mObject->setScale (scale); } } @@ -593,7 +686,9 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) handleSelectDrag(pos); return; break; - + case DragMode_Move_Snap: description = "Move Instances"; break; + case DragMode_Rotate_Snap: description = "Rotate Instances"; break; + case DragMode_Scale_Snap: description = "Scale Instances"; break; case DragMode_None: break; } @@ -621,7 +716,7 @@ void CSVRender::InstanceMode::dragAborted() void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor) { - if (mDragMode==DragMode_Move) + if (mDragMode==DragMode_Move || mDragMode==DragMode_Move_Snap) { osg::Vec3f eye; osg::Vec3f centre; diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 4ece934e93..feb307807a 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -53,6 +53,8 @@ namespace CSVRender osg::Vec3f quatToEuler(const osg::Quat& quat) const; osg::Quat eulerToQuat(const osg::Vec3f& euler) const; + float roundFloatToMult(const float val, const double mult) const; + osg::Vec3f getSelectionCenter(const std::vector >& selection) const; osg::Vec3f getScreenCoords(const osg::Vec3f& pos); osg::Vec3f getProjectionSpaceCoords(const osg::Vec3f& pos); diff --git a/apps/opencs/view/render/instancemovemode.cpp b/apps/opencs/view/render/instancemovemode.cpp index 723af811de..5591035492 100644 --- a/apps/opencs/view/render/instancemovemode.cpp +++ b/apps/opencs/view/render/instancemovemode.cpp @@ -6,7 +6,6 @@ CSVRender::InstanceMoveMode::InstanceMoveMode (QWidget *parent) "Move selected instances" "
  • Use {scene-edit-primary} to move instances around freely
  • " "
  • Use {scene-edit-secondary} to move instances around within the grid
  • " - "
" - "Grid move not implemented yet", + "", parent) {} From abf0c8048ed3b9b9655c7b872d49b28aba8bc308 Mon Sep 17 00:00:00 2001 From: Vidi_Aquam <89811652+VidiAquam@users.noreply.github.com> Date: Sun, 3 Apr 2022 09:04:12 -0500 Subject: [PATCH 2/4] Change reading of settings and add missing logic to mouse wheel drag --- apps/opencs/view/render/instancemode.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index f19d1051b1..e9a375f10d 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -594,9 +594,10 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou if (mDragMode == DragMode_Move_Snap) { - position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble()); - position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble()); - position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble()); + double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble(); + position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], snap); + position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], snap); + position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], snap); } // XYZ-locking @@ -629,9 +630,10 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou if (mDragMode == DragMode_Rotate_Snap) { - position.rot[0] = CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble())); - position.rot[1] = CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble())); - position.rot[2] = CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble())); + double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble(); + position.rot[0] = CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(snap)); + position.rot[1] = CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(snap)); + position.rot[2] = CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(snap)); } objectTag->mObject->setRotation(position.rot); @@ -741,6 +743,15 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor) ESM::Position position = objectTag->mObject->getPosition(); for (int i=0; i<3; ++i) position.pos[i] += offset[i]; + + if (mDragMode == DragMode_Move_Snap) + { + double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble(); + position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], snap); + position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], snap); + position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], snap); + } + objectTag->mObject->setPosition (position.pos); osg::Vec3f thisPoint(position.pos[0], position.pos[1], position.pos[2]); mDragStart = getMousePlaneCoords(getWorldspaceWidget().mapFromGlobal(QCursor::pos()), getProjectionSpaceCoords(thisPoint)); From 035fe778b2974987ca342ed1b4ccccf173d08cff Mon Sep 17 00:00:00 2001 From: Vidi_Aquam <89811652+VidiAquam@users.noreply.github.com> Date: Mon, 11 Apr 2022 08:12:38 -0500 Subject: [PATCH 3/4] Temporary workaround for angle snapping Made the angle snap only apply to an object when the drag is finished, which is much more usable until the rotation system can be fixed completely --- apps/opencs/view/render/instancemode.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index e9a375f10d..a84b26ca51 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -628,14 +628,6 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou position.rot[2] = euler.z(); } - if (mDragMode == DragMode_Rotate_Snap) - { - double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble(); - position.rot[0] = CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(snap)); - position.rot[1] = CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(snap)); - position.rot[2] = CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(snap)); - } - objectTag->mObject->setRotation(position.rot); } else if (mDragMode == DragMode_Scale || mDragMode == DragMode_Scale_Snap) @@ -702,6 +694,17 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) { + if (mDragMode == DragMode_Rotate_Snap) + { + ESM::Position position = objectTag->mObject->getPosition(); + double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble(); + position.rot[0] = CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(snap)); + position.rot[1] = CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(snap)); + position.rot[2] = CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(snap)); + + objectTag->mObject->setRotation(position.rot); + } + objectTag->mObject->apply (macro); } } From e2f393cbdb02287f00bc663b30e1b3c732697882 Mon Sep 17 00:00:00 2001 From: VidiAquam <11267882-VidiAquam@users.noreply.gitlab.com> Date: Mon, 11 Apr 2022 15:36:05 +0000 Subject: [PATCH 4/4] Update CHANGELOG.md, AUTHORS.md --- AUTHORS.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 84f465414e..b2050ef0bb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -226,6 +226,7 @@ Programmers unelsson uramer viadanna + Vidi_Aquam Vincent Heuken Vladimir Panteleev (CyberShadow) Wang Ryu (bzzt) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c665740ac..7401894d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,7 @@ Feature #2491: Ability to make OpenMW "portable" Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record Feature #2780: A way to see current OpenMW version in the console + Feature #3245: Grid and angle snapping for the OpenMW-CS Feature #3616: Allow Zoom levels on the World Map Feature #4297: Implement APPLIED_ONCE flag for magic effects Feature #4414: Handle duration of EXTRA SPELL magic effect