Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

Extending the Scene Graph

Class Struct

A class is defined by a typedef to an (if possible) anonymous struct. The first struct member must be the parent class and should be named super. Following are the classes fields and possibly newly defined virtual methods.

Virtual methods are defined as function pointers in the class struct.

Example:

 typedef struct {
     LsgNode super;
     void (*foobar)(float x);
     int visible;
 } LsgSample;

The Constructor

The constructor for a scene graph class is divided into two functions. The first is responsible for allocating memory for a new class instance, the second for initializing the newly allocated memory.

This division is necessary to allow the initializer being reused in descendant classes. Also it allows the creation of abstract classes by omitting the memory allocating part of the constructor.

The Memory Allocator

The memory allocator is quite trivial and looks almost the same for every class. It does the following three steps:
  1. allocate memory for the new instance
  2. call the initializer passing the new instance and all parameters
  3. return the new instance

Note:
This method should return NULL if an error is encountered while initializing the instance.
Example:
 LsgSample* LsgSample_create(void) {
     LsgSample* self = (LsgSample*)malloc(sizeof(LsgSample));

     LsgSample_init(self);

     return self;
 }

The Initializer

The initializer is the part of the constructor that does the actual work. It is responsible for correctly initializing the memory and setting the right virtual method pointers. The initializer method can roughly be divided into three sections:
  1. call the parent classes initializer
  2. set any overridden virtual method
  3. initialize members

Example:

 void LsgSample_init(LsgSample* self) {
     LsgNode_init(&self->super);

     ((LsgObject*)self)->destroy = (void (*)(LsgObject*))LsgSample_destroy;
     ((LsgNode*)self)->display   = (void (*)(LsgNode*, LsgFrustum*))LsgSample_display;

     self->foobar = LsgSample_foobar;
     self->show = 1;
 }

The Destructor

The virtual destructor method is called by LsgObject_free to free any allocated resources prior to destroying an object instance. The general structure of the destructor method is:
  1. free any allocated resources
  2. call the parent classes destructor

Example:

 void LsgSample_destroy(LsgSample* self) {
     // free any allocated resources

     LsgNode_destroy(&self->super);
 }

The Display Method

The method of the most interest is probably the virtual display method. It is called whenever the scene is rendered to screen (or selection buffer for that matter).

The second parameter defines the current view frustum in object coordinates. You can use it to speed up rendering if part of the node are outside the visible area.

Warning:
Be careful to always restore the OpenGL state before returning from this method. Failing to do so may affect the bahavior (or the look) of any node displayed afterwards.
Note:
You should use glPushAttrib and glPopAttrib to save and restore the OpenGL state rather then glGet or glIsEnabled. The latter may require the rendering pipeline to be flushed in order to determine the current state, which can have an impact on the display performance.
Example:
 void LsgSample_display(LsgSample* self, Frustum* frustum) {
     glPushAttrib(GL_ENABLE_BIT);

     glDisable(GL_LIGHTING);
     glDisable(GL_TEXTURE_2D);

     glBegin(GL_LINE_STRIP);
     glVertex2f(-1.0, -1.0);
     glVertex2f(-1.0,  1.0);
     glVertex2f( 1.0,  1.0);
     glVertex2f( 1.0, -1.0);
     glVertex2f(-1.0, -1.0);
     glEnd();

     glPopAttrib();

     // not strictly necessary; displays a neat bounding box if node.c has
     // been compiled with -DDEBUG_BBOX
     LsgNode_display(&self->super);
 }

The Update Method

The update method provides the framework for any time based behavior.

Note:
Don't forget to set the dirty flag when changing the objects bounding box.
Example:
 void LsgSample_update(LsgSample* self, float now) {
     // hide the object after 60 seconds
     self->visible = now < 60.0;

     LsgNode_update(&self->super);
 }

The Clean Method

The clean method is responsible for resetting the dirty flag. It does not need to be overridden other than for propagating the method call to sub nodes.

Example (LsgGroup):

 void LsgGroup_clean(LsgGroup* self) {
    LsgIterator* it;

    it = LsgIterator_create(self->children);
    while (LsgIterator_hasNext(it)) {
        LsgNode* child = (LsgNode*)LsgIterator_next(it);
        child->clean(child);
    }
    LsgObject_free((LsgObject*)it);

    LsgNode_clean(&self->super);
 }

(c) 2003, by Enno Cramer
generated on 13 Jul 2003
lescegra - doxygen