4
votes

Is there an easy way to use QMatrix4x4 with OpenGL functions, specifically glMultMatrixf?

If I understand this right, I'd have to transpose the matrix, and be sure to convert qreal (which can be either float or double depending on the underlying system) to GLfloat. Isn't there a function which does this for me?

I also have the same problem with QVector3D, which again I need as GLfloat array in the function glVertex3fv.

2

2 Answers

4
votes

First I want to mention that QMatrix4x4 has built-in operators for matrix multiplication (with a second matrix, a vector or a scalar). However, your question still needs an answer, as you want to pass a matrix to OpenGL sooner or later.

QMatrix4x4 uses qreals for internal representation. While the class is intended to be used with OpenGL directly (using constData() as suggested by Bart in his answer), you can make sure you pass it to the corresponding OpenGL function depending on the type, if you want to keep platform compatibility (on embedded devices, qreal is float!):

// these are defined in the OpenGL headers:
void glMultMatrixf(const GLfloat *m);
void glMultMatrixd(const GLdouble *m);

// add overloaded functions which call the underlying OpenGL function
inline void glMultMatrix(const GLfloat  *m) { glMultMatrixf(m); }
inline void glMultMatrix(const GLdouble *m) { glMultMatrixd(m); }

// add an overload for QMatrix4x4 for convenience
inline void glMultMatrix(const QMatrix4x4 &m) { glMultMatrix(m.constData()); }

You can also use this mechanism for vectors, here the glVertex* family, where it makes even more sense because of the number of components the "raw pointer" overloads need to consider, but the object oriented overloads can do automatically for you:

inline void glVertex2v(const GLfloat  *v) { glVertex2fv(v); }
inline void glVertex2v(const GLdouble *v) { glVertex2dv(v); }
inline void glVertex3v(const GLfloat  *v) { glVertex3fv(v); }
inline void glVertex3v(const GLdouble *v) { glVertex3dv(v); }
inline void glVertex4v(const GLfloat  *v) { glVertex4fv(v); }
inline void glVertex4v(const GLdouble *v) { glVertex4dv(v); }

// Note that QVectorND use floats, but we check this during compile time...
Q_STATIC_ASSERT(sizeof(QVector2D) == 2*sizeof(float));
Q_STATIC_ASSERT(sizeof(QVector3D) == 3*sizeof(float));
Q_STATIC_ASSERT(sizeof(QVector4D) == 4*sizeof(float));
inline void glVertex(const QVector2D &v) { glVertex2v(reinterpret_cast<const float*>(&v)); }
inline void glVertex(const QVector3D &v) { glVertex3v(reinterpret_cast<const float*>(&v)); }
inline void glVertex(const QVector4D &v) { glVertex4v(reinterpret_cast<const float*>(&v)); }

// Even for QPointF we can do it!
Q_STATIC_ASSERT(sizeof(QPointF) == 2*sizeof(qreal));
inline void glVertex(const QPointF &v) { glVertex4v(reinterpret_cast<const qreal*>(&v)); }

So your code keeps valid if the following changes are made:

  • Qt changes the representation of QVector* / QMatrix to use floats / doubles respectively,
  • Your code changes the number of components of vectors

... while especially the second isn't the case when using raw OpenGL commands like glVertex3f.

The Q_STATIC_ASSERTions in the code above are only defined since Qt 5.0. If you are using Qt4 (or code with Qt4 compatibility), add this in some global header file / before your definitions: http://ideone.com/VDPUSg [source: Qt5 / qglobal.h]

2
votes

qreal is defined to be a double on all platforms, except for ARM architectures. So most likely for you they are doubles.

That said, yes, you can use your QMatrix4x4 perfectly fine with OpenGL, using the constData() method. Of course with a double type you would either have to use glMultMatrixd, or create a float matrix out of it. Which might not be what you want to do. There are various examples by Qt which use the matrix type in their OpenGL demos.

I personally never use Qt's matrix and vector types for my OpenGL code though (even though I extensively use Qt), but rather go for a library like Eigen, GLM, or something else suitable.