PDA

View Full Version : OpenGL Rotation with only left button



danielperaza
3rd April 2011, 23:45
Hi!, I'm new to OpenGL programming, I'm working in my thesis right now, and I followed the Tetrahedron example. I was able to do some stuff from that point on.

The problem is that the Tetrahedron source code handles rotation by two different ways: it rotates around the XY axis if the left mouse button is pressed, or the XZ axis otherwise. What I'd like to do is to handle rotation only with the left mouse button, and leave the right mouse button to trigger translations.

Also, I'd like to perform zooming when an user makes double click with the left button.

Isn't any OpenGL-based widget library that I might use to implement these controls? I'd like to do these operations the way GoogleEarth does.

JohannesMunk
4th April 2011, 02:31
GoogleEarth features a trackball mechanism. Have a look at Qt's boxes (http://doc.trolltech.com/latest/demos-boxes.html) demo.

The spherical trackball should be what you are looking for.

http://doc.trolltech.com/latest/demos-boxes-trackball-h.html
http://doc.trolltech.com/latest/demos-boxes-trackball-cpp.html

For the rest (zooming, translating) you will have to get your hands dirty and implement your wishes into the appropriate mouseEvents.

Let us know if you need help with a more specific problem.

HIH

Johannes

danielperaza
9th April 2011, 22:50
Ok, I have reviewed the Boxes example. However, since it has a lot of stuff that I don't even need to know at this time, I tried to keep focused at the TrackBall class. I took a look to http://www.opengl.org/wiki/Trackball too, and I have a basic understanding of how the theory works. But I'm still needing a hand on implementation, becasue I'm very confused about it.

Based on the Scene.cpp file, I tried to find out how to use the TrackBall in my code, so I tried to build a test project, handling the rotation by these functions:



void Visualizer::mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.move(e->posF(), this->_trackball.rotation().conjugate());
updateGL();
}
}

void Visualizer::mousePressEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.push(e->posF(), this->_trackball.rotation().conjugate());
}
}

void Visualizer::mouseReleaseEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.release(e->posF(), this->_trackball.rotation().conjugate());
}
}


and in my draw() method (which is called from paintGL()) I tried this:



glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
QQuaternion q = this->_trackball.rotation();
glRotatef(180.0*q.scalar() / PI, q.x(), q.y(), q.z());


And it didn't work. Sorry if this question seems to be too stupid to ask :S, but remember that I'm new to Computer Graphics in general.

Any suggestions?

JohannesMunk
9th April 2011, 23:00
Could you post your complete test code?

Joh

danielperaza
10th April 2011, 00:14
Here is my test project.

6214

JohannesMunk
10th April 2011, 00:56
Thx. Saved me to recreate all that!

I didn't solve the problem yet, but found some things:

1) Try to set the axis of the trackballs

2) The trackball methods get passed anothers trackball quaternion!
this->_trackball.move(e->posF(), this->_trackball2.rotation().conjugate());

3) Ortho works. But try to get a perspective projection running.

My changes so far - still not working though:



#include "visualizer.h"

//#include <GLUT/glut.h>
#include <QtGlobal>
#include <QtDebug>
#include "constants.h"

Visualizer::Visualizer(QWidget *parent) :
QGLWidget(parent)
{
setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
this->_trackball = TrackBall(0.05f, QVector3D(0, 1, 0), TrackBall::Sphere);
this->_trackball2 = TrackBall(0.0f, QVector3D(0, 1, 0), TrackBall::Plane);
}

void Visualizer::initializeGL()
{
qglColor(Qt::black);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);

GLfloat lightPos[] = { 0.0, 0.0, -10.0, 0.0};
}

void Visualizer::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
this->draw();
}

void Visualizer::resizeGL(int w, int h)
{
//int ratio = w/h;
int side = qMin(w, h);
glViewport((w - side) / 2, (h - side) / 2, side, side);
//glViewport(0, 0, w, h);
}

static void multMatrix(const QMatrix4x4& m)
{
// static to prevent glMultMatrixf to fail on certain drivers
static GLfloat mat[16];
const qreal *data = m.constData();
for (int index = 0; index < 16; ++index)
mat[index] = data[index];
glMultMatrixf(mat);
}

static void loadMatrix(const QMatrix4x4& m)
{
// static to prevent glLoadMatrixf to fail on certain drivers
static GLfloat mat[16];
const qreal *data = m.constData();
for (int index = 0; index < 16; ++index)
mat[index] = data[index];
glLoadMatrixf(mat);
}

