CGFLib - A library for Computer Graphics @ FEUP
 All Classes Files Functions Variables Pages
Basic project

Table of Contents

The typical structure of a basic application based on CGFlib is supported by three main classes: CGFapplication, CGFscene and CGFinterface.

CGFapplication

CGFapplication is responsible for initializing the underlying libraries (OpenGL, GLUT, GLUI) and connecting together the scene and the interface. Its code is meant to be application-independent, so that the main code of a project (the one that goes into the main() function) one only has to:

int main(int argc, char* argv[])
{
try {
app.init(&argc, argv);
app.setScene(new CGFscene());
app.run();
}
catch(exception& ex) {
cout << "Error: " << ex.what();
return -1;
}
return 0;
}

At initialization, the application configures GLUT and GLUI to use CGFapplication methods as callbacks (or event handlers) for common events generated by those libraries, such as:

When running, the application will receive such events from GLUT and GLUI, and call the event handlers specific to the project, which should be coded in subclasses of the CGFscene and CGFinterface classes.

As they are, the default implementations of CGFscene and CGFinterface create an empty scene and interface. The following sections explain how to create a scene and an interface.

CGFscene

The most important methods of a CGFscene are init() and display(). The first is used to initialize all things related to the scene, and tt is called by CGFapplication when the scene is associated to it through CGFapplication::setScene(). The second is responsible for rendering the scene when it is called by CGFapplication::display() due to a display event generated by GLUT or GLUI.

For a simple scene, one can simply create a sub-class of CGFscene and implement these two functions, as in the following example:

class MyScene : public CGFscene
{
public:
void init()
{
// Some init code goes here, OpenGL or not.
// This is called when CGFapplication::setScene() is called in main().
// At this time, OpenGL and other libraries have been initialized.
// Example: enables lighting computation
glEnable(GL_LIGHTING);
// pre-compute some values
a = b * c;
// etc...
};
void display()
{
// Do some actual rendering
// Clear image and depth buffer everytime we update the scene
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Initialize Model-View matrix as identity (no transformations)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_QUADS);
glVertex3d(0,0,0);
glVertex3d(4,0,0);
glVertex3d(4,3,0);
glVertex3d(0,3,0);
glEnd();
// etc...
};
};

Then, in the main() function, instead of creating and providing a CGFscene object to CGFapplication::setScene(), one would provide a MyScene object:

int main(int argc, char* argv[])
{
try {
app.init(&argc, argv);
app.setScene(new MyScene()); // Notice the difference
app.run();
}
catch(exception& ex) {
cout << "Error: " << ex.what();
return -1;
}
return 0;
}

Now all one has to do is to code the scene in the display function.

CGFinterface

This class is responsible for handling user input, both in terms of low-level mouse and keyboard events, and higher-level GUI controls such as buttons, spinners or text boxes. A CGFinterface instance will be tied to a CGFscene instance by the application, and typically their implementation is also coupled. As the interface will most likely be used to control some parameters of the scene, it will need to have access and knowledge of available class members of the scene. This is made available internally by the pointer CGFinterface::scene, which is initialized via CGFinterface::setScene() when the interface is attached to an application via CGFapplication::setInterface().

The CGFinterface class is closely tied with GLUI and GLUT but wraps some functionality in terms of setup, and simplifies some mechanisms. The basis CGFinterface class handles mouse events on the viewport to control the camera (left button rotates, middle zooms, right pans) and supports some keys for actions such as taking a snapshot ('s') or exiting the program ('Esc'). In terms of GUI, it implements an empty interface.

One willing to add an interface to a scene should create a sub-class of CGFinterface and reimplement some of its methods. The three main components that make up the interface are the following:

These will be discussed in the following sections.

GUI

The methods of CGFinterface that should be overridden for creating a GUI are the following:

For instance, to create a pair of buttons so that one would print something in the console, and the other would invoke a method defined in a class MyScene, the two CGFinterface methods could be implemented as follows:

//...
class MyInterface: public CGFinterface
{
// ...
void initGUI()
{
addButton("Print something", 1); // the second parameter is an identifier of the control, should be different for every control
addButton("Do something on scene.", 2);
};
void processGUI(GLUI_Control *ctrl)
{
switch (ctrl->id) // the identifier can be found here
{
case 1:
{
cout << "One button was pressed";
break;
}
case 2:
{
((MyScene *) scene)->doSomething(); //notice the cast and the access to a method of the associated scene
break;
}
}
};
// ...
}

Depending on the type of control, different information can be accessed. The various types of controls available can be seen on the reference of CGFinterface. For details on their meaning and the associated structures, please consult the GLUI documentation.

Keyboard

For handling keyboard, only one method needs to be reimplemented:

Therefore, key presses can be easily handled:

void MyInterface::processKeyboard(unsigned char key, int x, int y)
{
CGFinterface::processKeyboard(key, x, y); // notice the call to the super class' method so that the default keys are processed
// if you would like to override those, remove this
switch (key)
{
case 'd': // print something
cout << "Key 'd' was pressed";
break;
}
}

Mouse

Mouse events are handled by the following functions:

Depending on the type of processing needed, one should override the corresponding functions (remember that the default implementation manipulates the scene's active camera).

As an example, for printing the mouse pointer coordinates when the left mouse button is pressed one could do the following:

void MyInterface::processMouse(int button, int state, int x, int y)
{
if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN))
cout << "x = " << x << " ; y = " << y;
}

For more information regarding the button and state constants, check the GLUT documentation.