Statelets

This chapter is intended to give an overview of the state management performed by SGL.  In SGL every type of OpenGL state is encapsulated in its own subclass of sglStatelet [editor's note: as of the writing of this paragraph not all state is currently implemented.  Until then, it is relatively simple to subclass sglStatelet to create your own. Sections at the end of this page describe how to do this]. Statelets can range from the simple, like sglLighting [which calls glEnable(GL_LIGHTING) or glDisable(GL_LIGHTING) ], to the complex, like sglTexture2D [which encapsulates every thing about a 2D texture that we could think of]. 

What is SGL state and how is it used?

An SGL state is simply a collection of sglStatelets that defines what is configured in terms of OpenGL state.  You can create a state for an sglDrawable by adding sglStatelets to an STL vector and then assigning it using the sglDrawable::setState() function.  For example, the following state includes an alpha texture that is modulated, enables alpha blending, and enables lighting:
   sglTexture2D *tex = new sglTexture2D;
   tex->setImage(image_ptr, GL_ALPHA, GL_UNSIGNED_BYTE,
                 IMAGE_SIZE_X, IMAGE_SIZE_Y, false);
   tex->setInternalFormat(GL_ALPHA);

   tex->setMagFilter(GL_NEAREST);
   tex->setMinFilter(GL_NEAREST);

   sglTexEnv  *tev = new sglTexEnv();
   tev->setMode(sglTexEnv::MODULATE);

   vector<sglStatelet*> state;
   state.push_back(tex);
   state.push_back(tev);
   state.push_back(new sglAlphaTest(true, GL_GREATER, 0.0f));
This state can be associated with an sglDrawable (geoset) with the following call:
   geoset->setState(0, state, tex_coords);
where tex_coords could be a pointer to an array of sglVec2f's that correspond to the texture coordinates needed by the sglDrawable for the texture contained in state.  The tex_coords could also be a reference to an sglTexCoords object that abstracts the dimension and thus could contain 1D, 2D or 3D texcoords, or it could be a reference to a vector of sglTexCoords objects which contains one sglTexCoords for each stage of the multitexture pipeline if multitexturing is being used. Note that there is also a sglGeoSet::setTexCoordList() function that takes any one of these three types as the parameter and is a convenience functions to set the texcoords for the state associated with mask zero. These must be used after the setState(..) function to set/replace texture coordinates, otherwise the setState(..) call will replace them (note the setState(..) that takes no texture coordinate parameter will remove the texture coordinates because it assume a NULL parameter).
 
Multiple States per sglDrawable:
More than one state can be assigned to a given sglDrawable.  When assigning subsequent states be sure to supply a unique state mask (the first parameter), otherwise existing states will be replaced.  For example, if a second state doesn't have an sglTexture, you don't need to supply texture coordinates as follows:
   geoset->setState(0x1, state_without_texture);
More than one state with texture can also be assigned to a single sglDrawable:
   geoset->setState(0x2, state_with_tex2, tex_coords2);
In this example the texture requires different texture coords and a new set is passed in with the call to setState, but you can share the same texcoords if you like:
   geoset->setState(0x4, state_with_tex3, tex_coords);
Note that if one of the statelets is an sglTexGen that enables automatic texture coordinate generation (using SPHERE_MAP mode, for example), then texture coodinates do not need to be specified:
   geoset->setState(0x8, state_with_texgen, NULL);
In each of these examples a unique state mask as been used, so that the resulting geoset will have 5 different states (including the default state with mask 0).
 
Selecting the Active State:
When multiple states are present for a given sglDrawable, then the cull traversal is responsible for selecting which state to use when rendering the geometry contained in the sglDrawable.  When calling sglScene::cull(...) one of the parameters is the state mask.  Once an sglDrawable has passed the cull test and is selected to be drawn, the state mask passed in with the call to cull() is compared to the masks in the list of states using an AND comparison as follows:
   if (cull_state_mask && geoset->state_mask[i])
      return geoset->state[i];
Therefore, the first one to evaluate to true is the one that will be applied for this sglDrawable.

If cull's state mask is 0, or none of the states' masks cause the above test to evaluate to true, then sglDrawable's default state is selected. The default state is the one with a state mask of 0.  Every sglDrawable is constructed with this state, which is empty (no sglStatelets) to begin with.  The user can change the default state of an sglDrawable, by calling sglDrawable::setState(0, ....); with a different vector of sglStatelets.
 

Default and Override state, and State Sorting:
Once an active state is selected for an sglDrawable it is placed with the sglDrawable in a draw bin.  Note that in most cases this state will not completely define all of the statelets that are available.   In this case, unspecified values will take on SGL's default or override value (which ever is in force at the time) for that statelet, and a full state specification for that geoset will be known.

For the default values, SGL maintains a "full state" specifications for default statelet values which is a vector containing one of each type of sglStatelet.  This is "built" at the beginning of sglScene::cull().  The override values, are initialized from a list of override statelets that the user passes into cull function (it is not a full state).  As the cull traversal proceeds, these lists can be modified when sglDefaultStateNodes and sglOverrideStateNodes are encountered. Whatever are in these lists at the time a geoset is selected for draw will affect what the complete state for that geoset is.  The priority is given as follows:

  1. The sglScene::createDefaultStatelets() creates a complete set of "baseline" statelets using the default constructor of each. These are generated at the beginning of the cull traversal and are the lowest priority.
  2. Stateletes contained in any sglDefaultStateNode's encountered in the scene graph traversal overwrite these values in the order they are encountered; therefore, sglDefaultStateNode's lower in the tree (farther from the root) take precedence over those higher in the tree.
  3. Statelets encountered in the sglDrawable (using the sglDrawable::setState() function) overwrite the full state last and thus take precedence over all the default states and the sglDefaultStateNode's.
  4. Statelets in sglOverrideStateNode's have priority over all the default and drawable statelets. Statelets in sglOverrideStateNode's higher in the scene graph (closer to the root) have priority over those in nodes lower in the scene graph (opposite that of sglDefaultStateNode).
  5. Statelets in the override_statelets parameter passed into the sglScene::cull() functions when starting the cull traversal have the highest priority.

Therefore, associated with every sglDrawable that is selected for drawing, a "complete" (or full) state definition is associated (as a vector of sglStatelets). It is this definition that is used to sort the geometry.

Defining State Sorting Policy:
The sglTraversalState object sorts the geometry as it is added during the cull traversal according to its state definition in an attempt to minimize state changes between each geometry object. The rules for sorting are as follows:
  • Geometry with transparency enabled are sorted back to front and rendered last.
  • Non-transparent geometry with the occlusion test enabled are sorted front to back and rendered second to last.
  • All other geometry is sorted via the user defined sort order and rendered first.
  • The first two items are fixed and cannot be changed, sort order in the third item can be defined by the user, and is specified via the sglTraversalState::setSortOrder() method. The default sort order is to sort on transforms only.

    To disable state sorting, call setSortOrder with an empty vector.

       vector<sglStatelet::StateEnum> order;
       trav_state.setSortOrder(order);
    For all other sort orderings, pass a vector of StateEnums containing the desired order. For example, to sort first by lighting on/off, second by texture, and third by material:
       vector<sglStatelet::StateEnum> order;
       order.push_back(sglStatelet::LIGHTING);
       order.push_back(sglStatelet::TEXTURE);
       order.push_back(sglStatelet::MATERIAL);
       trav_state.setSortOrder(order);
    Note, there is no "best" sort order. The best sort order is dependent on the graphics hardware/driver and the contents of your scene. See the testTraversal program for examples of two scene graphs that work best with completely different sort orders.

    Also note that some sorts are more expensive than others. Obviously, the more attributes that you sort on the more time it is going to take. You should also be aware that statelets that can take on an unlimited number of values (e.g. material, texture, etc.) have a worst case O(n*logn) sort time. Statelets with a fixed number of states (e.g., any boolean statelets, polygon style, etc.) a worst case O(n) sort time.


    The following is a list of current statelets and links to the reference documentation:
    Last modified: Tue Dec 11 16:35:11 EDT 2001