1. Timers for animation
Timers in GLUT
The specification of the GLUT timer function is this:
void glutTimerFunc(unsigned int msecs, void (*func)(int value), value);
Meaning? Your timer callback is a function that takes an integer value v, which is specified when you create the callback.
The behaviour of timers in GLUT is a little different than you might expect. From opengl.org:
So, when you set your timer function, it will be executed only once at the end of msecs milliseconds. It will not be called again. If you want a perpetual timer, you'll need to reset the timer in the callback. Sample code:
void TimeOut(int f) { // Do something for current frame 'f' DrawFrame(f); // Post redisplay if necessary glutPostRedisplay(); // Reset timer to go off again 't' millisecs glutTimerFunc(t, TimeOut, f + 1); }
You'll also need to set the initial callback of the timer, probably in main() with the rest of your GLUT callback registration:
void main(...)
{
...
glutTimerFunc(t, TimeOut, 0); // Draw frame 0 to start
...
}
Timers in QT
Using a timer in Qt is very similar in concept; the main difference, of course, is that it is object-oriented. There is a QTimer class, which has a signal timeout() that fires after a specified time in milliseconds.
In typical usage, you just instantiate a QTimer object, connect the timeout() slot to a callback slot, and call the start() method to set the timeout interval. For example, in the barebones Qt application you might add the following code to the Renderer class:
void Renderer::Renderer() // Constructor
{
...
// Assuming QTimer* timer; is in the header file
timer = new QTimer(this);
connect(timer, SIGNAL(timeout(), this, SLOT(animate()));
timer->start(100, false); // timeout after 100 ms
// param 2: true for single-shot, false for perpetual
}
and elsewhere
// Assuming slot: animate(); in the header file
void Renderer::animate()
{
// do something for current frame
...
// Post redisplay if necessary
updateGL();
}
Make sure you also #include <QTimer.h> somewhere in the header file.
2. Using mouse for translation and scaling
As I mentioned in lab previously, its worth looking into mapping all of your affine transformations onto mouse movements. You've already done this for rotations with the virtual trackball.
If you've ever used Maya, you know that the left mouse button is used to rotate the scene. Additionally, the middle button is used to translate the scene, and the right button is used to scale the scene. The question is, how do we map changes in the mouse position to parameters of translation and scaling (translation vector and scaling factor)?
Scaling
For scaling, the usual approach is to map the scale-up and scale-down operations to changes in either the x or y mouse coordinate. For example, moving the mouse to the right while holding the right button (positive x increase) would increase the scale factor, while moving the mouse to the left would decrease the scale factor.
This should be quite easy to implement, so I'll leave it up to you.
Translation
For translation, its a bit more complicated. Conceptually, what we want is for the object/scene to move in the same direction of the mouse movement, based on the current viewpoint (camera position). Consider (a) in the figure below: we need to find translation axes tx and ty based on the current camera position, and translate along these directions when the mouse is moved.
In the simplest case (i.e. the default camera position looking down the z axis at the origin), then tx would just be the x-axis and ty the y-axis. In the general case, however, we need to compute the axes based on the current camera position.
Fortunately, you already know how to do this. Remember the concept of an orthonormal basis, and that you can construct one from two arbitrary (non-parallel) vectors. We know that tx and ty should be orthogonal -- what would the third vector of the basis be? It would be coming out of the screen, orthogonal to both tx and ty.
We already have this vector -- its the view direction! Consider part (b) of the figure, which shows the camera-scene relationship from a different perspective. We know the location of the camera (pEye), the look-at point (pAt), and the up vector (vUp). So, we want two translation vectors that are orthogonal to the view vector (vView = pEye - pAt).

To construct an orthonormal basis, we need to have two non-parallel vectors. Assuming that our view configuration is valid, then vUp should not be parallel to vView. So, we can find tx by taking the cross-product:
tx = vUp x vView
Finally, we can find ty by taking another cross-product:
ty = vView x tx
3. Texture Mapping
There are two parts to texture mapping: 1) load an image file and pass it to OpenGL, and 2) apply texture map to an object being rendered.
Loading texture
The main function for loading a texture is glTexImage2d(...), which takes a whole schwack of parameters. The main thing to note is that you need to pass it a pointer to the decompressed pixel data (uchar *pixels), a width and a height, and a pixel format.
For the assignment, we've provided you code that loads Bitmap and PCX files. You'll have to take a look at the code to see how to get this information. For the pixel format, I can tell you that PCX files will use the GL_RGBA format, while Bitmap files use GL_BGR_EXT -- the blue and red channels are in reverse order. [Note that this is an extension (hence the _EXT), so it may not work on your machine. It works in the lab.]
Before loading a texture, you have to ask OpenGL for an ID and "bind" the texture. The associated functions are glGenTextures(...) and glBindTexture(...). So, assuming you have pixel data 'pixels' and width and height 'w' and 'h', you would load a texture like this:
int tex_name; glGenTextures(1, &tex_name); glBindTexture(GL_TEXTURE_2D, tex_name); glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h, 0,GL_RGBA, GL_UNSIGNED_BYTE,pixels);
(The values in red are ones that should be changed to match your code, you can leave the other ones as above.)
You can also configure texture behaviour with glTexParameteri(...). You don't really need to fiddle with that, but if you're interested look up the documentation or ask me.
Applying texture
The second part is to apply the image to an object. For each mesh face to be rendered, each vertex must have a texture coordinate assigned to it. These define a sub-image within the texture map that is mapped onto the face.
Assume you have a triangle with vertices vi and texture coordinates <si, ti> (texture coordinates are 2D). Texture coordinates are passed to OpenGL with the function glTexCoord2f(float s, float t), so you simply call this before each vertex. You should also make sure the texture is enabled and bound:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_name);
glBegin(GL_TRIANGLES);
{
glTexCoord2f(s1, t1); // specify texture coord
glNormal3fv(n1); // specify normal
glVertex3fv(v1); // specify vertex position
...
}
glEnd();
A textured object can still be affected by lighting calculations (i.e. the texture color would be brighter or darker depending on the incident light). By default, textures are not affected by lighting -- this is called GL_DECAL mode. To have lighting and texturing, you need to switch to GL_MODULATE mode, which is done with a call to glTexEnvi(...):
// Apply textures without lighting glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); // or... apply modulate texture according to lights glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
This setting applies to all textures, not only the bound texture.
A few notes:
- Textures are usually required to have power-of-2 dimensions. [This has apparently been relaxed in OpenGL 2.0 -- now the restriction is multiples-of-2.] If you pass it image data that doesn't satisfy this, it will return an error and not load the texture.
- The texture coordinates in the MD2 files are not "proper" -- they are integer (well,
short) values. They have to be modified before you can pass them toglTexCoord2f(). - I know some of you are not overly familiar with C++, so here's a little code snippet to check the extension of a filename:
#include <string.h>
...
// Find the last '.' in the filename
char *ext = strrchr(filename, '.');
// If no '.', will return null
if (ext == NULL) return false;
// Check the string from the '.' onwards to the end
else if (strcmp(ext, ".bmp") == 0)
{
std::cout << "loading a BMP texture, " << filename << std::endl;
}
else if (strcmp(ext, ".pcx") == 0)
{
std::cout << "loading a PCX texture, " << filename << std::endl;
}
