Beckman Institute            University of Illinois at Urbana-Champaign             
University of Illinois at Urbana-Champaign

Syzygy Documentation: Myriad Scene Graph

Integrated Systems Lab
04/12/2006

Documentation Table of Contents

An example of a scene graph application.

Background

This section introduces the Myriad scene graph and explains its C++ API.

For the (similar) Python API, please see this [tutorial SceneGraphTutorial.html]. Python's internal reference counting is integrated with Myriad's, so their combined operation is thread-safe (e.g. when a remote peer in a shared scene graph deletes nodes). Several scene graph examples are written in Python.

  • particle-shape: A particle system using the built-in shapes.
  • lorenz: A visualization of the Lorenz attractor.
  • cosmos: Python port of the C++ demo "cosmos."
  • simpleviz: A minimal Hello World program.
  • interact: A custom standalone-mode simulator that manipulates objects with a grasping tool.

When reading this section, first understand the basic design of the Myriad distributed scene graph, including how lighting, texturing, and coordinate arrays are inherited to guide where rendering happens, at the "drawable" nodes. Also understand how to [construct #Constructing] a node tree and populate it with built-in objects. That lets you get something running quickly.

Using the Myriad Scene Graph in Your Programs

The easiest way to use Myriad in your programs is via Syzygy's scene graph application framework (arDistSceneGraphFramework), which is supported in both C++ and Python. You will want direct access to the underlying arGraphicsDatabase, which is provided by the framework's getDatabase() method.

It is also possible to use arGraphicsDatabase objects directly in your programs, without an application framework.

Myriad's Basic Design

The arGraphicsDatabase object is the fundamental tool for working with the Myriad scene graph. It receives a stream of messages causing it to alter its internal contents. These messages are either in the form of arStructuredData or byte blocks which can be parsed into arStructuredData. The alter() method receives arStructuredData, while the alterRaw() method receives byte blocks, parses them, and runs the resulting arStructuredData object through alter().

The arGraphicsDatabase object manages a tree of arGraphicsNode objects. The base class arGraphicsNode is subclassed to define the various types of nodes, like visibility nodes, texture nodes, lines nodes, etc. These objects have three important methods, receiveData(), draw(), and dumpData(). Message streams sent to the arGraphicsDatabase via alter()/alterRaw() are either routed to the arGraphicsDatabase itself, like the message that requests a new node be created, or to a specific node, via that node's receiveData() method. For instance, a message might ask that a visibility node turn itself on or off. The draw() method renders graphics via OpenGL, and the dumpData() method serializes the node, creating a message that can be used to replicate that node in another arGraphicsDatabase.

In Myriad cluster rendering, graphics state is synchronized across multiple machines by replicating arGraphicsDatabase objects on each machine. An arGraphicsServer object exists embedded in the application on the master machine and an arGraphicsClient object exists embedded in an szgrender on each render box (slave); both arGraphicsServer and arGraphicsClient manage an arGraphicsDatabase. When an arGraphicsClient connects to the arGraphicsServer, the server serializes the state of its arGraphicsDatabase (using the dumpData() methods of its nodes) and sends it across the network to the newly-connected arGraphicsClient, which then uses the alterRaw() method of its arGraphicsDatabase to recreate the remote state. Subsequently, all alter() calls to the arGraphicsDatabase managed by the arGraphicsServer are mirrored over the network to those managed by the connected arGraphicsClients. This message stream can be coordinated with graphics buffer swaps on the render computers to ensure perfect state synchronization at each draw.

Node Inheritance and Drawing

Many operations, like drawing or intersection with a ray or bounding sphere, require traversing the arGraphicsDatabase's tree. Tree traversal is generally depth-first, with child nodes visited in the order they were added (though the permuteChildren(...) method of arGraphicsDatabase can change this).

Tree traversal is made thread-safe with respect to node removal (from another thread as might occur when the scene graph powers a shared world in peer-to-peer mode) via a reference counting mechanism. The traversing process increments the reference count of nodes it uses (decrementing the reference count when it is through with them). The owning arGraphicsDatabase, upon receiving a deletion message, decrements the nodes' reference counts and removes them from its internal tree. It does not delete them. Instead, a node deletes itself once its reference count has fallen to 0.

Many properties related to drawing, like the active texture, material, points set, and normals set, are inherited down the scene graph tree, from parent to child. As a tree traversal occurs, an arGraphicsContext object keeps track, at each visited node, of the ancestor nodes that hold the active properties, and, at an arDrawableNode, the active properties are used to actually draw a mesh. As an example, when the traversal passes an arMaterialNode (going down into the tree) the active material is changed to the arMaterialNode's. Similarly, as the traversal passes the arMaterialNode the other way (going back up the tree after visiting all nodes in the subtree), the active material reverts to its previous value. In this way, the graphical characteristics of a drawable node only depend on its ancestors, not on nodes to the left or the right in the scene graph. This localization is very important for guaranteeing predictable results when node trees are combined.

The following is a complete list of the properties used by the arDrawable node in drawing. If you are using the built-in shapes only, the blend, material, and texture properties are the only relevant ones.

  • texture (arTextureNode)
  • blend (arBlendNode)
  • material (arMaterialNode)
  • normal3 (arNormal3Node)
  • color4 (arColor4Node)
  • tex2 (arTex2Node)
  • index (arIndexNode)
  • points (arPointsNode)
  • graphics state (arGraphicsStateNode)

Node Types

  • transform: The type name for an arTransformNode. This node holds an OpenGL transform (an arMatrix4 object in Syzygy). If transform node T1 has transform node T2 as a child, which in turn has transform node T3 as a child, this is equivalent (in OpenGL) to:
        glMultMatrixfv(T1.getTransform());
        glMultMatrixfv(T2.getTransform());
        glMultMatrixfv(T3.getTransform());
    

  • texture: The type name for an arTextureNode. This holds a reference to a texture object managed by the arGraphicsDatabase. Drawable nodes descending from it will use it when drawing their meshes.

  • bounding sphere: The type name for an arBoundingSphereNode. Contains an arBoundingSphere. Drawing uses this node for view frustum culling and picking uses this node for pick space culling. These processes assume that any geometry contained in the nodes descendants is within the bounding sphere.

  • billboard: The type name for an arBillboardNode. Displays a text string.

  • visibility: The type name for an arVisibilityNode. Can turn traversal of the subtree of its descendants on or off.

  • blend: The type name for an arBlendNode. A little bit of a hack because of the way OpenGL handles blending. The node holds a floating point value between 0 and 1. If 1, blending is ignored. If less than one, the alpha component of colors is multiplied by the blend factor. When used skillfully, this can create nice transition effects. Remember, then using OpenGL blending, rendering order matters. Consequently, it may be necessary to reorder nodes in the tree (see permuteChildren(...)) to get the desired result.

  • light: The type name for an arlightNode. This contains an arLight, which describes an OpenGL light.

  • material: The type name for an arMaterialNode. This contains an arMaterial, which describes an OpenGL material.

  • normal3: The type name for an arNormal3Node. This contains a list of 3D normals.

  • color4: The type name for an arColor4Node. This contains a list of RGBA colors (with 32 bit floating point components).

  • tex2: The type name for an arTex2Node. This contains a list of 2D texture coordinates.

  • index: The type name for an arIndexNode. This contains a list of integers. It is intended to

  • points: The type name for an arPointsNode. This contains a list of 3D points.

  • drawable: The type name for an arDrawableNode. It just stores a primitive type (like triangles, triangle strip, quads, quad strip, etc.) and a number of primitives to draw. All the other information needed for drawing is contained in the arGraphicsContext, which references state stored in the node's ancestors.

  • graphics state: The type name for an arGraphicsStateNode. Turn lighting, depth testing, etc. on and off.

  • persp camera: The type name for an arPerspectiveCameraNode. Stores a camera.

  • viewer: The type name for an arViewerNode. Stores a user's head position (a VR camera).

Constructing a Node Tree

arGraphicsDatabase is a subclass of arDatabase, as arGraphicsNode is a subclass of arGraphicsNode. A short summary of the kinds of operations that are possible.

Node names. The root node has a special name ("root") and ID (0).

    string arDatabaseNode::getName() const;
    void arDatabaseNode::setName(const string& name);

Node IDs. Cannot set node ID This is done by an arDatabase only in node construction. An arDatabase never repeats node IDs for the nodes it creates.

    int arDatabaseNode::getID();

Node info.

    string arDatabaseNode::getInfo() const;
    void arDatabaseNode::setInfo(const string& info);

Node type.

    string arDatabaseNode::getTypeString() const;

Creating new nodes. This must be done via the arDatabase.

    arDatabaseNode* arDatabase::getRoot();
    arDatabaseNode* arDatabase::newNode(arDatabaseNode* parent,
                                        const string& type,
                                        const string& name = "",
                                        bool refNode = false);
    arDatabaseNode* arDatabaseNode::newNode(const string& type,
                                            const string& name = "",
                                            bool refNode = false);

Reference counting. Necessary for thread-safety (if another thread is deleting).

    void arDatabaseNode::ref();
    void arDatabaseNode::unref();
    int arDatabaseNode::getRef();
    bool arDatabaseNode::active();
    list<arDatabaseNode*> arDatabaseNode::getChildrenRef();

Deleting nodes, cutting and erasing.

    bool arDatabase::cutNode(arDatabaseNode* node);
    bool arDatabase::cutNode(int ID);
    bool arDatabase::eraseNode(arDatabaseNode* node);
    bool arDatabase::eraseNode(int ID);

Inserting between existing parent and child (or in front of ALL of a parent's children). Must go through arDatabase to avoid deadlocks.

    arDatabaseNode* arDatabase::insertNode(arDatabaseNode* parent,
  			                 arDatabaseNode* child,
  			                 const string& type,
  			                 const string& name = "",
                                           bool refNode = false);

Permuting children.

    void arDatabase::permuteChildren(arDatabaseNode* parent,
                                     list<arDatabaseNode*>& children);
    void arDatabaseNode::permuteChildren(list<arDatabaseNode*>& children);

Inspecting and traversing the node tree.

    arDatabaseNode* arDatabaseNode::getParent() const;
    list<arDatabaseNode*> arDatabaseNode::getChildren() const;
    void arDatabaseNode::ps(int maxLevel=10000);

Finding nodes via name or ID.

    arDatabaseNode* arDatabaseNode::findNode(const string& name,
                                             bool refNode = false);
    arDatabaseNode* arDatabaseNode::findNodeByType(const string& type.
                                                   bool refNode = false);
    arDatabaseNode* arDatabase::getNode(int ID);

Manipulating node level. Imporant only for peer-to-peer reality.

  • AR_IGNORE_NODE
  • AR_STRUCTURE_NODE
  • AR_STABLE_NODE
  • AR_OPTIONAL_NODE
  • AR_TRANSIENT_NODE
    arNodeLevel getNodeLevel();
    void setNodeLevel(arNodeLevel nodeLevel);

Helper Classes

arMaterial, with the fields having the same meaning and defaults as in the OpenGL specification.

    arVector3 diffuse;
    arVector3 ambient;
    arVector3 specular;
    arVector3 emissive;
    float exponent;
    float alpha;

arLight, with the fields having the same meaning and defaults as in the OpenGL specification.

    int lightID;     
    arVector4 position;
    arVector3 diffuse;
    arVector3 ambient;
    arVector3 specular;
    float     constantAttenuation;
    float     linearAttenuation;
    float     quadraticAttenuation;
    arVector3 spotDirection;
    float     spotCutoff;
    float     spotExponent;

arBoundingSphere

    arVector3 position;
    float     radius;
    bool      visibility;

arRay

    arVector3 origin;
    arVector3 direction;

Using Built-In Objects

The particle-shape example application.

Mention attachMesh. Mention setTransform.

arSphereMesh: Sphere of radius 1 centered at (0,0,0).

    void arSphereMesh::setSectionSkip(int skip);

arCubeMesh: Cube with side length 1 centered at (0,0,0) and axis-aligned.

arCylinderMesh: Cylinder of length 1 pointing along the z axis.

    void arCylinderMesh::setAttributes(int numberDivisions,
                                       float bottomRadius,
                                       float topRadius);
    void arCylinderMesh::toggleEnds(bool useEnds);

arRectangleMesh: Square with sides of length 1 centered at (0,0,0). Normal in the positive y direction. Explain texture coordinates?

arTorusMesh: Torus in the x-y plane.

    void arTorusMesh::reset(int numberBigAroundQuads,
                            int numberSmallAroundQuads,
                            float bigRadius,
                            float smallRadius)

Importing files in .obj format.

Node Methods in C++

  void arDatabaseNode::lock();
  void arDatabaseNode::unlock();
  arMatrix4 arTransformNode::getTransform();
  void arTransformNode::setTransform(const arMatrix4& transform);
  string arTextureNode::getFileName();
  void arTextureNode::setFileName(const string& fileName, int alpha = -1);
  string arBillboardNode::getText();
  void arBillboardNode::setText(const string& text);
  bool arVisibilityNode::getVisibility();
  void arVisibilityNode::setVisibility(bool visibility);
  float arBlendNode::getBlend();
  void arBlendNode::setBlend(float blendFactor);
  arLight arLightNode::getLight();
  void arLightNode::setLight(arLight& light);
  arMaterial arMaterialNode::getMaterial();
  void arMaterialNode::setMaterial(const arMaterial& material);
  const float* arNormal3Node::getNormal3(int& number);
  void arNormal3Node::setNormal3(int number, float* normal3, int* IDs = NULL);
  vector arNormal3Node::getNormal3();
  void arNormal3Node::setNormal3(vector& normal3);
  void arNormal3Node::setNormal3(vector& normal3,
                                 vector& IDs);
  const float* arColor4Node::getColor4(int& number);
  void arColor4Node::setColor4(int number, float* color4, int* IDs = NULL);
  vector arColor4Node::getColor4();
  void arColor4Node::setColor4(vector& color4);
  void arColor4Node::setColor4(vector& color4,
  		             vector& IDs);
  const float* arTex2Node::getTex2(int& number);
  void arTex2Node::setTex2(int number, float* tex2, int* IDs = NULL);
  vector arTex2Node::getTex2();
  void artex2Node::setTex2(vector& tex2);
  void arTex2Node::setTex2(vector& tex2,
  	                 vector& IDs);
  const int* arIndexNode::getIndices(int& number);
  void arIndexNode::setIndices(int number, int* indices, int* IDs = NULL);
  vector arIndexNode::getIndices();
  void arIndexNode::setIndices(vector& indices);
  void arIndexNode::setIndices(vector& indices,
  		             vector& IDs);

Drawable Node Specifics

Relationship between drawable number, the sizes of the various arrays, and the number of primitives drawn.

  • DG_POINTS
  • DG_LINES
  • DG_LINE_STRIP
  • DG_TRIANGLES
  • DG_TRIANGLE_STRIP
  • DG_QUADS
  • DG_QUAD_STRIP
  • DG_POLYGON
  int arDrawableNode::getType();
  string arDrawableNode::getTypeAsString();
  int arDrawableNode::getNumber();
  void arDrawableNode::setDrawable(arDrawableType type, int number);
  void arDrawableNode::setDrawableViaString(const string& type, int number);

Graphics State Node

  • point_size
  • line_width
  • shade_model
  • lighting
  • blend
  • depth_test
  • blend_func

  • false
  • true
  • smooth
  • flat
  • zero
  • one
  • dst_color
  • src_color
  • one_minus_dst_color
  • one_minus_src_color
  • src_alpha
  • one_minus_src_alpha
  • dst_alpha
  • one_minus_dst_alpha
  • src_alpha_saturate
  string arGraphicsStateNode::getStateName();
  bool arGraphicsStateNode::isFloatState();
  bool arGraphicsStateNode::isFloatState(const string& stateName);
  string arGraphicsStateNode::getStateValueString(int i);
  float arGraphicsStateNode::getStateValueFloat();
  bool arGraphicsStateNode::setGraphicsStateString(const string& stateName,
                                                   const string& value1,
  			                         const string& value2 = "false");
  bool arGraphicsStateNode::setGraphicsStateFloat(const string& stateName,
                                                  float stateValueFloat);

Notes on Thread Safety

The Myriad scene graph is thread-safe, for instance, with respect to drawing in multiple threads even with simultaneous deletions and other scene graph restructurings occur in still other threads.


[Schedule] [Labs] [Beckman Meeting Rooms] [Equipment] [Projects] [CUBE Projects] [Syzygy] [VSS] [People] [Events] [Publications]