OpenGL and OS/2
A Model Viewer - Part 4
Written by Perry Newhook
Welcome to another segment of OpenGL on OS/2! This month we continue with part 4 of our Quake II model viewer. Last month we made our model viewer a lot more interesting by adding a solid view mode with both artificial light and texturing. This month we will be adding animation to our modeller.
Quick Note on the Bookstore
A few months ago I recommended two books from our Amazon bookstore: "OpenGL Programming Guide: The Official Guide to Learning OpenGL v1.1", and "OpenGL Reference Manual: The Official Reference Manual for OpenGL v1.1". Recently I received an email from a reader saying that he bought one of the books and that it was one of the best OpenGL purchases he's ever made. In my opinion these books are probably the last books you will ever need to buy in order to program in OpenGL effectively (well at least until OpenGL 1.2 comes out). If you haven't bought these books yet but are thinking about OpenGL programming, run to the bookstore link off the EDM/2 main page and pick them up. They'll make a great gift for Christmas!
One note about the Amazon site: Proceeds from the purchases made from this site go to keeping this site running; without your support this site could not have brought you the great programming articles it has over the past many years. If you do buy something from Amazon, be sure you do it directly from the EDM/2 link. If you surf around too much beforehand, Amazon may not register that it came from our site.
Before we enable animation, let's first create a menu that we can control the animation with. We can add a few useful commands such as rewind (go to first frame), play backwards, play forwards, single step back, single step forward and stop animation. Since we never used the "Load Model" menu (give me a break, I make this up as I go), let's change it to our animate menu.
We can change the menu to "Animate..." and the sub2 menu to the
functions we want to add:
I have also changed the name of the function from the original menuFuncLoad() to menuFuncPlay() just to be consistent. Now we have to add processing to that function.
Just like the other functions, munuFuncPlay() is called with the id of
the menu that was selected. Based on this selection we will change two
variables: one that states which frame we are going to show, and another
that specifies which direction to increment the frame number for the next
time we show it. In this section we will not do any bounds checking; we
will leave the checking to see if it is a valid frame to just before we
display it in the paintWindow() function. The menuFuncPlay() is as
Menu control for animation can sometimes be difficult, especially when you want to stop at a specific frame or step backwards and forwards quickly between frames. For this reason, lets also add keyboard control commands.
Adding a keyboard callback function is done with the GLUT function glutKeyboardFunction(). The callback function specified is passed three parameters. The first is the keyboard character that was pressed, and the next two holds the position of the mouse at the time the key was pressed. In our function, we do not need to use this position information.
When we process the keypress, instead of reproducing the logic in
menuFuncPlay(), we'll simply call menuFuncPlay() with the parameter that
corresponds to the key we have pressed. I've picked keys that make some
vague sort of sense (maybe not though, I'm watching the Simpsons and
drinking beer while I'm writing this). Here is the keyboard callback
Now that we have keyboard accelerators mapped, we need a way for the
user of the application to know what keys have been mapped. Just like
regular OS/2 menus can indicate the accelerator key by underscoring the
associated letter, GLUT menus can do the same thing by adding a tilde (~)
just before the letter to be underscored. Change the sub2 menu creation
code to indicate which letter is the acceleration key:
Now that we have all of the animation control we need, we can figure out how to decode the different animation frames from the Quake II file.
Animation Frame Extraction
You may not realize it, but we've already partially completed this section. When we extracted the model data for display in part 2 of this series, what we actually extracted was the first frame of the animation sequence. In this section we will simply generalize the code we've already created to be able to read in any number of frames.
When we extracted the first frame of data, we extracted two pieces: the point data, and the mesh data used to connect the point data into a solid. For each successive frame the Quake II model file only stores the new positions of the points; the mesh data stays exactly the same for each frame.
The first frame of data is stored in the .MD2 file at offset
mdh->offsetFrames from the beginning of the file, and there are
mdh->numXYZ points in this frame. If you remember, mdh is our pointer
to the Quake II file header). The code we use now is:
Subsequent frames follow the first frame in the order that they are to
be shown. Each frame is mdh->framesize big so you don't have to
calculate the frame size yourself. When we allocate our own buffer that
stores the scaled and translated points, we have to allocate enough memory
for all of the frames. We can convert the above code to the following to
read multiple frames:
This is the only change we need to make to store multiple frames. Instead of frm pointing to only the first frame, for every frame we adjust frm to point to a new frames worth of data. Also, instead of pointList, the area where we store our converted points, we have a pointListPtr which changes for each frame we store. Now the only thing left to do is to read our frame data back out when we display it.
Animation Frame Display
From our animation control code above, we have two variables that
control the animation sequencing: frameToShow and frameDirection. Since we
didn't do any bounds checking when we set these variables, we have to do
it now before we show the frame. The first thing we do is to increase the
frameToShow by the direction we specified. If the direction is zero as in
some of the commands, we end up with a still animation as the frames do
not change. Second, we have to check that the frame we are about to show
falls within an allowable range.
Now that we have the correct frame to show, we can set a pointer to
point to the correct frame of data. Where we currently use the stored
pointList pointer to get the frame of data, the pointList pointer now is a
pointer to a series of frames. Before we try and display our frame, we
have to get this pointer to point to the frame we want.
And that is all we need to do! For the display code we just use this new pointer we created instead of the model->pointList pointer we used previously. You can make the changes above and try it out. What you should have now is a completely controllable, animation capable, Quake II modeller! Cool! The gif animation below is created from six frames of the running sequence. The model shown is the female tris.md2 model with the voodoo.pcx texture applied.
Just to tidy up some loose ends, we have yet to implement a function in
the main menu called 'Exit'. This is a very simple menu function whose id
gets passed to the menuFuncMain() function. For completeness I'll include
the function below.
That's all of the changes we need to our code. You can download the complete viewer here.
Things to Try
There are some feature that you can add yourself to make this program even better:
And so we come to the end of our modeller development. I hope everyone liked this series. Over the past four months, we've created a modeller with a lot of features:
This series was as a result of a reader feedback, so if you have any ideas for topics, send them in and I'll see what I can do. Over the last sixteen months (wow!) of OpenGL columns, we've covered a lot of topics, so many in fact that I've run out of new ideas to cover.
This issue therefore marks the end of my regular monthly OpenGL columns. Future OpenGL columns will instead be feature articles as topics come up. I've really enjoyed writing these articles and I've also enjoyed reading all of the viewer feedback I've received. I hope that these articles have introduced you to the exciting world of OpenGL programming, and that some of you have tried programming OpenGL on your own. OpenGL is a wonderful world, that will get only more exciting as more OpenGL capable accelerated video boards make their way down to the user market.
Happy Holidays, hope you have a Merry Christmas and see you soon in OS/2-OpenGL land!