| Home | Documentation | Download | Screenshots | Developer |
A more complex exemple that combines manipulatedFrames, selection and constraints.
This exemple illustrates different functions of the viewer. It displays a famous luxo lamp (Pixar) that can be interactively manipulated with the mouse. It illustrates the use of several ManipulatedFrames in the same scene.
Shift click on any part of the lamp to select it, and then move it with the mouse. To move the camera select the background or press the Control key (default modifier keys are switched).
A simpler object selection example is given in select.
A simpler frame displacement example is given in manipulatedFrame.
A simpler constrained frame example is given in constrainedFrame.
#include "qglviewer.h"
class Luxo
{
public :
Luxo();
void draw(const bool names=false);
qglviewer::ManipulatedFrame* frame(const unsigned short i) { return frame_[i]; }
void setSelectedFrameNumber(const unsigned short nb) { selected = nb; }
private :
// The four articulations of the viewer
qglviewer::ManipulatedFrame* frame_[4];
unsigned short selected;
void drawCone(const float zMin,const float zMax, const float r1, const float r2, const float nbSub);
void drawBase();
void drawArm();
void drawCylinder();
void setColor(const unsigned short nb);
void drawHead();
};
class Viewer : public QGLViewer
{
protected :
void draw();
void init();
void initSpotLight();
QString helpString() const;
void mousePressEvent(QMouseEvent *e);
void select(QMouseEvent*);
Luxo luxo;
};
#include "luxo.h"
#include <math.h>
using namespace qglviewer;
using namespace std;
////////////////////////////////// V i e w e r ////////////////////////////////////////
QString Viewer::helpString() const
{
QString text("<h2>L u x o ©</h2>");
text += "This example illustrates several functionnalities of QGLViewer, ";
text += "showing how easy it is to create a moderately complex application.<br>";
text += "The famous luxo® lamp (©Pixar) that can be interactively manipulated ";
text += "with the mouse. <b>Shift</b> left click on an a part of the lamp to select it, ";
text += "and then move it with the mouse. Select the background to move the camera, or ";
text += "press the <b>Control</b> key (the default shortcut modifier keys have been swapped).<br>";
text += "A simpler object selection example is given in <i>select</i>. ";
text += "A simpler frame displacement example is given in <i>manipulatedFrame</i>. ";
text += "A simpler constrained frame example is given in <i>constrainedFrame</i>.<br>";
text += "Feel free to use this code as the starting point of a multiple frame manipulation application.";
return text;
}
void Viewer::initSpotLight()
{
glMatrixMode(GL_MODELVIEW);
glEnable(GL_LIGHT1);
glLoadIdentity();
// Light default parameters
GLfloat spot_dir[3] = {0.0, 0.0, 1.0};
GLfloat light_ambient[4] = {0.5, 0.5, 0.5, 1.0};
GLfloat light_specular[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_diffuse[4] = {3.0, 3.0, 1.0, 1.0};
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_dir);
glLightf( GL_LIGHT1, GL_SPOT_EXPONENT, 3.0);
glLightf( GL_LIGHT1, GL_SPOT_CUTOFF, 50.0);
glLightf( GL_LIGHT1, GL_CONSTANT_ATTENUATION, 0.5);
glLightf( GL_LIGHT1, GL_LINEAR_ATTENUATION, 1.0);
glLightf( GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 1.5);
glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
}
void Viewer::init()
{
restoreFromFile();
// Make camera the default manipulated frame.
setManipulatedFrame( camera()->frame() );
// The frames can be moved without any key pressed
setMouseStateKey(QGLViewer::FRAME, Qt::NoButton);
// The camera can always be moved with the Control key.
setMouseStateKey(QGLViewer::CAMERA, Qt::ControlButton);
initSpotLight();
help();
}
void Viewer::mousePressEvent(QMouseEvent *e)
{
if (((e->state() & ~Qt::MouseButtonMask) == Qt::ShiftButton) && (e->button() == Qt::LeftButton))
{
select(e);
updateGL();
}
else
QGLViewer::mousePressEvent(e);
}
void Viewer::draw()
{
// Draw the ground
glColor3f(.4,.4,.4);
const float nbPatches = 100;
glNormal3f(0.0,0.0,1.0);
for (int j=0; j<nbPatches; ++j)
{
glBegin(GL_QUAD_STRIP);
for (int i=0; i<=nbPatches; ++i)
{
glVertex2f((2*i/nbPatches-1.0), (2*j/nbPatches-1.0));
glVertex2f((2*i/nbPatches-1.0), (2*(j+1)/nbPatches-1.0));
}
glEnd();
}
luxo.draw();
}
void Viewer::select(QMouseEvent* e)
{
// Make openGL context current
makeCurrent();
const int SENSITIVITY = 4;
const int NB_HITS_MAX = 1000;
// Prepare the selection mode
static GLuint hits[NB_HITS_MAX];
static GLint nb_hits = 0;
glSelectBuffer(NB_HITS_MAX, hits);
glRenderMode(GL_SELECT);
glInitNames();
// Loads the matrices
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
gluPickMatrix(static_cast<GLdouble>(e->x()), static_cast<GLdouble>(viewport[3] - e->y()), SENSITIVITY, SENSITIVITY, viewport);
// Don't use loadProjectionMatrix() directly as it clears the GL_PROJECTION matrix with a glLoadIdentity.
// The false flag indicates that no glLoadIdentity should be called, in order to combine the matrices.
camera()->loadProjectionMatrix(false);
camera()->loadModelViewMatrix();
// Render scene with objects ids
luxo.draw(true);
glFlush();
// Get the results
nb_hits = glRenderMode(GL_RENDER);
// Interpret results
unsigned int zMin = UINT_MAX;
short selected = -1;
for (int i=0; i<nb_hits; ++i)
{
if (hits[i*4+1] < zMin)
{
zMin = hits[i*4+1];
selected = hits[i*4+3];
}
}
if (selected >= 0)
{
setManipulatedFrame(luxo.frame(selected));
luxo.setSelectedFrameNumber(selected);
}
else
{
// Camera will be the default frame is no selection occurs.
setManipulatedFrame( camera()->frame() );
luxo.setSelectedFrameNumber(4); // dummy value
}
}
////////////////////////////////// L u x o ////////////////////////////////////////
Luxo::Luxo()
{
for (unsigned short i=0; i<4; ++i)
frame_[i] = new ManipulatedFrame();
// Creates a hierarchy of frames.
for (unsigned short i=1; i<4; ++i)
frame(i)->setReferenceFrame(frame(i-1));
// Initialize frames
frame(1)->setTranslation(Vec(0.0, 0.0, 0.08)); // Base height
frame(2)->setTranslation(Vec(0.0, 0.0, 0.5)); // Arm length
frame(3)->setTranslation(Vec(0.0, 0.0, 0.5)); // Arm length
frame(1)->setRotation(Quaternion(Vec(1.0,0.0,0.0), 0.6));
frame(2)->setRotation(Quaternion(Vec(1.0,0.0,0.0), -2.0));
frame(3)->setRotation(Quaternion(Vec(1.0,-0.3,0.0), -1.7));
// Set frame constraints
WorldConstraint* baseConstraint = new WorldConstraint();
baseConstraint->setTranslationConstraint(AxisPlaneConstraint::PLANE, Vec(0.0,0.0,1.0));
baseConstraint->setRotationConstraint(AxisPlaneConstraint::AXIS, Vec(0.0,0.0,1.0));
frame(0)->setConstraint(baseConstraint);
LocalConstraint* XAxis = new LocalConstraint();
XAxis->setTranslationConstraint(AxisPlaneConstraint::FORBIDDEN, Vec(0.0,0.0,0.0));
XAxis->setRotationConstraint (AxisPlaneConstraint::AXIS, Vec(1.0,0.0,0.0));
frame(1)->setConstraint(XAxis);
frame(2)->setConstraint(XAxis);
LocalConstraint* headConstraint = new LocalConstraint();
headConstraint->setTranslationConstraint(AxisPlaneConstraint::FORBIDDEN, Vec(0.0,0.0,0.0));
frame(3)->setConstraint(headConstraint);
// Means camera is selected.
selected = 4;
}
void Luxo::draw(const bool names)
{
// Luxo's local frame
glMultMatrixd(frame(0)->matrix());
if (names) glPushName(0);
setColor(0);
drawBase();
if (names) glPopName();
if (names) glPushName(1);
glMultMatrixd(frame(1)->matrix());
setColor(1);
drawCylinder();
drawArm();
if (names) glPopName();
if (names) glPushName(2);
glMultMatrixd(frame(2)->matrix());
setColor(2);
drawCylinder();
drawArm();
if (names) glPopName();
if (names) glPushName(3);
glMultMatrixd(frame(3)->matrix());
setColor(3);
drawHead();
if (names) glPopName();
// Add light
const GLfloat pos[4] = {0.0,0.0,0.0,1.0};
glLightfv(GL_LIGHT1, GL_POSITION, pos);
GLfloat spot_dir[3] = {0.0, 0.0, 1.0};
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_dir);
}
void Luxo::drawBase()
{
drawCone(0.0,0.03, 0.15, 0.15, 30);
drawCone(0.03,0.05, 0.15, 0.13, 30);
drawCone(0.05,0.07, 0.13, 0.01, 30);
drawCone(0.07,0.09, 0.01, 0.01, 10);
}
void Luxo::drawArm()
{
glTranslatef(0.02, 0.0, 0.0);
drawCone(0.0,0.5, 0.01, 0.01, 10);
glTranslatef(-0.04, 0.0, 0.0);
drawCone(0.0,0.5, 0.01, 0.01, 10);
glTranslatef(0.02, 0.0, 0.0);
}
void Luxo::drawHead()
{
drawCone(-0.02,0.06, 0.04, 0.04, 30);
drawCone(0.06,0.15, 0.04, 0.17, 30);
drawCone(0.15,0.17, 0.17, 0.17, 30);
}
void Luxo::drawCylinder()
{
glPushMatrix();
glRotatef(90, 0.0,1.0,0.0);
drawCone(-0.05,0.05, 0.02, 0.02, 20);
glPopMatrix();
}
void Luxo::setColor(const unsigned short nb)
{
if (nb == selected)
glColor3f(.9,.9,.0);
else
glColor3f(.9,.9,.9);
}
// Draws a truncated cone aligned with the Z axis.
void Luxo::drawCone(const float zMin,const float zMax, const float r1, const float r2, const float nbSub)
{
float angle,c,s;
Vec normal, p1, p2;
glBegin(GL_QUAD_STRIP);
for (unsigned short i=0; i<=nbSub; ++i)
{
angle = 2.0 * M_PI * i / nbSub;
c = cos(angle);
s = sin(angle);
p1 = Vec(r1*c, r1*s, zMin);
p2 = Vec(r2*c, r2*s, zMax);
normal = cross(Vec(-s,c,0.0) , (p2-p1));
normal.normalize();
glNormal3fv(normal.address());
glVertex3fv(p1.address());
glVertex3fv(p2.address());
}
glEnd();
}
#include "luxo.h"
#include <qapplication.h>
int main(int argc, char** argv)
{
// Read command lines arguments.
QApplication application(argc,argv);
// Instantiate the viewer.
Viewer v;
// Make the viewer window visible on screen.
v.show();
// Set the viewer as the application main widget.
application.setMainWidget(&v);
// Run main loop.
return application.exec();
}
Back to the main page