Fix multisampling and alignment issues after switch to QOpenGLWidget

* Implement `renderText()` more closely to Qt's version
* Add "+ 4" hack (see comment in code) to workaround alignment issue
* Fix scaling issues (`dpr` must be `qreal` and not `int` as it is in Qt's
  implementation, otherwise it breaks when using fractional scaling
  factors)
This commit is contained in:
Martchus 2022-12-20 18:16:06 +01:00 committed by Fabien Givors
parent 11101bbe91
commit e7d964fac4
2 changed files with 173 additions and 53 deletions

View File

@ -28,6 +28,9 @@
#include <QtWidgets>
#include <QtOpenGL>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_1_1>
#include <QPaintEngine>
#include <math.h>
@ -430,58 +433,60 @@ void CGLView::mediaTimerEvent(int ticks)
{
}
#ifndef QT_OPENGL_ES
void CGLView::renderText(double x, double y, double z, const QString &str, const QFont & font = QFont()) {
// Identify x and y locations to render text within widget
int height = this->height();
GLdouble textPosX = 0, textPosY = 0; //, textPosZ = 0;
//project(x, y, 0f, &textPosX, &textPosX, &textPosZ);
textPosX = x;
textPosY = y;
textPosY = height - textPosY; // y is inverted
// Retrieve last OpenGL color to use as a font color
GLfloat glColor[4];
glGetFloatv(GL_CURRENT_COLOR, glColor);
// Render text
QPainter painter(this);
painter.setPen(QColor::fromRgbF(glColor[0], glColor[1], glColor[2], glColor[3]));
painter.setFont(font);
painter.drawText(textPosX, textPosY, str);
painter.end();
}
inline GLint CGLView::project(GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble model[16], const GLdouble proj[16],
const GLint viewport[4],
GLdouble * winx, GLdouble * winy, GLdouble * winz)
static void qt_save_gl_state(QOpenGLFunctions_1_1 *functions1_1)
{
GLdouble in[4], out[4];
functions1_1->glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
functions1_1->glPushAttrib(GL_ALL_ATTRIB_BITS);
functions1_1->glMatrixMode(GL_TEXTURE);
functions1_1->glPushMatrix();
functions1_1->glLoadIdentity();
functions1_1->glMatrixMode(GL_PROJECTION);
functions1_1->glPushMatrix();
functions1_1->glMatrixMode(GL_MODELVIEW);
functions1_1->glPushMatrix();
in[0] = objx;
in[1] = objy;
in[2] = objz;
in[3] = 1.0;
transformPoint(out, model, in);
transformPoint(in, proj, out);
if (in[3] == 0.0)
return GL_FALSE;
in[0] /= in[3];
in[1] /= in[3];
in[2] /= in[3];
*winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
*winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
*winz = (1 + in[2]) / 2;
return GL_TRUE;
functions1_1->glShadeModel(GL_FLAT);
functions1_1->glDisable(GL_CULL_FACE);
functions1_1->glDisable(GL_LIGHTING);
functions1_1->glDisable(GL_STENCIL_TEST);
functions1_1->glDisable(GL_DEPTH_TEST);
functions1_1->glEnable(GL_BLEND);
functions1_1->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
inline void CGLView::transformPoint(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
static void qt_restore_gl_state(QOpenGLFunctions_1_1 *functions1_1)
{
functions1_1->glMatrixMode(GL_TEXTURE);
functions1_1->glPopMatrix();
functions1_1->glMatrixMode(GL_PROJECTION);
functions1_1->glPopMatrix();
functions1_1->glMatrixMode(GL_MODELVIEW);
functions1_1->glPopMatrix();
functions1_1->glPopAttrib();
functions1_1->glPopClientAttrib();
}
static void qt_gl_draw_text(QOpenGLFunctions *functions, QPainter *p, int x, int y, const QString &str,
const QFont &font)
{
GLfloat color[4];
functions->glGetFloatv(GL_CURRENT_COLOR, &color[0]);
const auto col = QColor::fromRgbF(color[0], color[1], color[2],color[3]);
QPen old_pen = p->pen();
QFont old_font = p->font();
p->setPen(col);
p->setFont(font);
p->drawText(x, y, str);
p->setPen(old_pen);
p->setFont(old_font);
}
static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
{
#define M(row,col) m[col*4+row]
out[0] =
@ -495,3 +500,124 @@ inline void CGLView::transformPoint(GLdouble out[4], const GLdouble m[16], const
#undef M
}
static inline GLint qgluProject(GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble model[16], const GLdouble proj[16],
const GLint viewport[4],
GLdouble * winx, GLdouble * winy, GLdouble * winz)
{
GLdouble in[4], out[4];
in[0] = objx;
in[1] = objy;
in[2] = objz;
in[3] = 1.0;
transform_point(out, model, in);
transform_point(in, proj, out);
if (in[3] == 0.0)
return GL_FALSE;
in[0] /= in[3];
in[1] /= in[3];
in[2] /= in[3];
*winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
*winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
*winz = (1 + in[2]) / 2;
return GL_TRUE;
}
#endif // !QT_OPENGL_ES
/*!
* \brief Renders the string \a str into the GL context of this widget.
*
* \a x, \a y and \a z are specified in scene or object coordinates
* relative to the currently set projection and model matrices. This
* can be useful if you want to annotate models with text labels and
* have the labels move with the model as it is rotated etc.
*
* \remarks
* - Taken from Qt 5's QGLWidget implementation (found in
* `qtbase/src/opengl/qgl.cpp`). The same counts for the static helper
* functions defined above.
* - Handling the translation for the \a z coordinate has been removed
* as it is not used here anyways.
*/
void CGLView::renderText(double x, double y, double z, const QString &str, const QFont & font = QFont()) {
#ifndef QT_OPENGL_ES
auto *const ctx = context();
auto *const gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_1>();
if (!ctx->isOpenGLES() && gl1funcs) {
if (str.isEmpty() || !isValid())
return;
QOpenGLFunctions *const funcs = ctx->functions();
QPaintEngine *engine = paintEngine();
QPainter *p;
bool reuse_painter = false;
bool use_depth_testing = funcs->glIsEnabled(GL_DEPTH_TEST);
bool use_scissor_testing = funcs->glIsEnabled(GL_SCISSOR_TEST);
qt_save_gl_state(gl1funcs);
if (engine->isActive()) {
reuse_painter = true;
p = engine->painter();
} else {
p = new QPainter(this);
}
const auto *const dev = p->device();
const auto width = dev ? dev->width() : this->width();
const auto height = dev ? dev->height() : this->height();
const auto dpr = dev ? dev->devicePixelRatioF() : 1.0;
GLdouble model[4 * 4], proj[4 * 4];
GLint view[4];
gl1funcs->glGetDoublev(GL_MODELVIEW_MATRIX, &model[0]);
gl1funcs->glGetDoublev(GL_PROJECTION_MATRIX, &proj[0]);
funcs->glGetIntegerv(GL_VIEWPORT, &view[0]);
GLdouble win_x = 0, win_y = 0, win_z = 0;
qgluProject(x, y, z, &model[0], &proj[0], &view[0],
&win_x, &win_y, &win_z);
win_x /= dpr;
win_y /= dpr;
win_y = height + 4 - win_y; // y is inverted
// note: Not sure why this is rendered 4 pixels to high, just putting "+ 4" there for now.
QRect viewport(view[0], view[1], view[2], view[3]);
if (!use_scissor_testing && viewport != rect()) {
funcs->glScissor(view[0], view[1], view[2], view[3]);
funcs->glEnable(GL_SCISSOR_TEST);
} else if (use_scissor_testing) {
funcs->glEnable(GL_SCISSOR_TEST);
}
funcs->glViewport(0, 0, width * dpr, height * dpr);
gl1funcs->glAlphaFunc(GL_GREATER, 0.0);
funcs->glEnable(GL_ALPHA_TEST);
if (use_depth_testing)
funcs->glEnable(GL_DEPTH_TEST);
qt_gl_draw_text(funcs, p, qRound(win_x), qRound(win_y), str, font);
if (!reuse_painter) {
p->end();
delete p;
}
qt_restore_gl_state(gl1funcs);
return;
}
#else // QT_OPENGL_ES
Q_UNUSED(x);
Q_UNUSED(y);
Q_UNUSED(z);
Q_UNUSED(str);
Q_UNUSED(font);
#endif
qWarning("QGLWidget::renderText is not supported under OpenGL/ES");
}

View File

@ -63,13 +63,7 @@ protected:
void resizeGL(int width, int height);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void renderText(double x, double y, double z, const QString &str, const QFont & font);
GLint project(GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble model[16], const GLdouble proj[16],
const GLint viewport[4],
GLdouble * winx, GLdouble * winy, GLdouble * winz);
void transformPoint(GLdouble out[4], const GLdouble m[16], const GLdouble in[4]);
private:
void drawDisplayText();