Introduction to SGL

This set of HTML pages is intended to act as a User's Manual for the Scene Graph Library (SGL).  Although the intent is to present this material as simply as possible, I am sure some knowledge and experience with other scene graph API's will be of tremendous help in understanding the concepts presented here.  Other scene graph APIs include Inventor (and Open Inventor), SGI's Performer, Java3d, VisKit, PLIB, OSG, OpenRM, etc.


Table of Contents


What is in a Scene Graph?

A scene graph is a directed acyclic graph (DAG) that contains all of the data need to render a 3D scene.  SGL is built on top of OpenGL and is influenced by its API.  The objects in SGL's DAG can be divided into three main categories: group (internal) nodes, geometry (leaf) nodes, and statelets: The above defines the data needed to render the 3D models.  To use this data a number of functions are also defined that traverse the tree and perform operations on each node.  These traversals are listed as follows (see the section on Traversals for more details):



Why Use a Scene Graph?

A well implemented scene graph will take advantage of various techniques to achieve better performance from the OpenGL driver without burdening the user with implementing complex algorithms to minimize the burden on the graphics system.  Some of the techniques that are implemented by SGL are as specific to OpenGL such as display listing geometry, using vertex arrays where possible, and using array prefetching (where implemented).  The most important techniques are more generally and devoted to minimizing the number of graphics commands issued: view-frustum culling and state sorting.  View frustum culling is used to determine which geometry lies either partially or wholly with in the viewport (or, more accurately, the view frustum).nbsp; By eliminating the geometry that lies wholly outside the viewport, the number of geometry commands that are sent down the graphics pipeline is reduced.nbsp; State sorting is also very important because, if done properly, it can order the commands that it sends to the graphics driver to minimize costly state changes.


Building a Simple Scene Graph

This sections describes how to build a simple scene graph and traverse it in the simplest possible way in order render it.  For more advanced traversal techniques please refer to the section on Traversals.

The standard scene graph is rooted by a special node called sglScene which is a subclass of sglGroup (for a complete class hierarchy go here):

   sglScene *root = new sglScene;
sglGroup, the base class for all internal nodes, is capable of having multiple children attached to it.  Therefore, more internal nodes or leaf nodes can be added to the root:
   sglTransformd *view_trans = new sglTransformd;
   scene->addChild(view_trans);

   sglDiscriminator *disc_node = new sglDiscriminator;
   disc_node->setMask(0x01);
   scene->addChild(disc_node);

   sglTransformd *geom_trans = new sglTransformd;
   disc_node->addChild(geom_trans);
See the section on Group Nodes for a list of all types of group nodes and the attributes that can be set for each.  So far this scene graph has two branches: (1) a transform node, and (2) a discriminator followed by a second transform node.

A camera and light can be added to the scene graph and moved around by attaching it to a transform node and changing the matrix inside the transform:

   sglPositionalLight *plite = new sglPositionalLight;
   plite->setDiffuse(1.0f, 0.5f, 0.5f, 1.0f);   // reddish light
   plite->setPosition(sglVec3f(0., 0., 1.));    // light positioned along -Z axis relative to the camera
   view_trans->addChild(plite)
   sglPerspectiveCamera *view_camera = new sglPerspectiveCamera;

   view_camera->setFOV(M_PI*0.25, (double)win_x/(double)win_y, 1.0, 200.0);

   sglViewPlatform *view_platform = new sglViewPlatform(*view_camera);
   view_platform->setEnableZUp(true);
   view_trans->addChild(view_platform);
See the section on Leaf Nodes for a list of all types of lights and cameras and the attributes that can be set for each.  Another type of leaf node is needed add geometry to the scene graph: sglGeode.  In order to add geometry to this scene graph, you must first add sglGeodes to the end of the branches you want to attach them to:
   sglGeode *geode = new sglGeode;
   geom_trans->addChild(geode);
You can add as many different kinds (subclasses) of sglGeometry to each of these geodes:
   sglTriangleSet *geometry1 = new sglTriangleSet;
   geode->addGeometry(geometry1);

   sglQuadSet *geometry2 = new sglQuadSet;
   geode->addChild(geometry2);  // etc...
Each different sglGeometry subclass has different requirements for specifying the attributes (see the documentation on Geometry Nodes for more details on each type).  Concentrating on the sglTriangleSet above, the following code shows how to describe a single triangle with an overall normal and per-vertex color:
   // dynamically allocation all of the arrays
   sglVec3f *vertices = new sglVec3f[3];
   vertices[0].set(0.0f, 0.0f, 0.0f);
   vertices[1].set(1.0f, 0.0f, 0.0f);
   vertices[2].set(0.0f, 1.0f, 0.0f);

   sglVec3f *normals = new sglVec3f[1];
   normals[0].set(0.0f, 0.0f, 1.0f);

   sglVec4f *colors = new sglVec4f[3];
   colors[0].set(1.0f, 0.0f, 0.0f, 1.0f);
   colors[1].set(0.0f, 1.0f, 0.0f, 1.0f);
   colors[2].set(0.0f, 0.0f, 1.0f, 1.0f);

   geometry1->setNumPrims(1);  // only one triangle (3 vertices implied)
   geometry1->setCoordList(vertices);
   geometry1->setNormalList(SGL_OVERALL, normals);
   geometry1->setColorList(SGL_PER_VERTEX, colors);
You can affect the appearance of the geometry by adding statelets to it (see the section on Statelets for a complete list and description of all the supported statelets).  The following code disables polygon anti-aliasing, enables lighting and enables color material to it:
   vector<sglStatelet*> state;
   state.push_back(new sglLighting(true));
   state.push_back(new sglAntiAliasPolygon(false));

   sglMaterial *mat = new sglMaterial;
   mat->setColorMode(GL_AMBIENT_AND_DIFFUSE);
   state.push_back(mat);

   geometry1->setState(0, state, NULL);
Note: the first parameter (0) of setState indicates the default state for this geometry.  SGL has the ability to set multiple different states so that the appearance of a given geometry can be changed by changing the state mask of the cull traversal.


Traversing the Scene Graph

Once the scene graph is built, two of the traversal functions can be used to cull and then render the contents of the scene graph.  The following code is one of the simpler ways (see sglView for an easier way) to traverse the scene graph (for more in-depth discussion of traversals see the section on Traversals and Multithreading):
   // Reuse the same traversal state node from frame to frame for better
   // performance.
   sglTraversalStatef trav_state;

   while (!quit_flag)
   {
      // Do application specific work
      doAppStuff();

      // Adjust internal data-structures (display lists, bounding volumes,
      // etc...) for the parts of the scene graph that may have changed
      // since last frame.
      root->preDraw();

      // Collect the geometry intersecting the given frustum & view_matrix.
      root->cull(trav_state,
                 win_x, win_y,
                 *view_platform,
                 override_statelets, disc_mask, state_mask);

      // NEW: setup OpenGL projection matrix
      (view_platform->getCamera()).applyProjection();

      // Render the visible geometry.
      trav_state.draw();
   }


Last modified: 20 July 2001