void Visualizer::draw()
{
int i;
GLint v[24][3]= {
{ 1, 1, 1},
{-1, 1, 1},
{-1, -1, 1}, // Front
{ 1, -1, 1},

{ 1, 1, 1},
{ 1, 1, -1},
{-1, 1, -1}, // Top
{-1, 1, 1},

{ 1, 1, 1},
{ 1, -1, 1},
{ 1, -1, -1}, // Right
{ 1, 1, -1},

{-1, 1, 1},
{-1, 1, -1},
{-1, -1, -1}, // Left
{-1, -1, 1},

{-1, -1, 1},
{-1, -1, -1}, // Bottom
{ 1, -1, -1},
{ 1, -1, 1},

{ 1, 1, -1},
{ 1, -1, -1},
{-1, -1, -1}, // Back
{-1, 1, -1}
};


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, width() / height(), 0.01, 15.0);
//glOrtho(-2, 2, -2, 2, -2, 2);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//glTranslatef(0.0,-50.0,0.0);

{
QMatrix4x4 m;
m.rotate(this->_trackball2.rotation());
multMatrix(m);
}

{
QMatrix4x4 m;
m.rotate(this->_trackball.rotation());
multMatrix(m);
}

/*QQuaternion q = this->_trackball.rotation();
qDebug() << q;
glRotatef(180.0*q.scalar() / PI, q.x(), q.y(), q.z());*/

glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

for(i=0; i<24; i+=4) {
glBegin(GL_LINE_LOOP);
glLineWidth(2.0);
qglColor(Qt::white);
glVertex3iv(v[i]);
glEnd();
}

glBegin(GL_QUADS);
qglColor(Qt::green);
for(i=0; i<24; i++)
{
glVertex3iv(v[i]);
}
glEnd();
}

void Visualizer::mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.move(e->posF(), this->_trackball2.rotation().conjugate());
updateGL();
}
}

void Visualizer::mousePressEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.push(e->posF(), this->_trackball2.rotation().conjugate());
}
}

void Visualizer::mouseReleaseEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.release(e->posF(), this->_trackball2.rotation().conjugate());
}
}
Can't look at this further until tomorrow afternoon!

Good luck

Johannes

danielperaza
10th April 2011, 01:11
How do I link to GLUT in Mac OS X 10.6 using qmake's .pro file?

JohannesMunk
10th April 2011, 07:35
For this code you don't need to!

But concerning this, I usually use GLee to get access to advanced OpenGL features:
http://www.opengl.org/sdk/libs/GLee/

I will have a look at your trackball later.

HIH

Johannes

danielperaza
10th April 2011, 19:50
Well I hope that if I use GLee my code doesn't start to sing as in the American TV Show, that would be horrible! :)

JohannesMunk
10th April 2011, 21:25
I can only deliver another intermediate:

1) accepted range of trackball parameters is [-1,1]!

2) perspective projection works! Remembered how it works :->

3) trackball works in x-direction. y still screwed up.


#include "visualizer.h"

//#include <GLUT/glut.h>
#include <QtGlobal>
#include <QtDebug>
#include "constants.h"

Visualizer::Visualizer(QWidget *parent) :
QGLWidget(parent)
{
setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
this->_trackball = TrackBall(0.05f, QVector3D(0, 1, 0), TrackBall::Sphere);
this->_trackball2 = TrackBall(0.0f, QVector3D(0, 1, 0), TrackBall::Plane);
}


void Visualizer::initializeGL()
{
qglColor(Qt::black);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);

GLfloat lightPos[] = { 0.0, 0.0, -10.0, 0.0};
}

void Visualizer::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
this->draw();
}

void Visualizer::resizeGL(int w, int h)
{
//int ratio = w/h;
glViewport(0, 0, (GLint)w, (GLint)h);
/*int side = qMin(w, h);
glViewport((w - side) / 2, (h - side) / 2, side, side);*/
//glViewport(0, 0, w, h);
}

static void multMatrix(const QMatrix4x4& m)
{
// static to prevent glMultMatrixf to fail on certain drivers
static GLfloat mat[16];
const qreal *data = m.constData();
for (int index = 0; index < 16; ++index)
mat[index] = data[index];
glMultMatrixf(mat);
}

static void loadMatrix(const QMatrix4x4& m)
{
// static to prevent glLoadMatrixf to fail on certain drivers
static GLfloat mat[16];
const qreal *data = m.constData();
for (int index = 0; index < 16; ++index)
mat[index] = data[index];
glLoadMatrixf(mat);
}

