From a35a2d6bf3f9525c6ba3cead820c13046c0e70b5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 24 Jan 2020 17:39:50 +0200 Subject: [PATCH] QmlDesigner: Fix far away gizmo scaling Changed auto scaler math to use more double precision math to improve scaling accuracy when camera is zoomed far or gizmo is far from origin. Change-Id: Ic3020bbf159c0d5d090af9c9b8e8a4aa372d2406 Fixes: QDS-1528 Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- .../qmlpuppet/mockfiles/AutoScaleHelper.qml | 22 +--- .../qml2puppet/editor3d/generalhelper.cpp | 4 +- .../qml2puppet/editor3d/mousearea3d.cpp | 107 ++++++++++++++---- .../qml2puppet/editor3d/mousearea3d.h | 4 +- 4 files changed, 89 insertions(+), 48 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml index 9c1bf043dc2..16c3dd86804 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml @@ -64,27 +64,7 @@ Node { if (!autoScale) { target.scale = Qt.vector3d(1, 1, 1); } else { - // Calculate the distance independent scale by first mapping the targets position to - // the view. We then measure up a distance on the view (100px) that we use as an - // "anchor" distance. Map the two positions back to the target node, and measure the - // distance between them now, in the 3D scene. The difference of the two distances, - // view and scene, will tell us what the distance independent scale should be. - - // Need to recreate vector as we need to adjust it and we can't do that on reference of - // scenePosition, which is read-only property - var scenePos = Qt.vector3d(scenePosition.x, scenePosition.y, scenePosition.z); - if (orientation === Node.RightHanded) - scenePos.z = -scenePos.z; - - var posInView1 = view3D.mapFrom3DScene(scenePos); - var posInView2 = Qt.vector3d(posInView1.x + 100, posInView1.y, posInView1.z); - - var rayPos1 = view3D.mapTo3DScene(Qt.vector3d(posInView2.x, posInView2.y, 0)); - var rayPos2 = view3D.mapTo3DScene(Qt.vector3d(posInView2.x, posInView2.y, 10)); - - var planeNormal = camera.forward; - var rayHitPos = helper.rayIntersectsPlane(rayPos1, rayPos2, scenePos, planeNormal); - relativeScale = scenePos.minus(rayHitPos).length() / 100; + relativeScale = helper.getRelativeScale(overlayNode); } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index 7be9ef5fdde..7e95e02ec21 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -126,7 +126,7 @@ float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float de { // Emprically determined divisor for nice zoom float multiplier = 1.f + (distance / 40.f); - float newZoomFactor = relative ? qBound(.0001f, zoomFactor * multiplier, 10000.f) + float newZoomFactor = relative ? qBound(.01f, zoomFactor * multiplier, 100.f) : zoomFactor; if (qobject_cast(camera)) { @@ -199,7 +199,7 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau camera->setPosition(lookAt + newLookVector); - float newZoomFactor = updateZoom ? qBound(.0001f, float(maxExtent / 700.), 10000.f) : oldZoom; + float newZoomFactor = updateZoom ? qBound(.01f, float(maxExtent / 700.), 100.f) : oldZoom; float cameraZoomFactor = zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); return QVector4D(lookAt, cameraZoomFactor); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp index 2c6d0d32ce2..bfc2891ce7f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp @@ -33,52 +33,90 @@ #include #include #include +#include namespace QmlDesigner { namespace Internal { -// Double precision vec3 for cases where float calculations can suffer from rounding errors -class DoubleVec { +// Double precision vector for cases where float calculations can suffer from rounding errors +class DoubleVec3D { public: - DoubleVec(const QVector3D &v) + DoubleVec3D() = default; + DoubleVec3D(const QVector3D &v) : x(double(v.x())), y(double(v.y())), z(double(v.z())) {} - DoubleVec(double xx, double yy, double zz) + DoubleVec3D(double xx, double yy, double zz) : x(xx), y(yy), z(zz) {} - static double dotProduct(const DoubleVec &v1, const DoubleVec &v2) + static double dotProduct(const DoubleVec3D &v1, const DoubleVec3D &v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } - QVector3D toVec3() + QVector3D toVec3() const { return QVector3D(float(x), float(y), float(z)); } - double x; - double y; - double z; + DoubleVec3D normalized() const + { + double len = x * x + y * y + z * z; + if (qFuzzyIsNull(len - 1.)) { + return *this; + } else if (!qFuzzyIsNull(len)) { + double sqrtLen = std::sqrt(len); + return DoubleVec3D(x / sqrtLen, y / sqrtLen, z / sqrtLen); + } else { + return {}; + } + } + + double length() const + { + double len = x * x + y * y + z * z; + return std::sqrt(len); + } + + double x = 0.; + double y = 0.; + double z = 0.; }; -DoubleVec operator*(double factor, const DoubleVec &v) + + +DoubleVec3D operator*(double factor, const DoubleVec3D &v) { - return DoubleVec(v.x * factor, v.y * factor, v.z * factor); + return DoubleVec3D(v.x * factor, v.y * factor, v.z * factor); } -DoubleVec operator+(const DoubleVec &v1, const DoubleVec &v2) +DoubleVec3D operator*(DoubleVec3D &v1, const DoubleVec3D &v2) { - return DoubleVec(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); + return DoubleVec3D(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); } -DoubleVec operator-(const DoubleVec &v1, const DoubleVec &v2) +DoubleVec3D operator+(const DoubleVec3D &v1, const DoubleVec3D &v2) { - return DoubleVec(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); + return DoubleVec3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); +} + +DoubleVec3D operator-(const DoubleVec3D &v1, const DoubleVec3D &v2) +{ + return DoubleVec3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); +} + +DoubleVec3D operator/(const DoubleVec3D &v, double div) +{ + return DoubleVec3D(v.x / div, v.y / div, v.z / div); +} + +DoubleVec3D operator/(const DoubleVec3D &v1, const DoubleVec3D &v2) +{ + return DoubleVec3D(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z); } MouseArea3D *MouseArea3D::s_mouseGrab = nullptr; @@ -300,15 +338,15 @@ QVector3D MouseArea3D::rayIntersectsPlane(const QVector3D &rayPos0, const QVector3D &planePos, const QVector3D &planeNormal) const { - const DoubleVec rayPos0D(rayPos0); - const DoubleVec rayPos1D(rayPos1); - const DoubleVec planePosD(planePos); - const DoubleVec planeNormalD(planeNormal); - const DoubleVec rayDirectionD = rayPos1D - rayPos0D; - const DoubleVec rayPos0RelativeToPlaneD = rayPos0D - planePosD; + const DoubleVec3D rayPos0D(rayPos0); + const DoubleVec3D rayPos1D(rayPos1); + const DoubleVec3D planePosD(planePos); + const DoubleVec3D planeNormalD(planeNormal); + const DoubleVec3D rayDirectionD = rayPos1D - rayPos0D; + const DoubleVec3D rayPos0RelativeToPlaneD = rayPos0D - planePosD; - const double dotPlaneRayDirection = DoubleVec::dotProduct(planeNormalD, rayDirectionD); - const double dotPlaneRayPos0 = -DoubleVec::dotProduct(planeNormalD, rayPos0RelativeToPlaneD); + const double dotPlaneRayDirection = DoubleVec3D::dotProduct(planeNormalD, rayDirectionD); + const double dotPlaneRayPos0 = -DoubleVec3D::dotProduct(planeNormalD, rayPos0RelativeToPlaneD); if (qFuzzyIsNull(dotPlaneRayDirection)) { // The ray is is parallel to the plane. Note that if dotLinePos0 == 0, it @@ -466,6 +504,29 @@ QVector3D MouseArea3D::pivotScenePosition(QQuick3DNode *node) const return mat44::getPosition(sceneTransform); } +double MouseArea3D::getRelativeScale(QQuick3DNode *node) const +{ + // Calculate the distance independent scale by first mapping the target's position to + // the view. We then measure up a distance on the view (100px) that we use as an + // "anchor" distance. Map the two positions back to the target node, and measure the + // distance between them now, in the 3D scene. The difference between the two distances, + // view and scene, will tell us what the distance independent scale should be. + + QVector3D nodePos(node->scenePosition()); + if (orientation() == QQuick3DNode::RightHanded) + nodePos.setZ(-nodePos.z()); + + DoubleVec3D posInView1(m_view3D->mapFrom3DScene(nodePos)); + + DoubleVec3D posInView2 = posInView1; + posInView2.x = posInView2.x + 100.; + + DoubleVec3D scenePos1(m_view3D->mapTo3DScene(posInView1.toVec3())); + DoubleVec3D scenePos2(m_view3D->mapTo3DScene(posInView2.toVec3())); + + return (scenePos1 - scenePos2).length() / 100.; +} + QVector3D MouseArea3D::getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h index f93fcbb9b8b..11b6de3f0f9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h @@ -114,6 +114,7 @@ public slots: Q_INVOKABLE void applyFreeRotation(QQuick3DNode *node, const QVector3D &startRotation, const QVector3D &pressPos, const QVector3D ¤tPos); Q_INVOKABLE QVector3D pivotScenePosition(QQuick3DNode *node) const; + Q_INVOKABLE double getRelativeScale(QQuick3DNode *node) const; signals: void view3DChanged(); @@ -148,6 +149,7 @@ private: void setHovering(bool enable); QVector3D getNormal() const; QVector3D getCameraToNodeDir(QQuick3DNode *node) const; + QVector3D getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const; Q_DISABLE_COPY(MouseArea3D) QQuick3DViewport *m_view3D = nullptr; @@ -162,8 +164,6 @@ private: bool m_dragging = false; bool m_active = false; - QVector3D getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const; - static MouseArea3D *s_mouseGrab; bool m_grabsMouse = false; QVector3D m_mousePosInPlane;