From 388917df891fe164035c6b30f74b272630f38def Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Wed, 10 Aug 2016 19:31:34 -0400 Subject: [PATCH 1/5] Base editor rotate/scale commit. --- apps/opencs/view/render/instancemode.cpp | 215 +++++++++++++++++++---- apps/opencs/view/render/instancemode.hpp | 11 +- apps/opencs/view/render/object.cpp | 133 ++++++++++++-- apps/opencs/view/render/object.hpp | 10 +- 4 files changed, 314 insertions(+), 55 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4eb9ea388..80687359e 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -27,6 +27,38 @@ int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const return id=="move" ? 0 : (id=="rotate" ? 1 : 2); } +osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const +{ + const float Pi = 3.14159265f; + + float x, y, z; + float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z()); + + if (std::abs(test) >= 1.f) + { + x = atan2(rot.x(), rot.w()); + y = (test > 0) ? (Pi / 2) : (-Pi / 2); + z = 0; + } + else + { + x = std::atan2(2 * (rot.w() * rot.x() - rot.y() * rot.z()), 1 - 2 * (rot.x() * rot.x() + rot.y() * rot.y())); + y = std::asin(test); + z = std::atan2(2 * (rot.w() * rot.z() - rot.x() * rot.y()), 1 - 2 * (rot.y() * rot.y() + rot.z() * rot.z())); + } + + return osg::Vec3f(-x, -y, -z); +} + +osg::Quat CSVRender::InstanceMode::eulerToQuat(const osg::Vec3f& euler) const +{ + osg::Quat xr = osg::Quat(-euler[0], osg::Vec3f(1,0,0)); + osg::Quat yr = osg::Quat(-euler[1], osg::Vec3f(0,1,0)); + osg::Quat zr = osg::Quat(-euler[2], osg::Vec3f(0,0,1)); + + return zr * yr * xr; +} + CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing", parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), @@ -44,14 +76,16 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) "Rotate selected instances" "" - "Not implemented yet"); + "Grid rotate not implemented yet"); mSubMode->addButton (":placeholder", "scale", "Scale selected instances" "" - "Not implemented yet"); + "Grid scale not implemented yet"); mSubMode->setButton (mSubModeId); @@ -151,6 +185,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) if (mDragMode!=DragMode_None || mLocked) return false; + WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { @@ -173,12 +208,33 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) { - objectTag->mObject->setEdited (Object::Override_Position); + if (mSubModeId == "move") + { + objectTag->mObject->setEdited (Object::Override_Position); + mDragMode = DragMode_Move; + } + else if (mSubModeId == "rotate") + { + objectTag->mObject->setEdited (Object::Override_Rotation); + mDragMode = DragMode_Rotate; + } + else if (mSubModeId == "scale") + { + objectTag->mObject->setEdited (Object::Override_Scale); + mDragMode = DragMode_Scale; + + // Calculate scale + int widgetWidth = getWorldspaceWidget().width(); + int widgetHeight = getWorldspaceWidget().height(); + + int x = pos.x() - widgetWidth / 2; + int y = pos.y() - widgetHeight / 2; + + mUnitScaleDist = std::sqrt(x * x + y * y); + } } } - // \todo check for sub-mode - if (CSVRender::ObjectMarkerTag *objectTag = dynamic_cast (hit.tag.get())) { mDragAxis = objectTag->mAxis; @@ -186,8 +242,6 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) else mDragAxis = -1; - mDragMode = DragMode_Move; - return true; } @@ -201,48 +255,139 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag (const QPoint& pos) void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) { - osg::Vec3f eye; - osg::Vec3f centre; - osg::Vec3f up; + osg::Vec3f offset; + osg::Quat rotation; - getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); + if (mDragMode == DragMode_Move) + { + osg::Vec3f eye, centre, 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; + } - if (diffY) - offset += up * diffY * speedFactor; + if (mDragAxis!=-1) + { + for (int i=0; i<3; ++i) + { + if (i!=mDragAxis) + offset[i] = 0; + } + } + } + else if (mDragMode == DragMode_Rotate) + { + osg::Vec3f eye, centre, up; + getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); - if (diffX) - offset += ((centre-eye) ^ up) * diffX * speedFactor; + osg::Vec3f camBack = eye - centre; - switch (mDragMode) + // Convert coordinate system + int widgetWidth = getWorldspaceWidget().width(); + int widgetHeight = getWorldspaceWidget().height(); + + int newX = pos.x() - widgetWidth / 2; + int newY = -pos.y() + widgetHeight / 2; + + int oldX = newX - diffX; + int oldY = newY - diffY; // diffY appears to already be flipped + + osg::Vec3f oldVec = osg::Vec3f(oldX, oldY, 0); + oldVec.normalize(); + + osg::Vec3f newVec = osg::Vec3f(newX, newY, 0); + newVec.normalize(); + + // Find angle and axis of rotation + float angle = std::acos(oldVec * newVec) * speedFactor; + if (((oldVec ^ newVec) * camBack < 0) ^ (camBack.z() < 0)) + angle *= -1; + + osg::Vec3f axis; + if (mDragAxis != -1) + { + for (int i = 0; i < 3; ++i) + { + if (i == mDragAxis) + axis[i] = 1; + else + axis[i] = 0; + } + + if (camBack * axis < 0) + axis *= -1; + } + else + axis = camBack; + + rotation = osg::Quat(angle, axis); + } + else if (mDragMode == DragMode_Scale) { - case DragMode_Move: + int widgetWidth = getWorldspaceWidget().width(); + int widgetHeight = getWorldspaceWidget().height(); + + int x = pos.x() - widgetWidth / 2; + int y = pos.y() - widgetHeight / 2; + + float dist = std::sqrt(x * x + y * y); + float scale = dist / mUnitScaleDist; + + // Only uniform scaling is currently supported + offset = osg::Vec3f(scale, scale, scale); + } + + 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())) { - if (mDragAxis!=-1) + if (mDragMode == DragMode_Move) + { + ESM::Position position = objectTag->mObject->getPosition(); for (int i=0; i<3; ++i) - if (i!=mDragAxis) - offset[i] = 0; - - std::vector > selection = - getWorldspaceWidget().getEdited (Mask_Reference); + { + position.pos[i] += offset[i]; + } - for (std::vector >::iterator iter (selection.begin()); - iter!=selection.end(); ++iter) + objectTag->mObject->setPosition(position.pos); + } + else if (mDragMode == DragMode_Rotate) { - if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + ESM::Position position = objectTag->mObject->getPosition(); + + osg::Quat currentRot = eulerToQuat(osg::Vec3f(position.rot[0], position.rot[1], position.rot[2])); + osg::Quat combined = currentRot * rotation; + + osg::Vec3f euler = quatToEuler(combined); + // There appears to be a very rare rounding error that can cause asin to return NaN + if (!euler.isNaN()) { - ESM::Position position = objectTag->mObject->getPosition(); - for (int i=0; i<3; ++i) - position.pos[i] += offset[i]; - objectTag->mObject->setPosition (position.pos); + position.rot[0] = euler.x(); + position.rot[1] = euler.y(); + position.rot[2] = euler.z(); } + + objectTag->mObject->setRotation(position.rot); } + else if (mDragMode == DragMode_Scale) + { + // Reset scale + objectTag->mObject->setEdited(0); + objectTag->mObject->setEdited(Object::Override_Scale); - break; - } + float scale = objectTag->mObject->getScale(); + scale *= offset.x(); - case DragMode_None: break; + objectTag->mObject->setScale (scale); + } + } } } @@ -258,6 +403,8 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) switch (mDragMode) { case DragMode_Move: description = "Move Instances"; break; + case DragMode_Rotate: description = "Rotate Instances"; break; + case DragMode_Scale: description = "Scale Instances"; break; case DragMode_None: break; } diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 4f7937b51..dd2755237 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -1,6 +1,9 @@ #ifndef CSV_RENDER_INSTANCEMODE_H #define CSV_RENDER_INSTANCEMODE_H +#include +#include + #include "editmode.hpp" namespace CSVWidget @@ -19,7 +22,9 @@ namespace CSVRender enum DragMode { DragMode_None, - DragMode_Move + DragMode_Move, + DragMode_Rotate, + DragMode_Scale }; CSVWidget::SceneToolMode *mSubMode; @@ -28,9 +33,13 @@ namespace CSVRender DragMode mDragMode; int mDragAxis; bool mLocked; + float mUnitScaleDist; int getSubModeFromId (const std::string& id) const; + osg::Vec3f quatToEuler(const osg::Quat& quat) const; + osg::Quat eulerToQuat(const osg::Vec3f& euler) const; + public: InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0); diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index cc151ead9..3a6640c54 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -29,6 +29,13 @@ #include "mask.hpp" + +const float CSVRender::Object::MarkerShaftWidth = 30; +const float CSVRender::Object::MarkerShaftBaseLength = 70; +const float CSVRender::Object::MarkerHeadWidth = 50; +const float CSVRender::Object::MarkerHeadLength = 50; + + namespace { @@ -179,7 +186,21 @@ void CSVRender::Object::updateMarker() { if (mSubMode==0) { - mMarker[i] = makeMarker (i); + mMarker[i] = makeMoveOrScaleMarker (i); + mMarker[i]->setUserData(new ObjectMarkerTag (this, i)); + + mRootNode->addChild (mMarker[i]); + } + else if (mSubMode==1) + { + mMarker[i] = makeRotateMarker (i); + mMarker[i]->setUserData(new ObjectMarkerTag (this, i)); + + mRootNode->addChild (mMarker[i]); + } + else if (mSubMode==2) + { + mMarker[i] = makeMoveOrScaleMarker (i); mMarker[i]->setUserData(new ObjectMarkerTag (this, i)); mRootNode->addChild (mMarker[i]); @@ -188,16 +209,11 @@ void CSVRender::Object::updateMarker() } } -osg::ref_ptr CSVRender::Object::makeMarker (int axis) +osg::ref_ptr CSVRender::Object::makeMoveOrScaleMarker (int axis) { osg::ref_ptr geometry (new osg::Geometry); - const float shaftWidth = 10; - const float shaftBaseLength = 50; - const float headWidth = 30; - const float headLength = 30; - - float shaftLength = shaftBaseLength + mBaseNode->getBound().radius(); + float shaftLength = MarkerShaftBaseLength + mBaseNode->getBound().radius(); // shaft osg::Vec3Array *vertices = new osg::Vec3Array; @@ -206,20 +222,20 @@ osg::ref_ptr CSVRender::Object::makeMarker (int axis) { float length = i ? shaftLength : 0; - vertices->push_back (getMarkerPosition (-shaftWidth/2, -shaftWidth/2, length, axis)); - vertices->push_back (getMarkerPosition (-shaftWidth/2, shaftWidth/2, length, axis)); - vertices->push_back (getMarkerPosition (shaftWidth/2, shaftWidth/2, length, axis)); - vertices->push_back (getMarkerPosition (shaftWidth/2, -shaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (-MarkerShaftWidth/2, -MarkerShaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (-MarkerShaftWidth/2, MarkerShaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (MarkerShaftWidth/2, MarkerShaftWidth/2, length, axis)); + vertices->push_back (getMarkerPosition (MarkerShaftWidth/2, -MarkerShaftWidth/2, length, axis)); } // head backside - vertices->push_back (getMarkerPosition (-headWidth/2, -headWidth/2, shaftLength, axis)); - vertices->push_back (getMarkerPosition (-headWidth/2, headWidth/2, shaftLength, axis)); - vertices->push_back (getMarkerPosition (headWidth/2, headWidth/2, shaftLength, axis)); - vertices->push_back (getMarkerPosition (headWidth/2, -headWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (-MarkerHeadWidth/2, -MarkerHeadWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (-MarkerHeadWidth/2, MarkerHeadWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (MarkerHeadWidth/2, MarkerHeadWidth/2, shaftLength, axis)); + vertices->push_back (getMarkerPosition (MarkerHeadWidth/2, -MarkerHeadWidth/2, shaftLength, axis)); // head - vertices->push_back (getMarkerPosition (0, 0, shaftLength+headLength, axis)); + vertices->push_back (getMarkerPosition (0, 0, shaftLength+MarkerHeadLength, axis)); geometry->setVertexArray (vertices); @@ -285,6 +301,87 @@ osg::ref_ptr CSVRender::Object::makeMarker (int axis) return geode; } +osg::ref_ptr CSVRender::Object::makeRotateMarker (int axis) +{ + const float Pi = 3.14159265f; + + const float InnerRadius = mBaseNode->getBound().radius(); + const float OuterRadius = InnerRadius + MarkerShaftWidth; + + const float SegmentDistance = 100.f; + const size_t SegmentCount = std::min(64, std::max(8, (int)(OuterRadius * 2 * Pi / SegmentDistance))); + const size_t VerticesPerSegment = 4; + const size_t IndicesPerSegment = 24; + + const size_t VertexCount = SegmentCount * VerticesPerSegment; + const size_t IndexCount = SegmentCount * IndicesPerSegment; + + const float Angle = 2 * Pi / SegmentCount; + + const unsigned short IndexPattern[IndicesPerSegment] = + { + 0, 4, 5, 0, 5, 1, + 2, 6, 4, 2, 4, 0, + 3, 7, 6, 3, 6, 2, + 1, 5, 7, 1, 7, 3 + }; + + + osg::ref_ptr geometry = new osg::Geometry(); + + osg::ref_ptr vertices = new osg::Vec3Array(VertexCount); + osg::ref_ptr colors = new osg::Vec4Array(1); + osg::ref_ptr primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, + IndexCount); + + for (size_t i = 0; i < SegmentCount; ++i) + { + size_t index = i * VerticesPerSegment; + + float innerX = InnerRadius * std::cos(i * Angle); + float innerY = InnerRadius * std::sin(i * Angle); + + float outerX = OuterRadius * std::cos(i * Angle); + float outerY = OuterRadius * std::sin(i * Angle); + + vertices->at(index++) = getMarkerPosition(innerX, innerY, MarkerShaftWidth / 2, axis); + vertices->at(index++) = getMarkerPosition(innerX, innerY, -MarkerShaftWidth / 2, axis); + vertices->at(index++) = getMarkerPosition(outerX, outerY, MarkerShaftWidth / 2, axis); + vertices->at(index++) = getMarkerPosition(outerX, outerY, -MarkerShaftWidth / 2, axis); + } + + colors->at(0) = osg::Vec4f (axis==0 ? 1.0f : 0.2f, axis==1 ? 1.0f : 0.2f, axis==2 ? 1.0f : 0.2f, 1.0f); + + for (size_t i = 0; i < SegmentCount; ++i) + { + size_t indices[IndicesPerSegment]; + for (size_t j = 0; j < IndicesPerSegment; ++j) + { + indices[j] = i * VerticesPerSegment + j; + + if (indices[j] >= VertexCount) + indices[j] -= VertexCount; + } + + size_t offset = i * IndicesPerSegment; + for (size_t j = 0; j < IndicesPerSegment; ++j) + { + primitives->setElement(offset++, indices[IndexPattern[j]]); + } + } + + geometry->setVertexArray(vertices); + geometry->setColorArray(colors, osg::Array::BIND_OVERALL); + geometry->addPrimitiveSet(primitives); + + geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF); + + osg::ref_ptr geode = new osg::Geode(); + geode->addDrawable (geometry); + + return geode; +} + osg::Vec3f CSVRender::Object::getMarkerPosition (float x, float y, float z, int axis) { switch (axis) @@ -494,7 +591,7 @@ ESM::Position CSVRender::Object::getPosition() const float CSVRender::Object::getScale() const { - return mOverrideFlags & Override_Scale ? mScaleOverride : getReference().mScale; + return (mOverrideFlags & Override_Scale) ? mScaleOverride : getReference().mScale; } void CSVRender::Object::setPosition (const float position[3]) diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 54a284fa3..d3cba6c55 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -78,6 +78,11 @@ namespace CSVRender private: + static const float MarkerShaftWidth; + static const float MarkerShaftBaseLength; + static const float MarkerHeadWidth; + static const float MarkerHeadLength; + CSMWorld::Data& mData; std::string mReferenceId; std::string mReferenceableId; @@ -89,7 +94,7 @@ namespace CSVRender Resource::ResourceSystem* mResourceSystem; bool mForceBaseToZero; ESM::Position mPositionOverride; - int mScaleOverride; + float mScaleOverride; int mOverrideFlags; osg::ref_ptr mMarker[3]; int mSubMode; @@ -115,7 +120,8 @@ namespace CSVRender void updateMarker(); - osg::ref_ptr makeMarker (int axis); + osg::ref_ptr makeMoveOrScaleMarker (int axis); + osg::ref_ptr makeRotateMarker (int axis); osg::Vec3f getMarkerPosition (float x, float y, float z, int axis); From 571969cd6534772ab22daf3bcf87a2ac7ecb51c4 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Fri, 12 Aug 2016 17:11:37 -0400 Subject: [PATCH 2/5] Scale from center of selection. --- apps/opencs/view/render/instancemode.cpp | 56 +++++++++++++++++++----- apps/opencs/view/render/instancemode.hpp | 5 +++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 80687359e..5b5edef9e 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -59,6 +59,36 @@ osg::Quat CSVRender::InstanceMode::eulerToQuat(const osg::Vec3f& euler) const return zr * yr * xr; } +osg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector >& selection) const +{ + osg::Vec3f center = osg::Vec3f(0, 0, 0); + int objectCount = 0; + + for (std::vector >::const_iterator iter (selection.begin()); iter!=selection.end(); ++iter) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) + { + const ESM::Position& position = objectTag->mObject->getPosition(); + center += osg::Vec3f(position.pos[0], position.pos[1], position.pos[2]); + + ++objectCount; + } + } + center /= objectCount; + + return center; +} + +osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos) +{ + osg::Matrix viewMatrix = getWorldspaceWidget().getCamera()->getViewMatrix(); + osg::Matrix projMatrix = getWorldspaceWidget().getCamera()->getProjectionMatrix(); + osg::Matrix windowMatrix = getWorldspaceWidget().getCamera()->getViewport()->computeWindowMatrix(); + osg::Matrix combined = viewMatrix * projMatrix * windowMatrix; + + return pos * combined; +} + CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing", parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), @@ -223,14 +253,16 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) objectTag->mObject->setEdited (Object::Override_Scale); mDragMode = DragMode_Scale; - // Calculate scale - int widgetWidth = getWorldspaceWidget().width(); + // Calculate scale factor + std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); + osg::Vec3f center = getScreenCoords(getSelectionCenter(selection)); + int widgetHeight = getWorldspaceWidget().height(); - int x = pos.x() - widgetWidth / 2; - int y = pos.y() - widgetHeight / 2; + float dx = pos.x() - center.x(); + float dy = (widgetHeight - pos.y()) - center.y(); - mUnitScaleDist = std::sqrt(x * x + y * y); + mUnitScaleDist = std::sqrt(dx * dx + dy * dy); } } } @@ -258,6 +290,8 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou osg::Vec3f offset; osg::Quat rotation; + std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); + if (mDragMode == DragMode_Move) { osg::Vec3f eye, centre, up; @@ -330,20 +364,22 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou } else if (mDragMode == DragMode_Scale) { - int widgetWidth = getWorldspaceWidget().width(); + osg::Vec3f center = getScreenCoords(getSelectionCenter(selection)); + + // Calculate scaling distance/rate int widgetHeight = getWorldspaceWidget().height(); - int x = pos.x() - widgetWidth / 2; - int y = pos.y() - widgetHeight / 2; + float dx = pos.x() - center.x(); + float dy = (widgetHeight - pos.y()) - center.y(); - float dist = std::sqrt(x * x + y * y); + float dist = std::sqrt(dx * dx + dy * dy); float scale = dist / mUnitScaleDist; // Only uniform scaling is currently supported offset = osg::Vec3f(scale, scale, scale); } - std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); + // Apply for (std::vector >::iterator iter (selection.begin()); iter!=selection.end(); ++iter) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (iter->get())) diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index dd2755237..933cae529 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -1,6 +1,7 @@ #ifndef CSV_RENDER_INSTANCEMODE_H #define CSV_RENDER_INSTANCEMODE_H +#include #include #include @@ -13,6 +14,7 @@ namespace CSVWidget namespace CSVRender { + class TagBase; class InstanceSelectionMode; class InstanceMode : public EditMode @@ -40,6 +42,9 @@ namespace CSVRender osg::Vec3f quatToEuler(const osg::Quat& quat) const; osg::Quat eulerToQuat(const osg::Vec3f& euler) const; + osg::Vec3f getSelectionCenter(const std::vector >& selection) const; + osg::Vec3f getScreenCoords(const osg::Vec3f& pos); + public: InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0); From 101c3cc90d375a1ac20db52a8325ff356c93206a Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Fri, 12 Aug 2016 17:12:10 -0400 Subject: [PATCH 3/5] Rotate around center of selection. --- apps/opencs/view/render/instancemode.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 5b5edef9e..feca6fa6a 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -323,14 +323,15 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou osg::Vec3f camBack = eye - centre; // Convert coordinate system - int widgetWidth = getWorldspaceWidget().width(); + osg::Vec3f screenCenter = getScreenCoords(getSelectionCenter(selection)); + int widgetHeight = getWorldspaceWidget().height(); - int newX = pos.x() - widgetWidth / 2; - int newY = -pos.y() + widgetHeight / 2; + float newX = pos.x() - screenCenter.x(); + float newY = (widgetHeight - pos.y()) - screenCenter.y(); - int oldX = newX - diffX; - int oldY = newY - diffY; // diffY appears to already be flipped + float oldX = newX - diffX; + float oldY = newY - diffY; // diffY appears to already be flipped osg::Vec3f oldVec = osg::Vec3f(oldX, oldY, 0); oldVec.normalize(); From 3e4ac0c6625a86ca1b3d0b5e98ebdda8f191ca02 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Sun, 14 Aug 2016 14:43:29 -0400 Subject: [PATCH 4/5] Potential fix for context sensitive select mode issues. --- apps/opencs/view/render/instancemode.cpp | 27 +++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index feca6fa6a..aa6408fef 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -215,23 +215,26 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) if (mDragMode!=DragMode_None || mLocked) return false; - WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); - if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) + + std::vector > selection = getWorldspaceWidget().getSelection (Mask_Reference); + if (selection.empty()) { - getWorldspaceWidget().clearSelection (Mask_Reference); - if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) + // 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()) { - CSVRender::Object* object = objectTag->mObject; - object->setSelected (true); + getWorldspaceWidget().clearSelection (Mask_Reference); + if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) + { + CSVRender::Object* object = objectTag->mObject; + object->setSelected (true); + } } - } - - std::vector > selection = - getWorldspaceWidget().getSelection (Mask_Reference); - if (selection.empty()) - return false; + selection = getWorldspaceWidget().getSelection (Mask_Reference); + if (selection.empty()) + return false; + } for (std::vector >::iterator iter (selection.begin()); iter!=selection.end(); ++iter) From 712577564868e0ad0b637fc6769b447a48856890 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Mon, 15 Aug 2016 15:07:43 -0400 Subject: [PATCH 5/5] Change free axis rotation. --- apps/opencs/model/prefs/state.cpp | 1 + apps/opencs/view/render/instancemode.cpp | 64 +++++++++++++++--------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 718d9a107..b8d6102ac 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -188,6 +188,7 @@ void CSMPrefs::State::declare() "Shift-acceleration factor during drag operations", 4.0). setTooltip ("Acceleration factor during drag operations while holding down shift"). setRange (0.001, 100.0); + declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1); declareCategory ("Tooltips"); declareBool ("scene", "Show Tooltips in 3D scenes", true); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index aa6408fef..4932e6ca9 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -323,33 +323,28 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou osg::Vec3f eye, centre, up; getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up); - osg::Vec3f camBack = eye - centre; - - // Convert coordinate system - osg::Vec3f screenCenter = getScreenCoords(getSelectionCenter(selection)); - - int widgetHeight = getWorldspaceWidget().height(); - - float newX = pos.x() - screenCenter.x(); - float newY = (widgetHeight - pos.y()) - screenCenter.y(); - - float oldX = newX - diffX; - float oldY = newY - diffY; // diffY appears to already be flipped + float angle; + osg::Vec3f axis; - osg::Vec3f oldVec = osg::Vec3f(oldX, oldY, 0); - oldVec.normalize(); + if (mDragAxis == -1) + { + // Free rotate + float rotationFactor = CSMPrefs::get()["3D Scene Input"]["rotate-factor"].toDouble() * speedFactor; - osg::Vec3f newVec = osg::Vec3f(newX, newY, 0); - newVec.normalize(); + osg::Quat cameraRotation = getWorldspaceWidget().getCamera()->getInverseViewMatrix().getRotate(); - // Find angle and axis of rotation - float angle = std::acos(oldVec * newVec) * speedFactor; - if (((oldVec ^ newVec) * camBack < 0) ^ (camBack.z() < 0)) - angle *= -1; + osg::Vec3f camForward = centre - eye; + osg::Vec3f screenDir = cameraRotation * osg::Vec3f(diffX, diffY, 0); + screenDir.normalize(); - osg::Vec3f axis; - if (mDragAxis != -1) + angle = std::sqrt(diffX*diffX + diffY*diffY) * rotationFactor; + axis = screenDir ^ camForward; + } + else { + // Global axis rotation + osg::Vec3f camBack = eye - centre; + for (int i = 0; i < 3; ++i) { if (i == mDragAxis) @@ -358,11 +353,32 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou axis[i] = 0; } + // Flip axis if facing opposite side if (camBack * axis < 0) axis *= -1; + + // Convert coordinate system + osg::Vec3f screenCenter = getScreenCoords(getSelectionCenter(selection)); + + int widgetHeight = getWorldspaceWidget().height(); + + float newX = pos.x() - screenCenter.x(); + float newY = (widgetHeight - pos.y()) - screenCenter.y(); + + float oldX = newX - diffX; + float oldY = newY - diffY; // diffY appears to already be flipped + + osg::Vec3f oldVec = osg::Vec3f(oldX, oldY, 0); + oldVec.normalize(); + + osg::Vec3f newVec = osg::Vec3f(newX, newY, 0); + newVec.normalize(); + + // Find angle and axis of rotation + angle = std::acos(oldVec * newVec) * speedFactor; + if (((oldVec ^ newVec) * camBack < 0) ^ (camBack.z() < 0)) + angle *= -1; } - else - axis = camBack; rotation = osg::Quat(angle, axis); }