void getProjectionMatrix(QMatrix4x4& mat, float nearZ, float farZ)
{
static const QMatrix4x4 reference(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f);

mat = reference;
mat(2, 2) = (nearZ+farZ)/(nearZ-farZ);
mat(2, 3) = 2.0f*nearZ*farZ/(nearZ-farZ);
}

void Visualizer::draw()
{
int i;
GLint v[24][3]= {
{ 1, 1, 1},
{-1, 1, 1},
{-1, -1, 1}, // Front
{ 1, -1, 1},

{ 1, 1, 1},
{ 1, 1, -1},
{-1, 1, -1}, // Top
{-1, 1, 1},

{ 1, 1, 1},
{ 1, -1, 1},
{ 1, -1, -1}, // Right
{ 1, 1, -1},

{-1, 1, 1},
{-1, 1, -1},
{-1, -1, -1}, // Left
{-1, -1, 1},

{-1, -1, 1},
{-1, -1, -1}, // Bottom
{ 1, -1, -1},
{ 1, -1, 1},

{ 1, 1, -1},
{ 1, -1, -1},
{-1, -1, -1}, // Back
{-1, 1, -1}
};


glMatrixMode(GL_PROJECTION);

/* QMatrix4x4 mat;
getProjectionMatrix(mat, 0.1f, 100.0f);

glMatrixMode(GL_PROJECTION);
glPushMatrix();
loadMatrix(mat);*/

glLoadIdentity();
gluPerspective(60.0, width() / height(), 0.01, 15.0);
gluLookAt(5.0,0,0,0,0,0,0,1,0);
//glOrtho(-2, 2, -2, 2, -2, 2);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//glTranslatef(0.0,0.0,-5.0);

{
QMatrix4x4 m;
m.rotate(this->_trackball2.rotation());
multMatrix(m);
}

{
QMatrix4x4 m;
m.rotate(this->_trackball.rotation());
multMatrix(m);
}

/*QQuaternion q = this->_trackball.rotation();
qDebug() << q;
glRotatef(180.0*q.scalar() / PI, q.x(), q.y(), q.z());*/

glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

for(i=0; i<24; i+=4) {
glBegin(GL_LINE_LOOP);
glLineWidth(2.0);
qglColor(Qt::white);
glVertex3iv(v[i]);
glEnd();
}

glBegin(GL_QUADS);
qglColor(Qt::green);
for(i=0; i<24; i++)
{
glVertex3iv(v[i]);
}
glEnd();
}

QPointF Visualizer::pixelPosToViewPos(const QPointF& p)
{
return QPointF(2.0 * float(p.x()) / width() - 1.0,
1.0 - 2.0 * float(p.y()) / height());
}

void Visualizer::mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.move(pixelPosToViewPos(e->posF()), this->_trackball2.rotation().conjugate());
updateGL();
e->accept();
} else {
this->_trackball.release(pixelPosToViewPos(e->posF()), this->_trackball2.rotation().conjugate());
}
}

void Visualizer::mousePressEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.push(pixelPosToViewPos(e->posF()), this->_trackball2.rotation().conjugate());
e->accept();
}
}

void Visualizer::mouseReleaseEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.release(pixelPosToViewPos(e->posF()), this->_trackball2.rotation().conjugate());
e->accept();
}
}


More tomorrow.

Joh

danielperaza
10th April 2011, 21:53
Thanks a LOT! I was asking on GLUT because I wanted to try the glut's drawing primitives like solidTeapot and so on... I found that I must supply the LIBS directive to qmake this way:



LIBS += -framework GLUT


Besides that, why does this code need two trackballs instead of one? Actually the Boxes example, uses three, and I'm still wondering why. I have the same question on the conjugate() call on the rotation's quaternions, I'd like to know what sense makes that call here.

JohannesMunk
10th April 2011, 21:59
It uses two trackballs, because with the right mouse you can change the rotation axis for the first. We could setup a fixed Quaternion instead, but maybe you will want to have this flexibility at some point.

Joh

JohannesMunk
11th April 2011, 16:15
Ok! It was an axis problem after all. I got the trackball working by looking at the thing from another position. I also removed the 2nd trackball and replaced it by an identity Quaternion.



#include "visualizer.h"

//#include <GLUT/glut.h>
#include <QtGlobal>
#include <QtDebug>
#include "constants.h"

Visualizer::Visualizer(QWidget *parent) :
QGLWidget(parent)
{
setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
this->_trackball = TrackBall(0.05f, QVector3D(0, 1, 0), TrackBall::Sphere);
//this->_trackball2 = TrackBall(0.0f, QVector3D(0, 1, 0), TrackBall::Plane);
}


