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:
parent
11101bbe91
commit
e7d964fac4
220
src/GlView.cpp
220
src/GlView.cpp
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue