PDA

View Full Version : QGraphicsScene Widgets in OpenGL Projection with only a tiny problem



JohannesMunk
6th April 2009, 21:03
Hi there!

This post is mainly to share my efforts of about a weeks time.. to get real 3-dimensional positioning of widgets working. I didn't find anything similar and maybe somebody is trying to achieve the same thing. (The WolfenQT example uses its own transformation and not the opengl setup.)

There is, however, one small problem left. I can't get text mouse selection to work. Everything is fully functional, but when I try to range-select text (mouse down, move, up) it won't do. I think it has something to do with my coordinate transformation.. somehow its still not exactly right, when it comes to orientation.

The crucial function is CGL3dProjectedItem:UpdateTransformation(CGLCamera* cam):


void CGL3dProjectedItem::UpdateTransformation(CGLCamera * cam)
{
//GLfloat projection[16];
GLfloat camera[16];
// Save Modelmatrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// use gl to perform the matrix multiplications. using modelview matrix as temp.. Start fresh
glLoadIdentity();
// Multiply camera Matrices
cam->GetTransformation(camera);
glMultMatrixf(camera);
// Item placement..
glTranslated(Position().x(),Position().y(),Positio n().z());
glTranslated(CenterPos().x(),CenterPos().y(),Cente rPos().z());
// Item rotation..
if (Angle().x() != 0) glRotatef(Angle().x(),1,0,0);
if (Angle().y() != 0) glRotatef(Angle().y(),0,1,0);
if (Angle().z() != 0) glRotatef(Angle().z(),0,0,1);
// Get final gl Matrix..
glGetFloatv(GL_MODELVIEW_MATRIX , _trans);

// At this point.. the coordinates are not yet normalized. m33 >> 1.
// Normalization will be performed in the QTransform (w' = m13*x + m23*y + m33 | x' /= w' | y' /= w')
QTransform trans = GetQTrans(_trans);

setTransform(trans);

// Restore previous gl matrix
glPopMatrix();

prepareGeometryChange();
_bounds = shape().boundingRect();

CGL3d dist = cam->Position() - Position();
setZValue(-dist.Length());
}

with cam->GetTransformation(camera):


void CGLCamera::_SetupProjection()
{
GLint vp[4];
glGetIntegerv(GL_VIEWPORT,vp);
gluPerspective(FOV(), (float)vp[2]/(float)vp[3], 0.01f, 100000.0f);
}

void CGLCamera::_SetupView()
{
gluLookAt(ex3(_position),ex3(_lookAt),ex3(_up));
}

// Returns product of projection and modelview matrix
void CGLCamera::GetTransformation(GLfloat* m)
{
if (_PerspectiveChanged == true)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
_SetupProjection();
_SetupView();
glGetFloatv(GL_MODELVIEW_MATRIX , m);
memcpy(_PerspectiveMatrix ,m,16 * sizeof *m);
glPopMatrix();
_PerspectiveChanged = false;
}
else {
memcpy(m,_PerspectiveMatrix,16* sizeof*m);
}
}


and GetQTrans:



QTransform GetQTrans(GLfloat* m)
{
// In QTransform the dx and dy are stored in m31 and m32. which is nuts.
// In 4x4 GL they are stored in 03 and 13, so that when you multiply
// with a homogenous (x,y,z,1) vector you get them added automatically.

// Build QTransform of this matrix. QTransform 3x3 recieves the values per row,
// whereas GL saves the 4x4 matrix per column.
// m11 m12 m13 1 2 3 m00 m10 m30 0 1 3
// QT: m21 m22 m23 4 5 6 GL: m01 m11 m31 4 5 7
// dx dy m33 7 8 9 m03 m13 m33 12 13 15

// AAH! QTransform does not use m33, unless the transformation is not affine, m13<>0 and m23 <> 0
QTransform trans;
if (qFuzzyCompare(m[3] + 1, 1) && qFuzzyCompare(m[7] + 1, 1))
trans = QTransform(m[0]/m[15], m[1]/m[15], m[4]/m[15], m[5]/m[15], m[12]/m[15], m[13]/m[15]);
else
trans = QTransform(m[0], m[1], m[3], m[4], m[5], m[7], m[12], m[13], m[15]);
return trans;
}


That was the Item->Scene transformation. Scene->View transformation looks like that:


void resizeEvent(QResizeEvent *event)
{
cgl->makeCurrent();
glViewport(0, 0, (GLint)event->size().width(), (GLint)event->size().height());
// Get Viewport parameters
GLint vp[4];
glGetIntegerv(GL_VIEWPORT,vp);
// Final viewport transformation.
QTransform vtrans(vp[2]*0.5, 0,
0, -vp[3]*0.5,
vp[0]+vp[2]*0.5,vp[1]+vp[3]*0.5);
setTransform(vtrans);
scene()->setSceneRect(QRect(-1,-1,2,2));
}


Note the negative sign for the y-component. Needed to turn OpenGLs upside-down into common upperleft orientation.

I copy pasted the code from a bigger project and created a little test environment for you to explore.

There are five units included in the zip:


widget3d:

CGL3dProjectedItem (base class for the gl transformation),
CGL3dProxyWidget (new proxy for correct placement and drawing),
CGLView (handles the opengl widget and viewport)
CGLScene (scene, camera, mouse and keyboard)

Graphics: OpenGL-Camera, some drawing, some materials..
mathext: 3 and 4-dim vectors, unitycircle, arctan, ..
main.cpp: very small test program
test.h: 3dProjectedItem (base class for the gl transformation),


You will need glee and glu in order to compile.

http://upload.convis.info/Data/Widget3d.jpg

Usage: Mouse move moves the lookat vector. Hold down <Shift> to increase speed.
Same goes for WASD+QE to move around. Movement is deactivated when a widget is focussed.

On the way to solve the selection problem.. I visualized myself the axes orientations. The blue box with the arrows indicates them. It is drawn in an item.paint, with the painter setup to the projected transformation by the scene. The bigger arrows are then drawn with opengl and the small ones with the painter. When I draw the axes in Drawbackground purely OpenGL I get the same matching directions. Then the blue z-axis is extended too. I guess the scene uses some ortho2d projection when painting the items.. So the mere axes orientation is probably not the problem.

Looking forward to hear your comments! And thx in advance for any hint with the selection problem!

Joh

JohannesMunk
2nd September 2009, 14:07
FINALLY!!

http://upload.convis.info/Data/Widget3d_2.jpg

So stupid: Only thing wrong was, that I didn't call the inherited QGraphicsscene::MouseMove handler..

Now everything works just fine!

Code is still a bit messy, but should help to understand the concepts anyway.

Hope it helps someone!

Johannes