2
votes

I am trying to animate an object in Qt3D to rotate around a specific axis (not the origin) while performing other transformations (e.g. scaling and translating).

The following code rotates the object as I want it but without animation yet.

QMatrix4x4 mat = QMatrix4x4();
mat.scale(10);
mat.translate(QVector3D(-1.023, 0.836, -0.651));
mat.rotate(QQuaternion::fromAxisAndAngle(QVector3D(0,1,0), -20));
mat.translate(-QVector3D(-1.023, 0.836, -0.651));
//scaling here after rotating/translating shifts the rotation back to be around the origin (??)

Qt3DCore::QTransform *transform = new Qt3DCore::QTransform(root);
transform->setMatrix(mat);
//...
entity->addComponent(transform);   //the entity of the object i am animating

I did not manage to incorporate a QPropertyAnimation as I desire with this code. Only animating the rotationY property does not let me include the rotation origin, so it rotates around the wrong axis. And animating the matrix property produces the end result but rotates in a way that is not desired/realistic in my scenario. So how can I animate this rotation to rotate around a given axis?

EDIT: There is a QML equivalent to what I want. There, you can specify the origin of the rotation and just animate the angle values:

Rotation3D{
  id: doorRotation
  angle: 0
  axis: Qt.vector3d(0,1,0)
  origin: Qt.vector3d(-1.023, 0.836, -0.651)
}

NumberAnimation {target: doorRotation; property: "angle"; from: 0; to: -20; duration: 500}

How can I do this in C++?

1
Apparently the order of scaling and rotating changes the behaviour of the rotation Yepp. Rotations are not commutative i.e. the order counts essentially (except in special cases). One rotation and uniform scaling should work in any order. If scaling is not uniform - it's a difference if you turn something and then distort it or first distort and then rotate it.Scheff's Cat
@Scheff yeah makes sense, but in my example the scaling is uniform. It's just that when the rotation is not around the origin but a different axis the order becomes relevant.Mariam
If you want to know more about this, you may google for e.g. "affine transformation computer graphics". Thus, I found SE: What are Affine Transformations? but there are surely tons of docs and tutorials out there.Scheff's Cat
rotateAround() is not a pure rotation (as rotations are always about origin). It does translations as well (to move rotation center). Involving translations, it's again not commutative anymore. First uniform scale and then translate is different than first translate and then uniform scale... ;-)Scheff's Cat
Can't the Qt 3D: Simple C++ Example help? With a modification like this in the updateMatrix() method in orbittransformcontroller.cp: QVector3D translationVector3D(20, 0, 0); m_matrix.translate(translationVector3D); m_matrix.rotate(m_angle, QVector3D(0.0f, 1.0f, 0.0f)); m_matrix.translate(-translationVector3D);Julien Déramond

1 Answers

1
votes

I think it is possible to use the Qt 3D: Simple C++ Example to obtain what it is looked for by simply modifying the updateMatrix() method in orbittransformcontroller.cpp:

void OrbitTransformController::updateMatrix()
{
    m_matrix.setToIdentity();
    // Move to the origin point of the rotation
    m_matrix.translate(40, 0.0f, -200);
    // Infinite 360° rotation
    m_matrix.rotate(m_angle, QVector3D(0.0f, 1.0f, 0.0f));
    // Radius of the rotation
    m_matrix.translate(m_radius, 0.0f, 0.0f);
    m_target->setMatrix(m_matrix);
} 

Note: It is easier to change the torus into a small sphere to observe the rotation.


EDIT from question asker: This idea is indeed a very good way of solving the problem! To apply it specifically to my scenario the updateMatrix() function has to look like this:

void OrbitTransformController::updateMatrix()
{
    //take the existing matrix to not lose any previous transformations
    m_matrix = m_target->matrix();
    // Move to the origin point of the rotation, _rotationOrigin would be a member variable
    m_matrix.translate(_rotationOrigin);
    // rotate (around the y axis)
    m_matrix.rotate(m_angle, QVector3D(0.0f, 1.0f, 0.0f));
    // translate back
    m_matrix.translate(-_rotationOrigin);
    m_target->setMatrix(m_matrix);
} 

I have made _rotationOrigin also a property in the controller class, which can then be set externally to a different value for each controller.