In the Qt Forums I found this question and sparkled my curiosity.
I found a very simple example in Qt to display a cube and modified the cube logic to create a cube with a side length of 1 unit.
Then I tried to click on the model and show the coordinates of the area where I clicked.
When there are no rotations involved, it seems that the picking works fine. But! If I un-comment the rotations (or one of them) in paintGL
I end up with "wrong" values.
For example:
- No rotations and click almost on the leftmost border of the cube: the
gluUnProject
method says that I clicked on the point{ -0.49075, 0.234, 0.5 }
, which seems ok.
- Rotations enabled and click almost on the leftmost border of the cube: I receive the point
{ -0.501456, 0.157555, -0.482942 }
, which seems wrong. Thex
coordinate is off its range of-0.5, 0.5
.
I think the coordinates transformation is okay. I've been researching through Google and the people always use the same piece of code. Moreover, the color information of the pixel matches the color of the face where I clicked.
So, can anyone tell me why when a rotation is involved I get wrong coordinates? I think I'm failing in some basic 3D understanding, but I'm not able to realize where it is.
Here's the code:
main.cpp:
#include <QApplication>
#include "GLCube.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GLCube w;
w.resize(800,600);
w.show();
return a.exec();
}
GLCube.h
#ifndef GLCUBE_H
#define GLCUBE_H
#include <QtOpenGL>
#include <QGLWidget>
class GLCube : public QGLWidget{
Q_OBJECT // must include this if you use Qt signals/slots
public:
GLCube(QWidget *parent = NULL)
: QGLWidget(parent) {
}
protected:
// Set up the rendering context, define display lists etc.:
void initializeGL();
// draw the scene:
void paintGL();
// setup viewport, projection etc.:
void resizeGL (int width, int height);
virtual void mousePressEvent(QMouseEvent *pme);
};
#endif
GLCube.cpp:
#include "GLCube.h"
#include <cmath>
#include <iostream>
#include <iomanip>
#include "GL/glu.h"
namespace {
float ver[8][3] =
{
{ 0.5, -0.5, 0.5 },
{ -0.5, -0.5, 0.5 },
{ -0.5, -0.5, -0.5 },
{ 0.5, -0.5, -0.5 },
{ 0.5, 0.5, 0.5 },
{ -0.5, 0.5, 0.5 },
{ -0.5, 0.5, -0.5 },
{ +0.5, 0.5, -0.5 }
};
GLfloat color[8][4] =
{
{0.0,0.0,0.0, 1.0},
{1.0,0.0,0.0, 1.0 },
{1.0,1.0,0.0, 1.0 },
{0.0,1.0,0.0, 1.0 },
{0.0,0.0,1.0, 1.0 },
{1.0,0.0,1.0, 1.0 },
{1.0,1.0,1.0, 1.0 },
{0.0,1.0,1.0, 1.0 },
};
void quad(int a,int b,int c,int d, int col)
{
glPointSize( 5 );
glBegin(GL_POINTS);
glColor4fv(color[1]);
glVertex3fv(ver[a]);
glColor4fv(color[2]);
glVertex3fv(ver[b]);
glColor4fv(color[3]);
glVertex3fv(ver[c]);
glColor4fv(color[4]);
glVertex3fv(ver[d]);
glEnd();
glBegin(GL_LINES);
glColor4fv(color[1]);
glVertex3fv(ver[a]);
glVertex3fv(ver[b]);
glColor4fv(color[1]);
glVertex3fv(ver[b]);
glVertex3fv(ver[c]);
glColor4fv(color[1]);
glVertex3fv(ver[c]);
glVertex3fv(ver[d]);
glColor4fv(color[1]);
glVertex3fv(ver[d]);
glVertex3fv(ver[a]);
glEnd();
glBegin(GL_QUADS);
glColor4fv(/*color[a]*/ color[col] );
glVertex3fv(ver[a]);
// glColor3fv(color[b]);
glVertex3fv(ver[b]);
// glColor3fv(color[c]);
glVertex3fv(ver[c]);
// glColor3fv(color[d]);
glVertex3fv(ver[d]);
glEnd();
}
void colorcube()
{
// glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
quad( 3, 2, 1, 0, 1 ); // bottom
quad( 7, 6, 5, 4, 2); // top
quad( 6, 2, 1, 5, 3 ); // front
quad( 7, 3, 0, 4, 5 ); // back
quad( 7, 6, 2, 3, 6 ); // left
quad( 4, 5, 1, 0, 7); // right
}
}
/*
* Sets up the OpenGL rendering context, defines display lists, etc.
* Gets called once before the first time resizeGL() or paintGL() is called.
*/
void GLCube::initializeGL(){
//activate the depth buffer
glEnable(GL_DEPTH_TEST);
qglClearColor(Qt::black);
glEnable(GL_CULL_FACE);
}
/*
* Sets up the OpenGL viewport, projection, etc. Gets called whenever the widget has been resized
* (and also when it is shown for the first time because all newly created widgets get a resize event automatically).
*/
void GLCube::resizeGL (int width, int height){
glViewport( 0, 0, (GLint)width, (GLint)height );
/* create viewing cone with near and far clipping planes */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum( -1.0, 1.0, -1.0, 1.0, 15.0, 30.0);
glMatrixMode( GL_MODELVIEW );
}
/*
* Renders the OpenGL scene. Gets called whenever the widget needs to be updated.
*/
void GLCube::paintGL(){
//delete color and depth buffer
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-20.0f); //move along z-axis
glRotatef(30.0,0.0,1.0,0.0); //rotate 30 degress around y-axis
glRotatef(15.0,1.0,0.0,0.0); //rotate 15 degress around x-axis
colorcube();
// originalcube();
}
void GLCube::mousePressEvent(QMouseEvent *pme) {
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
const int x = pme->x();
const int y = viewport[3] - pme->y();
qDebug() << "HERE: " << x << y;
GLfloat color[4];
glReadPixels( x, y, 1, 1, GL_RGBA, GL_FLOAT, color);
GLenum error = glGetError();
std::cout << "RETRIEVED COLOR:" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << std::endl;
printf( "\tERROR: %s (Code: %u)\n", gluErrorString(error), error );
if(GL_NO_ERROR != error) throw;
GLdouble depthScale;
glGetDoublev( GL_DEPTH_SCALE, &depthScale );
std::cout << "DEPTH SCALE: " << depthScale << std::endl;
GLfloat z;
glReadPixels( x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z );
error = glGetError();
std::cout << "X: " << x << ", Y: " << y << ", RETRIEVED Z: " << z << std::endl;
printf( "\tERROR: %s (Code: %u)\n", gluErrorString(error), error );
if(GL_NO_ERROR != error) throw;
std::cout << std::endl << std::endl;
GLdouble posX, posY, posZ;
GLint result;
result = gluUnProject( x, y, z, modelview, projection, viewport, &posX, &posY, &posZ);
error = glGetError();
std::cout << "3D point with POS: " << posX << " " << posY << " " << posZ << std::endl;
printf( "\tERROR: %s (Code: %u)\n", gluErrorString(error), error );
std::cout << "\tglUnProject: " << (( GL_FALSE == result ) ? "FALSE" : "TRUE") << std::endl;
if(GL_NO_ERROR != error) throw;
}
NOTE:
I'm running this in Windows 8.1 64 bit, Qt 4.8 and MinGW.
Also, both glReadPixels
and gluUnProject
exit without errors (ERROR CODE = GL_NO_ERROR
)
And glut
is not available. Only what OpenGL
, QtOpenGL
and/or glu
offer.