I am trying to write a 2D application with openGL (do not ask why, it a test for a stage) where I have a texture I can move around with a drag and drop, rotate or scal with the mouse. My texture is applied on a square.
For these operations, I need to know if I am clicking on the polygon, and the distance and angle between my cursor and the polygon center (which can be moved).
I did not find how to do these with the tutorial I am following does not cover that.
How can I find those 2 informations?
Here is the code :
#include "glwidget.h"
#include <QMouseEvent>
#include <QKeyEvent>
#ifdef WIN32
#include <GL/glext.h>
PFNGLACTIVETEXTUREPROC pGlActiveTexture = NULL;
#define glActiveTexture pGlActiveTexture
#endif //WIN32
GlWidget::GlWidget(QWidget *parent) :
QGLWidget(QGLFormat(), parent)
{
}
GlWidget::~GlWidget(){}
QSize GlWidget::sizeHint() const
{
return QSize(640,480);
}
void GlWidget::resizeGL(int w, int h){
h = (h<=0?1:h);
pMatrix.setToIdentity();
pMatrix.perspective(60.0,(float)w /(float)h,0.1,10);
glViewport(0,0,w,h);
//glScissor(w*0.8,0,w*0.2,h);
//glDisable(GL_SCISSOR_TEST);
}
void GlWidget::initializeGL(){
#ifdef WIN32
glActiveTexture = (PFNGLACTIVETEXTUREPROC) wglGetProcAddress((LPCSTR) "glActiveTexture");
#endif
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
qglClearColor(QColor(Qt::black));
shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
shaderProgram.link();
vertices << QVector3D(-1,-1,-2) << QVector3D(1,-1,-2) << QVector3D(-1,1,-2)
<< QVector3D(-1,1,-2) << QVector3D(1,-1,-2) << QVector3D(1,1,-2);
textureCoordinates << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(0, 1)
<< QVector2D(0, 1) << QVector2D(1, 0) << QVector2D(1, 1);
texture = bindTexture(QPixmap(":/icone.png"));
//mMatrix.setToIdentity();
}
void GlWidget::paintGL(){
//qglClearColor(QColor(Qt::white));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*qglClearColor(QColor(Qt::black));
glScissor(this->width()*0.8,0,this->width()*0.2,this->height());
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);*/
QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;
/* QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(25, 0, 1, 0);
cameraTransformation.rotate(-25, 1, 0, 0);
QVector3D cameraPosition = cameraTransformation * QVector3D(0, 0, 2.5);
QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);
vMatrix.lookAt(cameraPosition, QVector3D(0, 0, 0), cameraUpDirection);*/
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix*vMatrix*mMatrix);
shaderProgram.setUniformValue("texture", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glActiveTexture(0);
shaderProgram.setAttributeArray("vertex", vertices.constData());
shaderProgram.enableAttributeArray("vertex");
shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
shaderProgram.enableAttributeArray("textureCoordinate");
glDrawArrays(GL_TRIANGLES,0,vertices.size());
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("textureCoordinate");
shaderProgram.release();
}
void GlWidget::mousePressEvent(QMouseEvent *event){
if(event->button()==Qt::LeftButton)
lastPos = event->pos();
event->accept();
}
void GlWidget::mouseMoveEvent(QMouseEvent *event){
if(!event->buttons().testFlag(Qt::LeftButton)) return;
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if(keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
//TODO : implement scaling
}
else if(!keysPressed.contains(Qt::Key_Control) && keysPressed.contains(Qt::Key_Shift)){
//TODO : implement rotate
}
else if(!keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
//TODO : implement drag
}
}
void GlWidget::keyPressEvent(QKeyEvent *event){
keysPressed.append(event->key());
}
void GlWidget::keyReleaseEvent(QKeyEvent *event){
keysPressed.remove(keysPressed.lastIndexOf(event->key()));
}
All the comments are from test I've done and the original tutorial
Related
I have a simple vertex and fragment shader. I use them in a class derived from QtOpenGLWidget.the program is supposed to draw a triangle, but it does not show anything. The glClearColor works fine and I am able to change the background color. When I run the program from command line, the program outputs one single line, but draws nothing:
QOpenGLShaderProgram::attributeLocation( position ): shader program is not linked
When I insert getGLError after gl commands, none of them report an error.
here is the class definition:
class GLCanvas:public QOpenGLWidget , protected QOpenGLFunctions
{
QMatrix4x4 matrix;
QOpenGLShaderProgram* shader;
GLuint posLocation;
GLuint colLocation;
GLuint matLocation;
void cleanup();
public:
GLCanvas(QWidget* parent);
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
QStringList getGLinfo();
};
static const char *vertexShaderSource =
R"(
#version 330
in vec3 position;
void main()
{
gl_Position = vec4(position, 1);
})";
static const char *fragmentShaderSource =
R"(
#version 330
void main()
{
gl_FragColor = vec4(0.4,0.4,0.8,1.0);
})";
GLCanvas::GLCanvas(QWidget* parent): QOpenGLWidget(parent)
{
setFormat(QSurfaceFormat::defaultFormat());
matrix.setToIdentity();
posLocation = -1;
colLocation = -1;
matLocation = -1;
}
void GLCanvas::initializeGL()
{
makeCurrent();
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLCanvas::cleanup);
constexpr int glNoError = 0;
GLenum error = -1;
initializeOpenGLFunctions();
glClearColor(.1f, .1f, .1f, 1.0f);
error = glGetError();
if (error != glNoError)
{
qDebug() << "glclearcolor failed";
}
shader = new QOpenGLShaderProgram(this);
shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
error = glGetError();
if (error != glNoError)
{
qDebug() << "failed after adding vertex shader.";
}
shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
error = glGetError();
if (error != glNoError)
{
qDebug() << "failed after adding fragment shader.";
}
Q_ASSERT(shader->link());
error = glGetError();
if (error != glNoError)
{
qDebug() << "gl error at link.";
}
//colLocation = shader->attributeLocation("color");
//matLocation = shader->uniformLocation("matrix");
//shader->release();
posLocation = shader->attributeLocation("position");
error = glGetError();
if (error != glNoError)
{
qDebug() << "gl error at link.";
}
glEnable(GL_DEPTH_TEST);
//colLocation = shader->attributeLocation("color");
//matLocation = shader->uniformLocation("matrix");
//shader->release();
//glEnable(GL_CULL_FACE);
}
//###################################################################
void GLCanvas::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat vertices[] = { 0.0f, 0.707f, .0f, 0.5f, -0.5f, .0f ,-0.5f, -0.5f, .0f};
Q_ASSERT(shader->bind());
shader->setAttributeArray(posLocation, vertices, 3, 0);
glEnableVertexAttribArray(posLocation);
glDrawArrays(GL_LINES, 0, 3);
/*
GLfloat lvertices[] = { 0.907f, 0.907f, 0.5f, -0.957f, -0.957f, 0.5f };
shader->setAttributeArray(posLocation, lvertices, 0, 3);
glDrawArrays(GL_LINE_STRIP, 0, 2);
glDisableVertexAttribArray(posLocation);
*/
//glDisableVertexAttribArray(colLocation);
shader->release();
// qDebug() << getGLinfo();
}
void GLCanvas::resizeGL(int w, int h)
{
Q_UNUSED(w);
Q_UNUSED(h);
//matrix.setToIdentity();
//matrix.perspective(45.0f, w / float(h), 0.01f, 1000.0f);
glViewport(0, 0, w, h);
}
/*
void GLCanvas::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent *event)
{
Q_UNUSED(event);
}
*/
void GLCanvas::cleanup()
{
if (shader == nullptr)
return;
makeCurrent();
delete shader;
//shader = nullptr;
doneCurrent();
}
I did everything carefully from the beginning and things worked. probably a stupid mistake was the cause of it all. Thank you all.
I have Graphic scene in that i have to alight right,left,top or bottom based on first selected item(Reference Item). i searched i got some code but in this its aligning to scene right position. I have to align items based on first selected item. how can i do this?
void GraphicScene::ItemsRightAlign()
{
if (selectedItems().isEmpty())
return;
QRectF refRect = selectedItems().first()->boundingRect();
QList<QGraphicsItem*> sel =selectedItems(); // for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
QRectF itemRect = selItem->mapToScene(selItem->boundingRect()).boundingRect();
//if(align_right)
dx = refRect.right() - itemRect.right();
qDebug() << "item pos "<< dx << dy << selItem->mapToScene(selItem->boundingRect()).boundingRect() ;
selItem->moveBy(dx, dy);
}
}
For more details
Output should be like this output
The resolution method is to map the point that determines the right, left, up, down to the scene of the first item and the other items obtaining the difference that must be compensated.
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsItem>
class GraphicsScene: public QGraphicsScene{
Q_OBJECT
public:
GraphicsScene(QObject *parent=nullptr);
void moveSelecteds(Qt::Alignment aligment);
private slots:
void onSelectionChanged();
private:
void move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment);
QGraphicsItem *mRef;
};
#endif // GRAPHICSSCENE_H
graphicsscene.cpp
#include "graphicsscene.h"
GraphicsScene::GraphicsScene(QObject *parent):
QGraphicsScene(parent),
mRef(nullptr)
{
connect(this, &GraphicsScene::selectionChanged, this, &GraphicsScene::onSelectionChanged);
}
void GraphicsScene::moveSelecteds(Qt::Alignment aligment){
QList<QGraphicsItem *> its= selectedItems();
if(its.size() < 2)
return;
if(!its.removeOne(mRef))
return;
move(mRef, its, aligment);
}
void GraphicsScene::onSelectionChanged(){
if(selectedItems().isEmpty()){
mRef = nullptr;
}
else if(selectedItems().size() == 1){
mRef = selectedItems().first();
}
}
void GraphicsScene::move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment){
QPointF p;
switch (aligment) {
case Qt::AlignLeft:
p = QPointF(ref->mapToScene(ref->boundingRect().topLeft()).x(), 0);
break;
case Qt::AlignRight:
p = QPointF(ref->mapToScene(ref->boundingRect().topRight()).x(), 0);
break;
case Qt::AlignTop:
p = QPointF(0, ref->mapToScene(ref->boundingRect().topLeft()).y());
break;
case Qt::AlignBottom:
p = QPointF(0, ref->mapToScene(ref->boundingRect().bottomLeft()).y());
break;
}
for(QGraphicsItem *o: others){
QPointF delta;
switch (aligment) {
case Qt::AlignLeft:{
delta = p - QPointF(o->mapToScene(o->boundingRect().topLeft()).x(), 0);
break;
}
case Qt::AlignRight:{
delta = p - QPointF(o->mapToScene(o->boundingRect().topRight()).x(), 0);
break;
}
case Qt::AlignTop:{
delta = p - QPointF(0, o->mapToScene(o->boundingRect().topLeft()).y());
break;
}
case Qt::AlignBottom:{
delta = p - QPointF(0, o->mapToScene(o->boundingRect().bottomLeft()).y());
break;
}
}
o->moveBy(delta.x(), delta.y());
}
}
In this example you can use the up, down, left, right keys to move the items.
main.cpp
#include "graphicsscene.h"
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QShortcut>
#include <random>
static void create_items(QGraphicsScene & scene){
std::default_random_engine generator;
std::uniform_int_distribution<int> dist_size(30, 40);
std::uniform_int_distribution<int> dist_pos(-50, 50);
for(const QString & colorname : {"red", "green", "blue", "gray", "orange"}){
QRectF r(QPointF(dist_pos(generator), dist_pos(generator)),
QSizeF(dist_size(generator), dist_size(generator)));
auto item = new QGraphicsRectItem(r);
item->setPos(QPointF(dist_pos(generator), dist_pos(generator)));
item->setBrush(QColor(colorname));
item->setFlag(QGraphicsItem::ItemIsSelectable);
scene.addItem(item);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsScene scene;
create_items(scene);
QGraphicsView view(&scene);
const QList<QPair<Qt::Key, Qt::Alignment>> k_a {
{Qt::Key_Up, Qt::AlignTop},
{Qt::Key_Down, Qt::AlignBottom},
{Qt::Key_Left, Qt::AlignLeft},
{Qt::Key_Right, Qt::AlignRight}
};
for(const QPair<Qt::Key, Qt::Alignment> & p : k_a){
QShortcut *shorcut = new QShortcut(p.first, &view);
QObject::connect(shorcut, &QShortcut::activated, std::bind(&GraphicsScene::moveSelecteds, &scene, p.second));
}
view.resize(640, 480);
view.show();
return a.exec();
}
The complete example can be found in the following link.
Replace topLeft.x -- for Right align with topRight.x ,For Top Align replace topLeft.y and dx with dy,For Bottom Align replace bottomLeft.y and dx with dy
void GraphicScene::ItemsLeftAlign
{
if (selectedItems().isEmpty())
return;
QGraphicsItem *FirstSelItem = selectedItems().first();
QList<QGraphicsItem*> sel =selectedItems(); // for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
dx = (FirstSelItem->mapToScene(FirstSelItem->boundingRect().topLeft()).x()) -
(selItem->mapToScene(selItem->boundingRect().topLeft()).x());
selItem->moveBy(dx, dy);
}
}
i'm a new member and i have a question.
I want to develope a simple project with QT5 and opengl core profile for an end term project.
I first tried with a simple example but nothing happen. The screen show me only the red background but not the shader that i have write.
the code is the following:
main.cpp
#include <QApplication>
#include <QGLFormat>
#include "glwidget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//specify oepngl version and other stuff
QGLFormat glFormat;
glFormat.setVersion(4, 3);
glFormat.setProfile(QGLFormat::CoreProfile);
glFormat.setSampleBuffers(true);
GLWidget w(glFormat);
w.show();
return a.exec();
}
glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
#include <QGLBuffer>
#include <QGLShaderProgram>
#include <QOpenGLVertexArrayObject>
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
GLWidget(const QGLFormat& format, QWidget* parent = 0);
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
//virtual void keyPressEvent(QKeyEvent* e);
private:
bool prepareShaderProgram(const QString& vertexShaderPath, const QString& fragmentShaderPath);
QGLShaderProgram m_shader;
QGLBuffer m_vertexBuffer;
GLuint vertex_array_object;
};
#endif // GLWIDGET_H
glwidget.cpp
#include "glwidget.h"
GLWidget::GLWidget(const QGLFormat& format, QWidget* parent)
:QGLWidget(format, parent),
m_vertexBuffer(QGLBuffer::VertexBuffer)
{
}
void GLWidget::initializeGL()
{
QGLFormat glFormat = QGLWidget::format();
if(!glFormat.sampleBuffers())
qWarning() << "Can't enable sample buffers";
//set background color
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
//load the shaders
if(!prepareShaderProgram("/home/sera/workspace/qtWorkspace/provaShader/simple.vert",
"/home/sera/workspace/qtWorkspace/provaShader/simple.frag"))
return;
//create and bind the vertex buffer to the context
m_vertexBuffer.create();
if(!m_vertexBuffer.bind())
{
qWarning() << "could not bind vertex buffer to the opengl context";
return;
}
//bind program to the context
if(!m_shader.bind())
{
qWarning() << "could not bind shader program to the context";
return;
}
}
bool GLWidget::prepareShaderProgram(const QString &vertexShaderPath, const QString &fragmentShaderPath)
{
//load and compile shaders
bool result = m_shader.addShaderFromSourceFile(QGLShader::Vertex, vertexShaderPath);
if(!result)
qWarning() << m_shader.log();
result = m_shader.addShaderFromSourceFile(QGLShader::Fragment, fragmentShaderPath);
if(!result)
qWarning() << m_shader.log();
//link shaders into program
result = m_shader.link();
if(!result)
qWarning() << "could not link shader program" << m_shader.log();
return result;
}
void GLWidget::resizeGL(int w, int h)
{
//set the viewport
glViewport(0, 0, w, qMax(h, 1));
}
void GLWidget::paintGL()
{
//clear buffer with current color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPointSize(40.0);
glDrawArrays(GL_POINTS, 0, 1);
}
/*
void GLWidget::keyPressEvent( QKeyEvent* e )
{
switch (e->key())
{
case Qt::Key_Escape:
QCoreApplication::instance()->quit();
break;
default:
QGLWidget::keyPressEvent( e );
}
}
*/
simple.vert
#version 430 core
void main(void)
{
gl_Position = vec4(1.0, 0.0, 0.0, 1.0);
}
simple.frag
#version 430 core
out vec4 color;
void main(void)
{
color = vec4(1.0, 1.0, 1.0, 1.0);
}
And the result is this in the figure, but i'm aspected a point in the middle
result
What is wrong?
Can QGLFramebufferObject be larger than the viewport? For example, if I have a viewport of 300x300 pixels, can the fbo be 600x300? ...so one half of the fbo is shown...then applying a translation, the other half is shown. The first time make_text(); is called scaling is correct, second time the x dimension gets stretched.
main.h
#include <QGLWidget>
#include <QGLFunctions>
#include <QGLFramebufferObject>
#include <QFont>
#include <QGLShader>
class glview : public QGLWidget, protected QGLFunctions
{
Q_OBJECT
public:
explicit glview(QWidget *parent = 0);
~glview();
QSize sizeHint() const;
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
private:
void make_text(void);
QGLFramebufferObject *fbo;
QFont font;
quint32 vbo_id[1], texture_id;
QGLShaderProgram *txtovlp;
QTimer *delay;
private slots:
void refresh(void);
};
main.cpp
#include <QApplication>
#include <QPainter>
#include <QTimer>
#include "main.h"
struct txtr_vrtx {
GLfloat x;
GLfloat y;
GLfloat z;
GLfloat tx;
GLfloat ty;
}__attribute__((packed)) txtr_geo[] = {
// x, y, z, tx,ty
{0, 0, 0, 0, 0},
{0, 3, 0, 0, 1},
{6, 3, 0, 1, 1},
{6, 0, 0, 1, 0},
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
glview widget;
widget.show();
return app.exec();
}
glview::glview(QWidget *parent) : QGLWidget(parent)
{
fbo = NULL;
font.setFamily("Helvetica");
delay = new QTimer;
delay->start(2000);
connect(delay, SIGNAL(timeout()), this, SLOT(refresh()));
}
glview::~glview()
{
delete fbo;
}
void glview::refresh(void)
{
delay->stop();
qDebug() << "refresh fired";
make_text();
updateGL();
}
void glview::make_text(void)
{
glBindBuffer(GL_ARRAY_BUFFER, 0); // must unbind for QPainter
glEnable(GL_TEXTURE_2D);
if (fbo)
delete fbo;
makeCurrent();
fbo = new QGLFramebufferObject(600, 300, GL_TEXTURE_2D);
fbo->bind();
texture_id = fbo->texture();
QPainter painter(fbo);
font.setPointSize(20);
painter.setFont(font);
painter.eraseRect(0,0,600,300);
painter.setPen(Qt::blue);
painter.drawText(100, 140, "FBO");
painter.setPen(Qt::red);
painter.drawText(400, 140, "FBO");
painter.end();
fbo->release();
}
QSize glview::sizeHint() const
{
return QSize(300, 300);
}
void glview::initializeGL()
{
initializeGLFunctions();
qglClearColor(Qt::white);
QGLShader *txtovlp_vshader = new QGLShader(QGLShader::Vertex, this);
const char *txtovlp_vsrc =
"attribute highp vec4 vertex;\n"
"attribute mediump vec2 texCoord;\n"
"varying mediump vec2 texc;\n"
"uniform mediump mat4 matrix;\n"
"void main(void)\n"
"{\n"
" gl_Position = matrix * vertex;\n"
" texc = texCoord;\n"
"}\n";
txtovlp_vshader->compileSourceCode(txtovlp_vsrc);
QGLShader *txtovlp_fshader = new QGLShader(QGLShader::Fragment, this);
const char *txtovlp_fsrc =
"uniform sampler2D texture;\n"
"varying mediump vec2 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc.st);\n"
"}\n";
txtovlp_fshader->compileSourceCode(txtovlp_fsrc);
txtovlp = new QGLShaderProgram(this);
txtovlp->addShader(txtovlp_vshader);
txtovlp->addShader(txtovlp_fshader);
txtovlp->link();
glGenBuffers(1, vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(txtr_geo), txtr_geo, GL_STATIC_DRAW);
make_text();
glEnable(GL_DEPTH_TEST);
}
void glview::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void glview::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix;
matrix.ortho(0, 3, 0, 3, -1, 1);
//matrix.translate(-3,0,0);
txtovlp->bind();
txtovlp->setUniformValue("matrix", matrix);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id[0]);
glBindTexture(GL_TEXTURE_2D, texture_id);
int txtr_vertexLocation = txtovlp->attributeLocation("vertex");
txtovlp->enableAttributeArray(txtr_vertexLocation);
glVertexAttribPointer(txtr_vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(struct txtr_vrtx), 0);
int texCoordLocation = txtovlp->attributeLocation("texCoord");
txtovlp->enableAttributeArray(texCoordLocation);
glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(struct txtr_vrtx), ((char*)NULL + 12));
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glFlush();
}
QPainter trashes the GL context and I've learned that also includes the viewport. By adding glViewport(0, 0, width(), height()); at the end of make_text() (after QPainter is finished) restores the viewport for the next paint event.
The application compiles and runs with no compiler errors however no Q logo is displayed.
I have taken the QT Logo OpenGL example http://qt.developpez.com/doc/4.7/opengl-hellogl/
and attempted to compile in QT version 5.2.1 (MSVC 2010, 32bit) . I am aware that there is a change in the way OpenGL is used in version 5 from the older version 4. I have modified some of the code from a working GL example and it still does not work. My bet is the problem exists in the QtLogo because I have changed nothing in those files. So I am completely puzzled being a newbie to OpenGL.
I hope to have provided a descent problem statement. Answering this will help so many others unlock the mysteries of QT OpenGL changes. It is so frustrating.
1) QT version 5.2.1 (MSVC 2010, 32bit) - Installed with Qt 5.2.1 for Windows 32-bit (MinGW 4.8, OpenGL, 634 MB) installer
2) Windows 7, tried 32bit and 64bit
3) Intel CPUs (used two different Intel based computers)
4) The application does work correctly in QT version 4
5) The code is available for download http://1drv.ms/1g88ScS
Here is the code:
hellogl.pro
# HELLOGL
#VPATH += ../shared
#INCLUDEPATH += ../shared
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = hellogl
TEMPLATE = app
SOURCES += main.cpp \
window.cpp \
glwidget.cpp \
qtlogo.cpp
HEADERS += window.h \
glwidget.h \
qtlogo.h
FORMS += window.ui
GLWIDGET.H
// glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
class QtLogo;
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit GLWidget(QWidget *parent = 0);
~GLWidget();
QSize minimumSizeHint() const;
QSize sizeHint() const;
public slots:
void setXRotation(int angle);
void setYRotation(int angle);
void setZRotation(int angle);
signals:
void xRotationChanged(int angle);
void yRotationChanged(int angle);
void zRotationChanged(int angle);
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
QtLogo *logo;
int xRot;
int yRot;
int zRot;
QPoint lastPos;
QColor qtGreen;
QColor qtPurple;
};
#endif
qtlogo.h
//qtlogo.h
#ifndef QTLOGO_H
#define QTLOGO_H
//#include <QObject>
//#include <QtOpenGL>
#include <QColor>
#include <GL/gl.h>
//#include <QGLWidget>
//#include <QtWidgets>
#include <QMatrix4x4>
#include <QVector3D>
#include <qmath.h>
class Patch;
class Geometry;
//! [0]
class QtLogo : public QObject
{
public:
QtLogo(QObject *parent, int d = 64, qreal s = 1.0);
~QtLogo();
void setColor(QColor c);
void draw() const;
private:
void buildGeometry(int d, qreal s);
QList<Patch *> parts;
Geometry *geom;
};
//! [0]
#endif // QTLOGO_H
window.h
// window.h
#ifndef WINDOW_H
#define WINDOW_H
//#include <QObject>
//#include <QApplication>
//#include <QHBoxLayout>
//#include <QSpinBox>
//#include <QKeyEvent>
#include <QWidget>
#include <QSlider>
class QSlider;
class GLWidget;
namespace Ui {
class Window;
}
class Window : public QWidget
{
Q_OBJECT
public:
explicit Window(QWidget *parent = 0);
~Window();
protected:
void keyPressEvent(QKeyEvent *event);
private:
Ui::Window *ui;
QSlider *createSlider();
GLWidget *glWidget;
QSlider *xSlider;
QSlider *ySlider;
QSlider *zSlider;
};
#endif
glwidget.cpp
// glwidget.cpp
#include <math.h>
#include <QtWidgets>
#include <QtOpenGL>
#include "glwidget.h"
#include "qtlogo.h"
//#include <QColor>
/*
#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE 0x809D
#endif
*/
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
logo = 0;
xRot = 0;
yRot = 0;
zRot = 0;
qtGreen = QColor::fromCmykF(0.40, 0.0, 1.0, 0.0);
qtPurple = QColor::fromCmykF(0.39, 0.39, 0.0, 0.0);
}
GLWidget::~GLWidget()
{
}
QSize GLWidget::minimumSizeHint() const
{
return QSize(50, 50); //
}
QSize GLWidget::sizeHint() const
{
return QSize(800, 800);
}
static void qNormalizeAngle(int &angle)
{
while (angle < 0)
angle += 360 * 16;
while (angle > 360 * 16)
angle -= 360 * 16;
}
void GLWidget::setXRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != xRot) {
xRot = angle;
emit xRotationChanged(angle);
updateGL();
}
}
void GLWidget::setYRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != yRot) {
yRot = angle;
emit yRotationChanged(angle);
updateGL();
}
}
void GLWidget::setZRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != zRot) {
zRot = angle;
emit zRotationChanged(angle);
updateGL();
}
}
void GLWidget::initializeGL()
{
qglClearColor(qtPurple.dark());
logo = new QtLogo(this, 64);
logo->setColor(qtGreen.dark());
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_MULTISAMPLE);
static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0, 0.0, -10.0);
glRotatef(xRot / 16.0, 1.0, 0.0, 0.0);
glRotatef(yRot / 16.0, 0.0, 1.0, 0.0);
glRotatef(zRot / 16.0, 0.0, 0.0, 1.0);
logo->draw();
//logo->setColor(QColor(0,0,0));
}
void GLWidget::resizeGL(int width, int height)
{
int side = qMin(width, height);
glViewport((width - side) / 2, (height - side) / 2, side, side);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
#ifdef QT_OPENGL_ES_1
glOrthof(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#else
glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#endif
glMatrixMode(GL_MODELVIEW);
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if (event->buttons() & Qt::LeftButton) {
setXRotation(xRot + 8 * dy);
setYRotation(yRot + 8 * dx);
} else if (event->buttons() & Qt::RightButton) {
setXRotation(xRot + 8 * dy);
setZRotation(zRot + 8 * dx);
}
lastPos = event->pos();
}
main.cpp
// main.cpp
#include <QtOpenGL>
#include <QtGui>
#include <QApplication>
#include <QDesktopWidget>
#include "window.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.resize(window.sizeHint());
int desktopArea = QApplication::desktop()->width() *
QApplication::desktop()->height();
int widgetArea = window.width() * window.height();
if (((float)widgetArea / (float)desktopArea) < 0.75f)
window.show();
else
window.showMaximized();
return app.exec();
}
qtlogo.cpp
// qtlogo.cpp
#include "qtlogo.h"
static const qreal tee_height = 0.311126;
static const qreal cross_width = 0.25;
static const qreal bar_thickness = 0.113137;
static const qreal inside_diam = 0.20;
static const qreal outside_diam = 0.30;
static const qreal logo_depth = 0.10;
static const int num_divisions = 32;
//! [0]
struct Geometry
{
QVector<GLushort> faces;
QVector<QVector3D> vertices;
QVector<QVector3D> normals;
void appendSmooth(const QVector3D &a, const QVector3D &n, int from);
void appendFaceted(const QVector3D &a, const QVector3D &n);
void finalize();
void loadArrays() const;
};
//! [0]
//! [1]
class Patch
{
public:
enum Smoothing { Faceted, Smooth };
Patch(Geometry *);
void setSmoothing(Smoothing s) { sm = s; }
void translate(const QVector3D &t);
void rotate(qreal deg, QVector3D axis);
void draw() const;
void addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n);
void addQuad(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &d);
GLushort start;
GLushort count;
GLushort initv;
GLfloat faceColor[4];
QMatrix4x4 mat;
Smoothing sm;
Geometry *geom;
};
//! [1]
static inline void qSetColor(float colorVec[], QColor c)
{
colorVec[0] = c.redF();
colorVec[1] = c.greenF();
colorVec[2] = c.blueF();
colorVec[3] = c.alphaF();
}
void Geometry::loadArrays() const
{
glVertexPointer(3, GL_FLOAT, 0, vertices.constData());
glNormalPointer(GL_FLOAT, 0, normals.constData());
}
void Geometry::finalize()
{
// TODO: add vertex buffer uploading here
// Finish smoothing normals by ensuring accumulated normals are returned
// to length 1.0.
for (int i = 0; i < normals.count(); ++i)
normals[i].normalize();
}
void Geometry::appendSmooth(const QVector3D &a, const QVector3D &n, int from)
{
// Smooth normals are acheived by averaging the normals for faces meeting
// at a point. First find the point in geometry already generated
// (working backwards, since most often the points shared are between faces
// recently added).
int v = vertices.count() - 1;
for ( ; v >= from; --v)
if (qFuzzyCompare(vertices[v], a))
break;
if (v < from)
{
// The vert was not found so add it as a new one, and initialize
// its corresponding normal
v = vertices.count();
vertices.append(a);
normals.append(n);
}
else
{
// Vert found, accumulate normals into corresponding normal slot.
// Must call finalize once finished accumulating normals
normals[v] += n;
}
// In both cases (found or not) reference the vert via its index
faces.append(v);
}
void Geometry::appendFaceted(const QVector3D &a, const QVector3D &n)
{
// Faceted normals are achieved by duplicating the vert for every
// normal, so that faces meeting at a vert get a sharp edge.
int v = vertices.count();
vertices.append(a);
normals.append(n);
faces.append(v);
}
Patch::Patch(Geometry *g)
: start(g->faces.count())
, count(0)
, initv(g->vertices.count())
, sm(Patch::Smooth)
, geom(g)
{
qSetColor(faceColor, QColor(Qt::darkGray));
}
void Patch::rotate(qreal deg, QVector3D axis)
{
mat.rotate(deg, axis);
}
void Patch::translate(const QVector3D &t)
{
mat.translate(t);
}
static inline void qMultMatrix(const QMatrix4x4 &mat)
{
if (sizeof(qreal) == sizeof(GLfloat))
glMultMatrixf((GLfloat*)mat.constData());
#ifndef QT_OPENGL_ES
else if (sizeof(qreal) == sizeof(GLdouble))
glMultMatrixd((GLdouble*)mat.constData());
#endif
else
{
GLfloat fmat[16];
GLfloat const *r = mat.constData();
for (int i = 0; i < 16; ++i)
fmat[i] = r[i];
glMultMatrixf(fmat);
}
}
//! [2]
void Patch::draw() const
{
glPushMatrix();
qMultMatrix(mat);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, faceColor);
const GLushort *indices = geom->faces.constData();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, indices + start);
glPopMatrix();
}
//! [2]
void Patch::addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n)
{
QVector3D norm = n.isNull() ? QVector3D::normal(a, b, c) : n;
if (sm == Smooth)
{
geom->appendSmooth(a, norm, initv);
geom->appendSmooth(b, norm, initv);
geom->appendSmooth(c, norm, initv);
}
else
{
geom->appendFaceted(a, norm);
geom->appendFaceted(b, norm);
geom->appendFaceted(c, norm);
}
count += 3;
}
void Patch::addQuad(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &d)
{
QVector3D norm = QVector3D::normal(a, b, c);
if (sm == Smooth)
{
addTri(a, b, c, norm);
addTri(a, c, d, norm);
}
else
{
// If faceted share the two common verts
addTri(a, b, c, norm);
int k = geom->vertices.count();
geom->appendSmooth(a, norm, k);
geom->appendSmooth(c, norm, k);
geom->appendFaceted(d, norm);
count += 3;
}
}
static inline QVector<QVector3D> extrude(const QVector<QVector3D> &verts, qreal depth)
{
QVector<QVector3D> extr = verts;
for (int v = 0; v < extr.count(); ++v)
extr[v].setZ(extr[v].z() - depth);
return extr;
}
class Rectoid
{
public:
void translate(const QVector3D &t)
{
for (int i = 0; i < parts.count(); ++i)
parts[i]->translate(t);
}
void rotate(qreal deg, QVector3D axis)
{
for (int i = 0; i < parts.count(); ++i)
parts[i]->rotate(deg, axis);
}
// No special Rectoid destructor - the parts are fetched out of this member
// variable, and destroyed by the new owner
QList<Patch*> parts;
};
class RectPrism : public Rectoid
{
public:
RectPrism(Geometry *g, qreal width, qreal height, qreal depth);
};
RectPrism::RectPrism(Geometry *g, qreal width, qreal height, qreal depth)
{
enum { bl, br, tr, tl };
Patch *fb = new Patch(g);
fb->setSmoothing(Patch::Faceted);
// front face
QVector<QVector3D> r(4);
r[br].setX(width);
r[tr].setX(width);
r[tr].setY(height);
r[tl].setY(height);
QVector3D adjToCenter(-width / 2.0, -height / 2.0, depth / 2.0);
for (int i = 0; i < 4; ++i)
r[i] += adjToCenter;
fb->addQuad(r[bl], r[br], r[tr], r[tl]);
// back face
QVector<QVector3D> s = extrude(r, depth);
fb->addQuad(s[tl], s[tr], s[br], s[bl]);
// side faces
Patch *sides = new Patch(g);
sides->setSmoothing(Patch::Faceted);
sides->addQuad(s[bl], s[br], r[br], r[bl]);
sides->addQuad(s[br], s[tr], r[tr], r[br]);
sides->addQuad(s[tr], s[tl], r[tl], r[tr]);
sides->addQuad(s[tl], s[bl], r[bl], r[tl]);
parts << fb << sides;
}
class RectTorus : public Rectoid
{
public:
RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int numSectors);
};
RectTorus::RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int k)
{
QVector<QVector3D> inside;
QVector<QVector3D> outside;
for (int i = 0; i < k; ++i) {
qreal angle = (i * 2 * M_PI) / k;
inside << QVector3D(iRad * qSin(angle), iRad * qCos(angle), depth / 2.0);
outside << QVector3D(oRad * qSin(angle), oRad * qCos(angle), depth / 2.0);
}
inside << QVector3D(0.0, iRad, 0.0);
outside << QVector3D(0.0, oRad, 0.0);
QVector<QVector3D> in_back = extrude(inside, depth);
QVector<QVector3D> out_back = extrude(outside, depth);
// Create front, back and sides as separate patches so that smooth normals
// are generated for the curving sides, but a faceted edge is created between
// sides and front/back
Patch *front = new Patch(g);
for (int i = 0; i < k; ++i)
front->addQuad(outside[i], inside[i],
inside[(i + 1) % k], outside[(i + 1) % k]);
Patch *back = new Patch(g);
for (int i = 0; i < k; ++i)
back->addQuad(in_back[i], out_back[i],
out_back[(i + 1) % k], in_back[(i + 1) % k]);
Patch *is = new Patch(g);
for (int i = 0; i < k; ++i)
is->addQuad(in_back[i], in_back[(i + 1) % k],
inside[(i + 1) % k], inside[i]);
Patch *os = new Patch(g);
for (int i = 0; i < k; ++i)
os->addQuad(out_back[(i + 1) % k], out_back[i],
outside[i], outside[(i + 1) % k]);
parts << front << back << is << os;
}
QtLogo::QtLogo(QObject *parent, int divisions, qreal scale)
: QObject(parent)
, geom(new Geometry())
{
buildGeometry(divisions, scale);
}
QtLogo::~QtLogo()
{
qDeleteAll(parts);
delete geom;
}
void QtLogo::setColor(QColor c)
{
for (int i = 0; i < parts.count(); ++i)
qSetColor(parts[i]->faceColor, c);
}
//! [3]
void QtLogo::buildGeometry(int divisions, qreal scale)
{
qreal cw = cross_width * scale;
qreal bt = bar_thickness * scale;
qreal ld = logo_depth * scale;
qreal th = tee_height *scale;
RectPrism cross(geom, cw, bt, ld);
RectPrism stem(geom, bt, th, ld);
QVector3D z(0.0, 0.0, 1.0);
cross.rotate(45.0, z);
stem.rotate(45.0, z);
qreal stem_downshift = (th + bt) / 2.0;
stem.translate(QVector3D(0.0, -stem_downshift, 0.0));
RectTorus body(geom, 0.20, 0.30, 0.1, divisions);
parts << stem.parts << cross.parts << body.parts;
geom->finalize();
}
//! [3]
//! [4]
void QtLogo::draw() const
{
geom->loadArrays();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
for (int i = 0; i < parts.count(); ++i)
parts[i]->draw();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
//! [4]
window.cpp
// window.cpp
#include <QtWidgets>
#include "window.h"
#include "ui_window.h"
#include "glwidget.h"
#include <QSlider>
Window::Window(QWidget *parent) :
QWidget(parent),
ui(new Ui::Window)
{
ui->setupUi(this);
//glWidget = new GLWidget;
glWidget = ui->widget;
xSlider = createSlider();
ySlider = createSlider();
zSlider = createSlider();
connect(xSlider, SIGNAL(valueChanged(int)), glWidget, SLOT(setXRotation(int)));
connect(glWidget, SIGNAL(xRotationChanged(int)), xSlider, SLOT(setValue(int)));
connect(ySlider, SIGNAL(valueChanged(int)), glWidget, SLOT(setYRotation(int)));
connect(glWidget, SIGNAL(yRotationChanged(int)), ySlider, SLOT(setValue(int)));
connect(zSlider, SIGNAL(valueChanged(int)), glWidget, SLOT(setZRotation(int)));
connect(glWidget, SIGNAL(zRotationChanged(int)), zSlider, SLOT(setValue(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(glWidget);
mainLayout->addWidget(xSlider);
mainLayout->addWidget(ySlider);
mainLayout->addWidget(zSlider);
setLayout(mainLayout);
xSlider->setValue(15 * 16);
ySlider->setValue(345 * 16);
zSlider->setValue(0 * 16);
setWindowTitle(tr("Hello GL"));
}
QSlider *Window::createSlider()
{
QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(0, 360 * 16);
slider->setSingleStep(16);
slider->setPageStep(15 * 16);
slider->setTickInterval(15 * 16);
slider->setTickPosition(QSlider::TicksRight);
return slider;
}
Window::~Window()
{
delete ui;
}
void Window::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Escape)
close();
else
QWidget::keyPressEvent(e);
}
:-) I have nailed it!
The problem was: The Q Logo does not appear in QT5. Although it does work in QT4.
After spending weeks on this problem, googling for hours, going through the hassle of compiling QT many many times, I have finally solved the problem with this code and the mystery of opengl_es and ANGLE
So for those of us completely baffled as to why QT OpenGL examples code prior to QT5 may not work or work properly, it could be the same issue as this code.
Basically comment out or omit references to QT_OPENGL_ES.
In this hello world Q example:
Comment this out as shown contained in the Qlogo.cpp file
/*
#ifndef QT_OPENGL_ES
else if (sizeof(qreal) == sizeof(GLdouble))
glMultMatrixd((GLdouble*)mat.constData());
#endif
*/
Lots of code lovin' to you all.