1
votes

I have a task to draw 3D objects on the ground using OpenGL. I use OpenGL left-handed coord system where Y axis is up. But 3D objects and camera orbiting objects should use different coord systems with following properties:

  • XY plane is a ground plane;
  • Z-axis is up;
  • Y-axis is "North";
  • X-axis is "East";
  • The azimuth (or horizontal) angle is [0, 360] degrees;
  • The elevation (or vertical) angle is [0, 90] degrees from XY plane. End user uses azimuth and elevation to rotate camera around some center. So I made following code to convert from spherical coordinates to quaternion:
//polar: x - radius, y - horizontal angle, z - vertical 
    QQuaternion CoordinateSystemGround::fromPolarToQuat(const QVector3D& polar) const {
    QQuaternion dest = QQuaternion::fromEulerAngles({0, polar.y(), polar.z()});
    //convert user coord system back to OpenGL by rotation around X-axis
    QQuaternion orig = QQuaternion::fromAxisAndAngle(1, 0, 0, -90);
    return dest * orig;
}

and back:

    QVector3D CoordinateSystemGround::fromQuatToPolar(const QQuaternion& q) const {
    //convert OpenGL coord system to destination by rotation around X-axis
    QQuaternion dest = QQuaternion::fromAxisAndAngle(1, 0, 0, 90);
    QQuaternion out = q * dest;
    QVector3D euler = out.toEulerAngles();

    float hor = euler.y();
    if(hor < 0.0f)
        hor += 360.0f;

    float ver = euler.z();
    if(ver > 90.0f)
        ver = 90.0f;
    else if(ver < 0.0f)
        ver = 0.0f;

    //x changes later
    return QVector3D(0, hor, ver);
}

But it doesn't work right. I suppose fromPolarToQuat conversion has mistake somewhere and I can't understand where.

1
I recommend to us QMatrix4x4::lookAt to setup a view matrix.Rabbid76
@Rabbid76, I already use it. But at first I need to get camera coordinates in cartesian, so steps are: polar -> quaternion -> cartesian -> view matrix.Alexey S.
why not use 4x4 homogenous transform matrices ? they are exactly for this ... its just matter of multiplying by direct or inverse matrix ...Spektre
Why don't you transform from polar to world coordinates directly? There is no need for quaternions.Michael Nastenko
@MichaelNastenko, well, I found easier and less error-prone to get angles with quaternions rather than with direct conversion.Alexey S.

1 Answers

1
votes

Seems like I found the solution. So, to get polar angles from quaternion:

QVector3D CoordinateSystemGround::fromQuatToPolar(const QQuaternion& q) const {
    QQuaternion coord1 = QQuaternion::fromAxisAndAngle(0, 1, 0, -180);
    QQuaternion coord2 = QQuaternion::fromAxisAndAngle(1, 0, 0, -90);
    QQuaternion out = orig1 * orig2 * q;
    QVector3D euler = out.toEulerAngles();

    float hor = euler.y();
    if(hor < 0.0f)
        hor += 360.0f;

    float ver = euler.x();

    return QVector3D(0, hor, -ver);
}

And back:

QQuaternion CoordinateSystemGround::fromPolarToQuat(const QVector3D& polar) const {
    QQuaternion dest = QQuaternion::fromEulerAngles({-polar.z(), polar.y(), 0});
    QQuaternion coord1 = QQuaternion::fromAxisAndAngle(1, 0, 0, 90);
    QQuaternion coord2 = QQuaternion::fromAxisAndAngle(0, 1, 0, 180);
    return coord1 * coord2 * dest;
}

Not sure it's an optimal solution though, but it works as it should.

Edited

After some research I've found few mistakes and made optimized and hope correct version of conversion:

QVector3D CoordinateSystemGround::fromQuatToPolar(const QQuaternion& q) const {
    // back to OpenGL coord system: just multiplication on inverted destination coord system
    QQuaternion dest = QQuaternion::fromAxes({-1, 0, 0}, {0, 0, 1}, {0, 1, 0}).inverted();
    QVector3D euler = (dest * q).toEulerAngles();

    float hor = euler.y();
    if(hor < 0.0f)
        hor += 360.0f;

    float ver = euler.x();

    return QVector3D(0, hor, -ver);
}

And back:

QQuaternion CoordinateSystemGround::fromPolarToQuat(const QVector3D& polar) const {
    //just rotate if we were in OpenGL coord system
    QQuaternion orig = QQuaternion::fromEulerAngles({-polar.z(), polar.y(), 0});
    //and then multiply on destination coord system
    QQuaternion dest = QQuaternion::fromAxes({-1, 0, 0}, {0, 0, 1}, {0, 1, 0});
    return dest * orig;
}