Update: 8/22/10 Checkout the updated framework and post: http://paulsolt.com/2010/08/glut-object-oriented-framework-on-github/
I created a C style driver program that used OpenGL/GLUT for my computer animation course projects. It worked fine for the first project. However, there were multiple projects and they all started to use the same boiler plate code. In order to reuse code, I decided to refactor and make an extensible class to setup GLUT. My goal was to make it easy to extend the core behavior of the GLUT/OpenGL application.
As I refactored the code I decided to make a base class to perform all the GLUT setup. There's a first time for everything and I didn't realize the scope of this change until I was committed to it. I will summarize the problems and how to solve them. The code and explanations should provide the basic understanding of what is happening, but it will not compile as it is provided.
Problem 1: My initial attempt was to pass member functions to the GLUT callback functions. However, you can't pass member functions from a class directly to C callback functions. The GLUT callback functions expect a function signature exactly as function(BLAH) and I was giving it something that was function(this, BLAH). Where the "this" portion was the object passed under the hood.
class AnimationFramework {
public:
void display();
void run();
void keyboard(unsigned char key, int x, int y);
void keyboardUp(unsigned char key, int x, int y);
void specialKeyboard(int key, int x, int y);
void specialKeyboard(int key, int x, int y);
void startFramework(int argc, char *argv[]);
};
... in the setup function for GLUT
void AnimationFramework::startFramework(int argc, char *argv[]) {
// Initialize GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(300, 100);
glutInitWindowSize(600, 480);
glutCreateWindow("Animation Framework");
// Function callbacks
glutDisplayFunc(display); // ERROR
glutKeyboardFunc(keyboard); // ERROR
glutKeyboardUpFunc(keyboardUp); // ERROR
glutSpecialFunc(specialKeyboard); // ERROR
glutSpecialUpFunc(specialKeyboardUp); // ERROR
init(); // Initialize
glutIdleFunc(run); // The program run loop // ERROR
glutMainLoop(); // Start the main GLUT thread
}
Solution 1: Create static methods, and pass these static methods to the call back functions. All the logic for the AnimationFramework will go into these static methods. I've fixed the compiler errors, but it feels like a step backwards from what I set out to do.
class AnimationFramework {
public:
static void displayWrapper();
static void runWrapper();
static void keyboardWrapper(unsigned char key, int x, int y);
static void keyboardUpWrapper(unsigned char key, int x, int y);
static void specialKeyboardWrapper(int key, int x, int y);
static void specialKeyboardUpWrapper(int key, int x, int y);
void startFramework(int argc, char *argv[]);
};
... in a setup function for GLUT
void AnimationFramework::startFramework(int argc, char *argv[]) {
// Initialize GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(300, 100);
glutInitWindowSize(600, 480);
glutCreateWindow("Animation Framework");
// Function callbacks
glutDisplayFunc(displayWrapper); // NO ERRORS!
glutKeyboardFunc(keyboardWrapper);
glutKeyboardUpFunc(keyboardUpWrapper);
glutSpecialFunc(specialKeyboardWrapper);
glutSpecialUpFunc(specialKeyboardUpWrapper);
init(); // Initialize
glutIdleFunc(runWrapper); // The program run loop
glutMainLoop(); // Start the main GLUT thread
}
I've made a bit of progress, but when you think about it, static methods aren't much different from the plain C functions.
Problem 2: I want to have the functionality in the instance methods, yet I'm stuck with static methods. I'm not encapsulating the behavior inside instance methods, and I have no option for inheritance. I've still failed to hit the initial goal of creating an easy to use object oriented GLUT wrapper that's extensible.
Solution 2: I need to make virtual instance methods in the class and call them from the static callback functions. But I can't make the function calls directly, I need a static instance of the class to make the instance function calls. (Re-read, as it's a little complex) All I need to do is pass an instance of the class or subclass and I'll be able to extend the functionality. It's a little tricky/ugly, but it's the best method I've found for encapsulating C style GLUT into a C++ application.
class AnimationFramework {
protected:
static AnimationFramework *instance;
public:
static void displayWrapper();
static void runWrapper();
static void keyboardWrapper(unsigned char key, int x, int y);
static void keyboardUpWrapper(unsigned char key, int x, int y);
static void specialKeyboardWrapper(int key, int x, int y);
static void specialKeyboardUpWrapper(int key, int x, int y);
void startFramework(int argc, char *argv[]);
void run();
virtual void display(float dTime);
virtual void keyboard(unsigned char key, int x, int y);
virtual void keyboardUp(unsigned char key, int x, int y);
virtual void specialKeyboard(int key, int x, int y);
virtual void specialKeyboardUp(int key, int x, int y);
};
The static methods are implemented here:
// Static functions which are passed to Glut function callbacks
void AnimationFramework::displayWrapper() {
instance->displayFramework(); // calls display(float) with time delta
}
void AnimationFramework::runWrapper() {
instance->run();
}
void AnimationFramework::keyboardWrapper(unsigned char key, int x, int y) {
instance->keyboard(key,x,y);
}
void AnimationFramework::keyboardUpWrapper(unsigned char key, int x, int y) {
instance->keyboardUp(key,x,y);
}
void AnimationFramework::specialKeyboardWrapper(int key, int x, int y) {
instance->specialKeyboard(key,x,y);
}
void AnimationFramework::specialKeyboardUpWrapper(int key, int x, int y) {
instance->specialKeyboardUp(key,x,y);
}
The startFramework() method is the same as provided in the Solution 1.
EDIT:
I left out details on how the instance was set. In my program, I subclassed AnimationFramework and created classes for each program I needed overriding the appropriate methods. As an example, KeyFrameFramework was a subclass in my project.
I have a function in my AnimationFramework.h
static void setInstance(AnimationFramework * framework);
AnimationFramework.cpp
AnimationFramework *AnimationFramework::instance = 0;
void AnimationFramework::setInstance(AnimationFramework *framework) {
instance = framework;
}
The main.cpp looks something like this:
int main(int argc, char *argv[]) {
AnimationFramework *f = new KeyFrameFramework(); // Subclass of AnimationFramework
f->setInstance(f);
f->setTitle("Key Framing:");
f->setLookAt(0.0, 2.0, 10.0, 0.0, 2.0, 0.0, 0.0, 1.0, 0.0);
f->startFramework(argc, argv);
return 0;
}
I set the instance variable of the AnimationFramework class to an AnimationFramework subclass called KeyFrameFramework. Doing so allows me to use polymorphism and call the appropriate functionality that is specific to each animation project. Note: Don't set the instance within a constructor, since the object is not fully initialized until the constructor is finished. You need to set the instance after your subclass object has been created.
Let me know if you have any questions. Below are the references I used.
References: