aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp346
1 files changed, 313 insertions, 33 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp
index 03ec0f56d9..1495bc4ea5 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp
@@ -58,10 +58,7 @@ public:
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
- QVector3D toVec3() const
- {
- return QVector3D(float(x), float(y), float(z));
- }
+ QVector3D toVec3() const { return {float(x), float(y), float(z)}; }
DoubleVec3D normalized() const
{
@@ -82,6 +79,8 @@ public:
return std::sqrt(len);
}
+ DoubleVec3D operator-() { return {-x, -y, -z}; }
+
double x = 0.;
double y = 0.;
double z = 0.;
@@ -119,6 +118,268 @@ DoubleVec3D operator/(const DoubleVec3D &v1, const DoubleVec3D &v2)
return DoubleVec3D(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z);
}
+// Limited functionality double precision matrix4x4 for cases where float calculations
+// can suffer from rounding errors
+class DoubleMat44 {
+public:
+ DoubleMat44()
+ {
+ // Default is identity matrix
+ m[0][0] = 1.;
+ m[0][1] = 0.;
+ m[0][2] = 0.;
+ m[0][3] = 0.;
+ m[1][0] = 0.;
+ m[1][1] = 1.;
+ m[1][2] = 0.;
+ m[1][3] = 0.;
+ m[2][0] = 0.;
+ m[2][1] = 0.;
+ m[2][2] = 1.;
+ m[2][3] = 0.;
+ m[3][0] = 0.;
+ m[3][1] = 0.;
+ m[3][2] = 0.;
+ m[3][3] = 1.;
+ }
+
+ DoubleMat44(const QMatrix4x4 &mat)
+ {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j)
+ m[i][j] = double(mat(j, i));
+ }
+ }
+
+ QMatrix4x4 toQMatrix4x4()
+ {
+ return QMatrix4x4(float(m[0][0]), float(m[1][0]), float(m[2][0]), float(m[3][0]),
+ float(m[0][1]), float(m[1][1]), float(m[2][1]), float(m[3][1]),
+ float(m[0][2]), float(m[1][2]), float(m[2][2]), float(m[3][2]),
+ float(m[0][3]), float(m[1][3]), float(m[2][3]), float(m[3][3]));
+ }
+
+ static inline double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1)
+ {
+ return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0];
+ }
+
+ static inline double matrixDet3(const double m[4][4], int col0, int col1, int col2,
+ int row0, int row1, int row2)
+ {
+ return m[col0][row0] * matrixDet2(m, col1, col2, row1, row2)
+ - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2)
+ + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2);
+ }
+
+ DoubleMat44 inverted()
+ {
+ DoubleMat44 inv;
+
+ double det = matrixDet3(m, 0, 1, 2, 0, 1, 2);
+ if (det != 0.) {
+ det = 1. / det;
+
+ inv.m[0][0] = matrixDet2(m, 1, 2, 1, 2) * det;
+ inv.m[0][1] = -matrixDet2(m, 0, 2, 1, 2) * det;
+ inv.m[0][2] = matrixDet2(m, 0, 1, 1, 2) * det;
+ inv.m[0][3] = 0;
+ inv.m[1][0] = -matrixDet2(m, 1, 2, 0, 2) * det;
+ inv.m[1][1] = matrixDet2(m, 0, 2, 0, 2) * det;
+ inv.m[1][2] = -matrixDet2(m, 0, 1, 0, 2) * det;
+ inv.m[1][3] = 0;
+ inv.m[2][0] = matrixDet2(m, 1, 2, 0, 1) * det;
+ inv.m[2][1] = -matrixDet2(m, 0, 2, 0, 1) * det;
+ inv.m[2][2] = matrixDet2(m, 0, 1, 0, 1) * det;
+ inv.m[2][3] = 0;
+ inv.m[3][0] = -inv.m[0][0] * m[3][0] - inv.m[1][0] * m[3][1] - inv.m[2][0] * m[3][2];
+ inv.m[3][1] = -inv.m[0][1] * m[3][0] - inv.m[1][1] * m[3][1] - inv.m[2][1] * m[3][2];
+ inv.m[3][2] = -inv.m[0][2] * m[3][0] - inv.m[1][2] * m[3][1] - inv.m[2][2] * m[3][2];
+ inv.m[3][3] = 1;
+ }
+ return inv;
+ }
+
+ DoubleVec3D transform(const DoubleVec3D &v)
+ {
+ DoubleVec3D ret;
+
+ auto multCol = [&](int c, double d) {
+ ret.x += m[c][0] * d;
+ ret.y += m[c][1] * d;
+ ret.z += m[c][2] * d;
+ };
+
+ multCol(0, v.x);
+ multCol(1, v.y);
+ multCol(2, v.z);
+ multCol(3, 1.);
+
+ return ret;
+ }
+
+ static DoubleMat44 matrixMultiD(const QMatrix4x4& m1, const QMatrix4x4& m2)
+ {
+ DoubleMat44 m1d(m1);
+ DoubleMat44 m2d(m2);
+
+ return matrixMultiD(m1d, m2d);
+ }
+
+ static DoubleMat44 matrixMultiD(const DoubleMat44& m1, const DoubleMat44& m2)
+ {
+ DoubleMat44 m;
+
+ m.m[0][0] = m1.m[0][0] * m2.m[0][0]
+ + m1.m[1][0] * m2.m[0][1]
+ + m1.m[2][0] * m2.m[0][2]
+ + m1.m[3][0] * m2.m[0][3];
+ m.m[0][1] = m1.m[0][1] * m2.m[0][0]
+ + m1.m[1][1] * m2.m[0][1]
+ + m1.m[2][1] * m2.m[0][2]
+ + m1.m[3][1] * m2.m[0][3];
+ m.m[0][2] = m1.m[0][2] * m2.m[0][0]
+ + m1.m[1][2] * m2.m[0][1]
+ + m1.m[2][2] * m2.m[0][2]
+ + m1.m[3][2] * m2.m[0][3];
+ m.m[0][3] = m1.m[0][3] * m2.m[0][0]
+ + m1.m[1][3] * m2.m[0][1]
+ + m1.m[2][3] * m2.m[0][2]
+ + m1.m[3][3] * m2.m[0][3];
+
+ m.m[1][0] = m1.m[0][0] * m2.m[1][0]
+ + m1.m[1][0] * m2.m[1][1]
+ + m1.m[2][0] * m2.m[1][2]
+ + m1.m[3][0] * m2.m[1][3];
+ m.m[1][1] = m1.m[0][1] * m2.m[1][0]
+ + m1.m[1][1] * m2.m[1][1]
+ + m1.m[2][1] * m2.m[1][2]
+ + m1.m[3][1] * m2.m[1][3];
+ m.m[1][2] = m1.m[0][2] * m2.m[1][0]
+ + m1.m[1][2] * m2.m[1][1]
+ + m1.m[2][2] * m2.m[1][2]
+ + m1.m[3][2] * m2.m[1][3];
+ m.m[1][3] = m1.m[0][3] * m2.m[1][0]
+ + m1.m[1][3] * m2.m[1][1]
+ + m1.m[2][3] * m2.m[1][2]
+ + m1.m[3][3] * m2.m[1][3];
+
+ m.m[2][0] = m1.m[0][0] * m2.m[2][0]
+ + m1.m[1][0] * m2.m[2][1]
+ + m1.m[2][0] * m2.m[2][2]
+ + m1.m[3][0] * m2.m[2][3];
+ m.m[2][1] = m1.m[0][1] * m2.m[2][0]
+ + m1.m[1][1] * m2.m[2][1]
+ + m1.m[2][1] * m2.m[2][2]
+ + m1.m[3][1] * m2.m[2][3];
+ m.m[2][2] = m1.m[0][2] * m2.m[2][0]
+ + m1.m[1][2] * m2.m[2][1]
+ + m1.m[2][2] * m2.m[2][2]
+ + m1.m[3][2] * m2.m[2][3];
+ m.m[2][3] = m1.m[0][3] * m2.m[2][0]
+ + m1.m[1][3] * m2.m[2][1]
+ + m1.m[2][3] * m2.m[2][2]
+ + m1.m[3][3] * m2.m[2][3];
+
+ m.m[3][0] = m1.m[0][0] * m2.m[3][0]
+ + m1.m[1][0] * m2.m[3][1]
+ + m1.m[2][0] * m2.m[3][2]
+ + m1.m[3][0] * m2.m[3][3];
+ m.m[3][1] = m1.m[0][1] * m2.m[3][0]
+ + m1.m[1][1] * m2.m[3][1]
+ + m1.m[2][1] * m2.m[3][2]
+ + m1.m[3][1] * m2.m[3][3];
+ m.m[3][2] = m1.m[0][2] * m2.m[3][0]
+ + m1.m[1][2] * m2.m[3][1]
+ + m1.m[2][2] * m2.m[3][2]
+ + m1.m[3][2] * m2.m[3][3];
+ m.m[3][3] = m1.m[0][3] * m2.m[3][0]
+ + m1.m[1][3] * m2.m[3][1]
+ + m1.m[2][3] * m2.m[3][2]
+ + m1.m[3][3] * m2.m[3][3];
+
+ return m;
+ }
+
+ static DoubleMat44 rotationMatrix(const QQuaternion &rot)
+ {
+ DoubleMat44 rotMat;
+ double xp = double(rot.x());
+ double yp = double(rot.y());
+ double zp = double(rot.z());
+ double wp = double(rot.scalar());
+
+ const double f2x = xp + xp;
+ const double f2y = yp + yp;
+ const double f2z = zp + zp;
+ const double f2xw = f2x * wp;
+ const double f2yw = f2y * wp;
+ const double f2zw = f2z * wp;
+ const double f2xx = f2x * xp;
+ const double f2xy = f2x * yp;
+ const double f2xz = f2x * zp;
+ const double f2yy = f2y * yp;
+ const double f2yz = f2y * zp;
+ const double f2zz = f2z * zp;
+
+ rotMat.m[0][0] = 1. - (f2yy + f2zz);
+ rotMat.m[1][0] = f2xy - f2zw;
+ rotMat.m[2][0] = f2xz + f2yw;
+ rotMat.m[0][1] = f2xy + f2zw;
+ rotMat.m[1][1] = 1. - (f2xx + f2zz);
+ rotMat.m[2][1] = f2yz - f2xw;
+ rotMat.m[0][2] = f2xz - f2yw;
+ rotMat.m[1][2] = f2yz + f2xw;
+ rotMat.m[2][2] = 1. - (f2xx + f2yy);
+
+ return rotMat;
+ }
+
+ double m[4][4];
+};
+
+static DoubleMat44 calcLocalTransform(const QQuick3DNode *node)
+{
+ DoubleVec3D pivotD(-node->pivot());
+ const DoubleVec3D scaleD(node->scale());
+ const DoubleVec3D posD(node->position());
+ pivotD = pivotD * scaleD;
+ DoubleMat44 localTransform;
+
+ localTransform.m[0][0] = scaleD.x;
+ localTransform.m[1][1] = scaleD.y;
+ localTransform.m[2][2] = scaleD.z;
+
+ localTransform.m[3][0] = pivotD.x;
+ localTransform.m[3][1] = pivotD.y;
+ localTransform.m[3][2] = pivotD.z;
+
+ DoubleMat44 rotMat = DoubleMat44::rotationMatrix(node->rotation());
+ localTransform = DoubleMat44::matrixMultiD(rotMat, localTransform);
+
+ localTransform.m[3][0] += posD.x;
+ localTransform.m[3][1] += posD.y;
+ localTransform.m[3][2] += posD.z;
+
+ return localTransform;
+}
+
+static DoubleMat44 calcGlobalTransform(const QQuick3DNode *node)
+{
+ DoubleMat44 localTrans = calcLocalTransform(node);
+ QQuick3DNode *parent = node->parentNode();
+ if (parent) {
+ DoubleMat44 globalTrans = calcGlobalTransform(parent);
+ return DoubleMat44::matrixMultiD(globalTrans, localTrans);
+ }
+ return localTrans;
+}
+
+static DoubleVec3D getNormalD(const DoubleMat44 &m)
+{
+ return DoubleVec3D(m.m[2][0], m.m[2][1], m.m[2][2]).normalized();
+}
+
MouseArea3D *MouseArea3D::s_mouseGrab = nullptr;
MouseArea3D::MouseArea3D(QQuick3DNode *parent)
@@ -333,29 +594,25 @@ void MouseArea3D::componentComplete()
m_view3D->installEventFilter(this);
}
-QVector3D MouseArea3D::rayIntersectsPlane(const QVector3D &rayPos0,
- const QVector3D &rayPos1,
- const QVector3D &planePos,
- const QVector3D &planeNormal) const
+static DoubleVec3D rayIntersectsPlaneD(const DoubleVec3D &rayPos0,
+ const DoubleVec3D &rayPos1,
+ const DoubleVec3D &planePos,
+ const DoubleVec3D &planeNormal)
{
- 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 DoubleVec3D rayDirection = rayPos1 - rayPos0;
+ const DoubleVec3D rayPos0RelativeToPlane = rayPos0 - planePos;
- const double dotPlaneRayDirection = DoubleVec3D::dotProduct(planeNormalD, rayDirectionD);
- const double dotPlaneRayPos0 = -DoubleVec3D::dotProduct(planeNormalD, rayPos0RelativeToPlaneD);
+ const double dotPlaneRayDirection = DoubleVec3D::dotProduct(planeNormal, rayDirection);
+ const double dotPlaneRayPos0 = -DoubleVec3D::dotProduct(planeNormal, rayPos0RelativeToPlane);
if (qFuzzyIsNull(dotPlaneRayDirection)) {
- // The ray is is parallel to the plane. Note that if dotLinePos0 == 0, it
+ // The ray is is parallel to the plane. Note that if dotPlaneRayPos0 == 0, it
// additionally means that the line lies in plane as well. In any case, we
// signal that we cannot find a single intersection point.
- return QVector3D(0.f, 0.f, -1.f);
+ return DoubleVec3D(0., 0., -1.);
}
- // Since we work with a ray (that has a start), distanceFromLinePos0ToPlane
+ // Since we work with a ray (that has a start), distanceFromRayPos0ToPlane
// must be above 0. If it was a line segment (with an end), it also need to be less than 1.
// (Note: a third option would be a "line", which is different from a ray or segment in that
// it has neither a start, nor an end). Then we wouldn't need to check the distance at all.
@@ -363,8 +620,21 @@ QVector3D MouseArea3D::rayIntersectsPlane(const QVector3D &rayPos0,
// the line were directed away from the plane when looking forward.
const double distanceFromRayPos0ToPlane = dotPlaneRayPos0 / dotPlaneRayDirection;
if (distanceFromRayPos0ToPlane <= 0.)
- return QVector3D(0.f, 0.f, -1.f);
- return (rayPos0D + distanceFromRayPos0ToPlane * rayDirectionD).toVec3();
+ return DoubleVec3D(0., 0., -1.);
+ return (rayPos0 + distanceFromRayPos0ToPlane * rayDirection);
+}
+
+QVector3D MouseArea3D::rayIntersectsPlane(const QVector3D &rayPos0,
+ const QVector3D &rayPos1,
+ const QVector3D &planePos,
+ const QVector3D &planeNormal) const
+{
+ const DoubleVec3D rayPos0D(rayPos0);
+ const DoubleVec3D rayPos1D(rayPos1);
+ const DoubleVec3D planePosD(planePos);
+ const DoubleVec3D planeNormalD(planeNormal);
+
+ return rayIntersectsPlaneD(rayPos0D, rayPos1D, planePosD, planeNormalD).toVec3();
}
// Get a new scale based on a relative scene distance along a drag axes.
@@ -544,18 +814,28 @@ QVector3D MouseArea3D::getMousePosInPlane(const MouseArea3D *helper,
if (!helper)
helper = this;
- const QVector3D mousePos1(float(mousePosInView.x()), float(mousePosInView.y()), 0);
- const QVector3D rayPos0 = m_view3D->mapTo3DScene(mousePos1);
- const QVector3D mousePos2(float(mousePosInView.x()), float(mousePosInView.y()),
- rayPos0.length());
- const QVector3D rayPos1 = m_view3D->mapTo3DScene(mousePos2);
- const QVector3D globalPlanePosition = helper->mapPositionToScene(QVector3D(0, 0, 0));
- const QVector3D intersectGlobalPos = rayIntersectsPlane(rayPos0, rayPos1,
- globalPlanePosition, helper->forward());
- if (qFuzzyCompare(intersectGlobalPos.z(), -1))
- return intersectGlobalPos;
-
- return helper->mapPositionFromScene(intersectGlobalPos);
+ const DoubleVec3D mousePos1(float(mousePosInView.x()), float(mousePosInView.y()), 0);
+ const DoubleVec3D rayPos0 = m_view3D->mapTo3DScene(mousePos1.toVec3());
+ DoubleVec3D rayPos1;
+ if (qobject_cast<QQuick3DOrthographicCamera *>(m_view3D->camera())) {
+ rayPos1 = rayPos0 - rayPos0.length() * DoubleVec3D(m_view3D->camera()->cameraNode()->getDirection());
+ } else {
+ DoubleVec3D dir;
+ DoubleVec3D camPos = m_view3D->camera()->scenePosition();
+ dir = (rayPos0 - camPos).normalized();
+ rayPos1 = rayPos0 + rayPos0.length() * dir;
+ }
+
+ const DoubleVec3D globalPlanePosition = helper->mapPositionToScene(QVector3D(0, 0, 0));
+ DoubleMat44 sceneTrans = calcGlobalTransform(helper);
+ const DoubleVec3D intersectGlobalPos = rayIntersectsPlaneD(rayPos0, rayPos1,
+ globalPlanePosition,
+ -getNormalD(sceneTrans));
+
+ if (qFuzzyCompare(intersectGlobalPos.z, -1.))
+ return intersectGlobalPos.toVec3();
+
+ return sceneTrans.inverted().transform(intersectGlobalPos).toVec3();
}
bool MouseArea3D::eventFilter(QObject *, QEvent *event)