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
-
Introduction (this page).
-
SGL Nodes
-
SGL Geometry
-
SGL State
-
Traversals and Multithreading
-
SGL Math classes
-
sgldb - SGL data bases (loaders)
-
sglu - SGL Utilities
-
A Guide to Extending SGL
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:
-
Group nodes - these appear only as interior nodes to the DAG and
contain a list of child nodes (other group nodes or leaf nodes. They
are used for transformation (translate, rotate, and scale) child nodes,
selection (via switches, discrimination, level-of-detail, etc.).
They also store coarse bounding sphere information to speed up certain
traversals (see below).
-
Leaf nodes - as the name implies they appear at the leaves of the
tree. Some leaf nodes are used to define the view position and orientation,
others define the position and orientation of lights in the scene, and
others encapsulate either text or geometry (sglGeode) to be rendered in
the 3D scene.
-
Geometry nodes - attached only to the sglGeode leaf nodes in the
DAG. They contain the vertex, normal, color, and texture coordinate
data, information about the GL primitive type (POINT, LINE, TRIANGLE, QUADSTRIP...),
and indexing information (if desired). These nodes define the geometry
of the 3D objects. They also contain tighter axis-aligned bounding
box information used in the traversals (below).
-
Statelets - contain information of the OpenGL state that should
be applied when geometry nodes are encountered. State information
includes but is not limited to material, textures, polygon offset, polygon
mode (point, line, or fill), antialiasing, lighting and transparency.
These nodes can appear in a couple of different places in the scene graph:
-
Associated with the root node - they can be specified and default or override
states for the entire tree by storing them in the root (sglScene) node.
-
Associated with special group nodes - sglDefaultStateNode and sglOverrideStateNode,
to change the default and override state behaviours for all the children
in the DAG below this node.
-
Associated with geometry nodes - these statelets (together with the default
and override states) define the appearance of the geometry.
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):
-
preDraw - is a traversal that should be performed before cull, intersect,
or pick to clean up the tree and make sure internal data structures are
up to date.
-
cull - given a view frustum and a few other parameters (like LOD scale,
discriminator mask, state mask...) it will traverse the tree and find all
of the leaf nodes that are wholly or partially within the view frustum.
It also collects the current state associated with each selected geometry.
It performs state sorting on these pairs and places them in a render list.
-
draw - not strictly a scene graph traversal, this traverses the render
list generated by a cull traversal and issues the OpenGL commands needed
to render the 3D scene.
-
intersect - given a directed line segment it will find the point (if any)
at which it intersects a bounding sphere, bounding box, or polygon face
(which mode is set by the user).
-
pick - is like intersect, but selects and returns all the objects that
lie with in a frustum specifed by the user.
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