How to get scaling, rotation, and translation from a transformation matrix
-
I want to get scaling, rotation, and translation from a transformation matrix in Qt C++ OpenGL ES 2.0 to make a keyframe animation with linear interpolation for Android and WebAssembly. I have the next example in JavaScript with the glMatrix (https://glmatrix.net/) library that creates the transformation matrix and gets scaling, rotation, and translation from the transformation matrix and prints them to the console:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>How to get scaling, rotation, and translation from a transformation matrix using glMatrix and JavaScript</title> </head> <body> <!-- Since import maps are not yet supported by all browsers, its is necessary to add the polyfill es-module-shims.js --> <script async src="https://unpkg.com/es-module-shims@1.7.3/dist/es-module-shims.js"> </script> <script type="importmap"> { "imports": { "gl-matrix": "https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/+esm" } } </script> <script type="module"> import { mat4, quat, vec3 } from "gl-matrix"; // Create a transformation matrix const matrix = mat4.create(); mat4.translate(matrix, matrix, [20, 80, 0]); mat4.rotate(matrix, matrix, -90 * Math.PI / 180, [0, 0, 1]); mat4.scale(matrix, matrix, [20, 20, 1]); console.log("Transformation matrix: " + matrix); // Output: 0, -20, 0, 0, // 20, 0, 0, 0, // 0, 0, 1, 0, // 20, 80, 0, 1 // "mat4" has a column-major order // Get scaling const scaling = vec3.create(); mat4.getScaling(scaling, matrix); console.log(`Scaling: (sx: ${scaling[0]},` + ` sy: ${scaling[1]}, sz${scaling[2]})`); // Output: (sx: 20, sy: 20, sz: 1) // Get rotation const rotation = quat.create(); mat4.getRotation(rotation, matrix); console.log(`Rotation: (rx: ${rotation[0]}, ry: ${rotation[1]}`, ` rz: ${rotation[2]}, rw: ${rotation[3]})`); // Output: (rx: 0, ry: 0 rz: -0.7071067690849304, rw: 0.7071067690849304) // Get translation const translation = vec3.create(); mat4.getTranslation(translation, matrix); console.log(`Translation: (tx: ${translation[0]},` + ` ty: ${translation[1]}, tz: ${translation[2]})`); // Output: (tx: 20, ty: 80, tz: 0) </script> </body> </html>
I want to rewrite it to Qt. I read a documentation and tried to google but I didn't find how to extract scaling from a transformation matrix.
#include <QtGui/QMatrix4x4> #include <QtGui/QOpenGLFunctions> #include <QtGui/QQuaternion> #include <QtMath> #include <QtOpenGL/QOpenGLWindow> #include <QtWidgets/QApplication> #include <QtWidgets/QWidget> class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions { public: OpenGLWindow() { setTitle("OpenGL ES 2.0, Qt6, C++"); resize(350, 350); QMatrix4x4 m; m.translate(20.f, 80.f, 0.f); m.rotate(-90.f, QVector3D(0.f, 0.f, 1.f)); m.scale(20.f, 20.f); qDebug() << "Transformation matrix: " << m; // Output: 0, 20, 0, 20, // -20, 0, 0, 80, // 0, 0, 1, 0, // 0, 0, 0, 1 // "QMatrix4x4" has a row-major order } void initializeGL() override { initializeOpenGLFunctions(); glClearColor(0.2f, 0.2f, 0.2f, 1.f); } void paintGL() override { glClear(GL_COLOR_BUFFER_BIT); } }; int main(int argc, char *argv[]) { QApplication::setAttribute(Qt::ApplicationAttribute::AA_UseDesktopOpenGL); QApplication app(argc, argv); OpenGLWindow w; w.show(); return app.exec(); }
-
I opened a source code of
getScaling
and saw how scaling is calculated there: https://glmatrix.net/docs/mat4.js.html#line1197export function getScaling(out, mat) { let m11 = mat[0]; let m12 = mat[1]; let m13 = mat[2]; let m21 = mat[4]; let m22 = mat[5]; let m23 = mat[6]; let m31 = mat[8]; let m32 = mat[9]; let m33 = mat[10]; out[0] = Math.hypot(m11, m12, m13); out[1] = Math.hypot(m21, m22, m23); out[2] = Math.hypot(m31, m32, m33); return out; }
I made the same with qHypot
#include <QtGui/QMatrix3x3> #include <QtGui/QMatrix4x4> #include <QtGui/QOpenGLFunctions> #include <QtGui/QQuaternion> #include <QtMath> #include <QtOpenGL/QOpenGLWindow> #include <QtWidgets/QApplication> #include <QtWidgets/QWidget> class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions { public: OpenGLWindow() { setTitle("OpenGL ES 2.0, Qt6, C++"); resize(350, 350); QMatrix4x4 matrix; matrix.translate(20.f, 80.f, 0.f); matrix.rotate(-90.f, QVector3D(0.f, 0.f, 1.f)); matrix.scale(20.f, 20.f); qDebug() << "Transformation matrix: " << matrix; // Output: 0, 20, 0, 20, // -20, 0, 0, 80, // 0, 0, 1, 0, // 0, 0, 0, 1 // "QMatrix4x4" has a row-major order // Get scaling float sx = qHypot(matrix.row(0)[0], matrix.row(1)[0], matrix.row(2)[0]); float sy = qHypot(matrix.row(0)[1], matrix.row(1)[1], matrix.row(2)[1]); float sz = qHypot(matrix.row(0)[2], matrix.row(1)[2], matrix.row(2)[2]); QVector3D scaling(sx, sy, sz); qDebug() << "Scaling:" << scaling; // Output: QVector3D(20, 20, 1) // Get rotation QMatrix3x3 rotationMatrix; rotationMatrix.data()[0] = matrix.column(0)[0] / sx; rotationMatrix.data()[1] = matrix.column(0)[1] / sy; rotationMatrix.data()[2] = matrix.column(0)[2] / sz; rotationMatrix.data()[3] = matrix.column(1)[0] / sx; rotationMatrix.data()[4] = matrix.column(1)[1] / sy; rotationMatrix.data()[5] = matrix.column(1)[2] / sz; rotationMatrix.data()[6] = matrix.column(2)[0] / sx; rotationMatrix.data()[7] = matrix.column(2)[1] / sy; rotationMatrix.data()[8] = matrix.column(2)[2] / sz; QQuaternion rotation = QQuaternion::fromRotationMatrix(rotationMatrix); qDebug() << "Rotation:" << rotation; // Output: QQuaternion(scalar:0.707107, vector:(0, 0, -0.707107)) // Get translation float tx = matrix.row(0)[3]; float ty = matrix.row(1)[3]; float tz = matrix.row(2)[3]; QVector3D translation(tx, ty, tz); qDebug() << "Translation:" << translation; // Output: QVector3D(20, 80, 0) } void initializeGL() override { initializeOpenGLFunctions(); glClearColor(0.2f, 0.2f, 0.2f, 1.f); } void paintGL() override { glClear(GL_COLOR_BUFFER_BIT); } }; int main(int argc, char *argv[]) { QApplication::setAttribute(Qt::ApplicationAttribute::AA_UseDesktopOpenGL); QApplication app(argc, argv); OpenGLWindow w; w.show(); return app.exec(); }
QT += core gui opengl widgets win32: LIBS += -lopengl32 CONFIG += c++17 SOURCES += \ main.cpp
P.S. The QMatrix4x4 constructor uses row-major order. The glMatrix fromValues method uses column-major order.
-