void Visualizer::initializeGL()
{
qglColor(Qt::black);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);

GLfloat lightPos[] = { 0.0, 0.0, -10.0, 0.0};
}

void Visualizer::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
this->draw();
}

void Visualizer::resizeGL(int w, int h)
{
glViewport(0, 0, (GLint)w, (GLint)h);
}

static void multMatrix(const QMatrix4x4& m)
{
// static to prevent glMultMatrixf to fail on certain drivers
static GLfloat mat[16];
const qreal *data = m.constData();
for (int index = 0; index < 16; ++index)
mat[index] = data[index];
glMultMatrixf(mat);
}

static void loadMatrix(const QMatrix4x4& m)
{
// static to prevent glLoadMatrixf to fail on certain drivers
static GLfloat mat[16];
const qreal *data = m.constData();
for (int index = 0; index < 16; ++index)
mat[index] = data[index];
glLoadMatrixf(mat);
}

void getProjectionMatrix(QMatrix4x4& mat, float nearZ, float farZ)
{
static const QMatrix4x4 reference(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f);

mat = reference;
mat(2, 2) = (nearZ+farZ)/(nearZ-farZ);
mat(2, 3) = 2.0f*nearZ*farZ/(nearZ-farZ);
}

void Visualizer::draw()
{
int i;
GLint v[24][3]= {
{ 1, 1, 1},
{-1, 1, 1},
{-1, -1, 1}, // Front
{ 1, -1, 1},

{ 1, 1, 1},
{ 1, 1, -1},
{-1, 1, -1}, // Top
{-1, 1, 1},

{ 1, 1, 1},
{ 1, -1, 1},
{ 1, -1, -1}, // Right
{ 1, 1, -1},

{-1, 1, 1},
{-1, 1, -1},
{-1, -1, -1}, // Left
{-1, -1, 1},

{-1, -1, 1},
{-1, -1, -1}, // Bottom
{ 1, -1, -1},
{ 1, -1, 1},

{ 1, 1, -1},
{ 1, -1, -1},
{-1, -1, -1}, // Back
{-1, 1, -1}
};


glMatrixMode(GL_PROJECTION);

glLoadIdentity();
gluPerspective(60.0, width() / height(), 0.01, 15.0);
gluLookAt(0.0,0.0,5.0,0,0,0,0,1,0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

{
QMatrix4x4 m;
m.rotate(this->_trackball.rotation());
multMatrix(m);
}

/*QQuaternion q = this->_trackball.rotation();
qDebug() << q;
glRotatef(180.0*q.scalar() / PI, q.x(), q.y(), q.z());*/

glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

for(i=0; i<24; i+=4) {
glBegin(GL_LINE_LOOP);
glLineWidth(2.0);
qglColor(Qt::white);
glVertex3iv(v[i]);
glEnd();
}

glBegin(GL_QUADS);
qglColor(Qt::green);
for(i=0; i<24; i++)
{
glVertex3iv(v[i]);
}
glEnd();
}

QPointF Visualizer::pixelPosToViewPos(const QPointF& p)
{
return QPointF(2.0 * float(p.x()) / width() - 1.0,
1.0 - 2.0 * float(p.y()) / height());
}

void Visualizer::mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.move(pixelPosToViewPos(e->posF()), QQuaternion());
updateGL();
e->accept();
} else {
this->_trackball.release(pixelPosToViewPos(e->posF()), QQuaternion());
}
}

void Visualizer::mousePressEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.push(pixelPosToViewPos(e->posF()), QQuaternion());
//this->_trackball.push(pixelPosToViewPos(e->posF()), this->_trackball2.rotation().conjugate());
e->accept();
}
}

void Visualizer::mouseReleaseEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
this->_trackball.release(pixelPosToViewPos(e->posF()),QQuaternion());
e->accept();
}
}

Thought it would be easier to get those Trackballs working. I only cannibalized some of their functionality some time ago.

Is your problem solved so far?

Johannes

danielperaza
12th April 2011, 06:06
Man, I really owe you a beer! Thanks a lot! :)

cadop
7th August 2014, 10:35
I know this is an old thread, but maybe someone can update it with a working example for Qt5? The zip file from danielpereza compiles, but has the arcball problem. Replacing the visualizer.cpp file with the one Johannes made creates a ton of compiler errors, including "cannot convert 'const float*' to 'const qreal*' on line 42, and other gluPerspective not declared in scope, and pixelPosToViewPos not declared in scope, etc.

This would be great resource as it is a basic implementation of a difficult subject.