class ssgBase - The Universal Abstract Base Class.

All significant SSG classes are derived from ssgBase - which offers a type testing mechanism and a means to print out the tree hierarchy in human-readable form, or to save/load it to/from disk.

class ssgBase 
{
  void  ref () ;
  void  deRef () ;
  int     getRef () ;

  int   isA ( int ty ) ;
  int   isAKindOf  ( int ty ) ;

  int   getType (void) ;
  virtual char *getTypeName(void) ;

  ssgBase *getUserData () ;
  void  setUserData ( ssgBase *user_data ) ;

  void  setName ( char *nm ) ;
  char *getName () ;
  const char *getPrintableName () ;

  virtual void print ( FILE *fd = stderr, char *indent = "", int how_much = 2 ) ;
  virtual int load ( FILE *fd ) ;
  virtual int save  ( FILE *fd = stderr ) ;

  ssgBase *clone ( int clone_flags ) ;
}  ;

Reference Counting.

All SSG classes are reference counted - that means that whenever you connect a node into the scene graph with ssgBranch::addKid(), we increment its reference count and each time we remove a node from the graph with ssgBranch::removeKid(), we decrement the count - and if it's zero, we'll delete the node to recover memory.

Sometimes, you need a node to stay in memory even though it may be be disconnected from the scene graph. You can achieve that by calling ssgBase::ref() to increment the reference count. If you later find you don't need that node anymore then you may ssgBase::deRef() it. If you ssgBase::deRef() a node to zero, SSG won't automatically delete it - you still need to use delete to do that. Since such deletions are recursive, you may delete an entire sub-branch with a single call.

Instead of using ssgBase::deRef() directly, you should use ssgDeRefDelete() which automatically deletes the node if the reference count drops to zero.

You can read the current ref count for a node using ssgBase::getRef().

Names.

It's often useful to attach an ASCII name to a node in the scene graph - this is often derived from a name field in whatever modelling tool was used to create the object. ssgBase::setName(s) sets the name, ssgBase::getName() returns it.

User Data

Although one can derive a new C++ class from an SSG class and thereby customise it's behavior, it's often more convenient to simply attach application-specific data structures to a basic entity. Two functions are provided: ssgBase::getUserData() and ssgBase::setUserData(data).

Notice that user data is of class ssgBase - which means that user data can be named, ref-counted - and can in turn have user data of it's own. This allows user data to be formed into linked lists when multiple user data items need to be attached to a single node.

Destructor Functions

When an SSG entity is NOT connected into the scene graph in any way, then the correct way to get rid of it and free up memory is to call it's destructor function. However, when the entity is included into the scene graph, you should disconnect it from the tree and let the reference count mechanism take care of the cleanup.

Type Names

SSG frequently needs to know what kind of an object an ssgBase is. Since C++ programs may create new classes that inherit from SSG classes, we provide several functions to make run time type determination possible. There is an external function for each type that returns the type token for that type:

  int ssgTypeBase       () ;
  int ssgTypeEntity     () ;
  int ssgTypeLeaf       () ;
  int ssgTypeVTable     () ;
  int ssgTypeVtxTable   () ;
  int ssgTypeDisplayList() ;
  int ssgTypeBranch     () ;
  int ssgTypeBaseTransform ();
  int ssgTypeTransform  () ;
  int ssgTypeTexTrans   () ;
  int ssgTypeSelector   () ;
  int ssgTypeTimedSelector () ;
  int ssgTypeRangeSelector () ;
  int ssgTypeRoot       () ;
  int ssgTypeCutout     () ;

Now, you can use the ssgBase::isA(type) or ssgBase::isAKindOf to test the type of the node. For example, if you want to test whether a node is a Leaf node or a Branch node, you can do this:

  if ( mynode -> isAKindOf ( ssgTypeLeaf() ) )
    printf ( "Leaf node\n" ) ;
  else
  if ( mynode -> isAKindOf ( ssgTypeBranch() ) )
    printf ( "Branch node\n" ) ;
  else
    printf ( "Something else\n" ) ;

Notice that if you ran that code on (say) an ssgSelector, then it'll print "Branch node" since the Selector class is derived from the Branch class. If you wanted to tell if a node was *exactly* a Branch node - and not from a derived class, then you could use:

  if ( mynode -> isA ( ssgTypeBranch() ) )
    printf ( "Branch node\n" ) ;

Finally, you can actually read the type of a node - either as a token (using ssgBase::getType()) or as an ASCII string (using ssgBase::getTypeName()). The latter is very useful for debug routines:

  printf ( "ERROR - something wrong with my '%s' node.\n",
                    mynode -> getTypeName () ) ;

Printing

It's sometimes useful during debug to print a section of the scene graph so you can examine it. ssgBase::print(fd, indent, how_much) does that for you - it prints out the node itself - and anything connected beneath it in the scene graph. fd is the file descriptor to print to (defaults to stderr) and indent is a string that will prefix all output lines - and is used internally within SSG to make printout of tree structures more legible by indenting them.

The values for the parameter how_much may be 0,1,2,3 or 4 and determine how much is printed, with 0 meaning little and 4 meaning much.
For how_much = 0, basic information of the branches is printed
For 1, additionally there is: basic leaf info, state pointers
For 2, additionally there is: states, user data, number of parents, bSphere
For 3, additionally there is: Reference count
For 4, additionally there is: contents of vertex-, normal-, colour-, texCoord- and index-arrays
Experience tells that for how_much = 0 you get very little out put, with 1, 2 and 3 a manageable amount and with 4 a huge amount, up to 100 MB for a medium sized model.

It would be unwise to attempt to parse the output of ssgBase::print into another program since it is only intended for human consumption and the format may change dramatically between revisions of SSG.

Cloning:

All classes derived from ssgBase have a member function ssgBase *clone(int clone_flags) which new's a new object of that class as a copy of the calling object.

The 'clone_flags' is a set of tokens that you 'OR' together to specify how 'deep' you want the cloning to go. By default (with clone_flags==0), only the object itself is cloned and the clone will simply point to the same child structures as the original object. However, if you OR in 'SSG_CLONE_RECURSIVE', then all ssgEntities beneath this one will also be cloned. ORing in SSG_CLONE_GEOMETRY will cause all the per-vertex data at the leaf nodes to also be cloned. SSG_CLONE_STATE causes ssgState objects to be cloned also, SSG_CLONE_STATE_RECURSIVE also causes states pointed to by other states to be copied, SSG_CLONE_USERDATA causes user data attached to the original node to be referenced (NOT copied) by the clone - otherwise, user data for the clone is set to NULL. Finally, SSG_CLONE_TEXTURE causes texture maps to be replicated also.

Most copy operations will typically use either zero, SSG_CLONE_RECURSIVE or (SSG_CLONE_RECURSIVE | SSG_CLONE_GEOMETRY)


<= previous = Return to SSG Index = next =>

Valid HTML 4.0!
Steve J. Baker. <sjbaker1@airmail.net>