For better user interface, you might want to display some message onto the bottom of your GLUT window. One easy way to do this is to split the entire GLUT window into two viewports, with the upper larger one for geometry rendering, and the bottom smaller one for message output.
This can be done by, in your display(), switching temporarily to the bottom viewport and rendering text there. The important thing to remember is to RESTORE all openGL states before switching back to the normal rendering.
Here is what I did to show the text into the bottom area of a GLUT window.
First, change your reshape() into,
int win_w, win_h, sub_win_h; void reshape(int w, int h) { win_w = w, win_h = int(.97*h); sub_win_h = h - win_h; // viewport is almost the entire GLUT window except the bottom area. glViewport(0, sub_win_h, (GLsizei) win_w, (GLsizei) win_h ); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, win_w/float(win_h), 1.0, 100.0); }
Then write a new function, which is supposed to render the given text into the bottom area of the GLUT window, and restore everything before returning.
void show_message_window(const char* txt) { glViewport(0, 0, (GLsizei) win_w, (GLsizei) sub_win_h); { glMatrixMode(GL_PROJECTION); glPushMatrix(); { glLoadIdentity(); gluOrtho2D(0.0, (GLdouble) win_w, 0.0, (GLdouble) sub_win_h); glMatrixMode(GL_MODELVIEW); glPushMatrix(); { glLoadIdentity(); glPushAttrib(GL_LIGHTING_BIT); { glDisable(GL_LIGHTING); glColor3f(.1, .6, .6); glRectf(.0, .0, win_w, sub_win_h); glColor3f(.1, .1, .1); glRasterPos3f(0, 0, 0.1); } glPopAttrib(); for(int i=0; i<strlen(txt); i++) { glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, txt[i]); } } glPopMatrix(); } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } glViewport(0, sub_win_h, (GLsizei) win_w, (GLsizei) win_h); }
Now, you can simply make a call to show_message_window() inside your display() to show your message.
One simple way is to use GLUT idle function call back. First write a idle callback function which updates the transformation. Now based on user interactive, if the the animation is enabled, you (re)register this function as idle callback, otherwise do a null registration glutIdleFunc(NULL).
void do_animation() { ... } void keyboard (unsigned char key, int x, int y) { bool static animation = false; switch (key) { case 'A': case 'a': if( (animation = ! animation) ) glutIdleFunc(do_animation); else glutIdleFunc(NULL); break; case 'q': case 'Q': exit(0); } }
The computer represents a float number only approximately, and operations on float numbers will accumulate numeric error.
When we try to find whether two float numbers are equal, this is the wrong way to do
if(a == b) { ... }
You should write a function like,
bool approx_eq(float a, float b) { return fabs(a-b) < 1.0E-8; }
And you you should change the comparison code into,
if( approx_eq(a,b) ) { ... }
OpenGL has selection and picking feature which should be used if you want a robust picking in your Open GL application. However, they are kind of complicated, and here is a simple alternative way to do picking if your GL application has a very simple scene.
Remember we have bounding box for each triangle mesh object, which tells us the center of the model. Now use gluProject() to find the final position of the center point in the glut window. Also remember when you click your mouse, the mouse callback function knows the clicked mouse position. The model is picked if these two positions are close enough.
GLdouble modelview[16], proj[16]; int viewport[4]; glGetIntegerv (GL_VIEWPORT, viewport); // get current viewport transformation glGetDoublev (GL_MODELVIEW_MATRIX, modelview); // get current model view transformation glGetDoublev (GL_PROJECTION_MATRIX, proj); // get current projection transformation GLdouble objX, objY, objZ; // the position of the point in 3D space GLdouble winX, winY, winZ; // the position of the point finally rendered onto the glut window. gluProject (objX, objY, objZ, modelview, proj, viewport, &winX, &winY, &winZ);
When you do the picking, you have to be aware that the winY returned from gluProject() starts at the bottom of the window and goes up, while the y value in mouse callback starts at the top and goes down.
Try this demonstration program to get some idea about how the gluProject works.
glEnabel(GL_NORMALIZE);
// direction v1, direction v2, and the return direction forms a right hand system. Vector PerpendicularDirection(Vector const& v1, Vector const& v2) { return Vector( (v1.y*v2.z - v1.z*v2.y), (v1.z*v2.x - v1.x*v2.z), (v1.x*v2.y - v1.y*v2.x) ); }
float Angle(Vector const& v1, Vector const& v2) { float norm1 = sqrt( v1 * v1 ); float norm2 = sqrt( v2 * v2 ); return acos( v1 * v2 / (norm1 * norm2) ); }
#include <iostream> #include <sstream> using namespace std; const char* tostring(int var) { ostringstream os; os << var; return os.str().c_str(); }
Before the end of the afternoon class. Though not encouraged, you can also submit it before 7pm tomorrow.
Please pack all the above into one package, with filename as yourname.zip (you do not have to rename the .zip extension).
Please remember, readme file is as IMPORTANT as your source code and executable.
1.3.6