Chapter 1. Key Concepts

Chapters 1 through 5 are devoted to extending the set of nodes, actions, and elements in the database. Understanding how to create new nodes and actions requires some background information that was not needed in The Inventor Mentor. Important concepts discussed in this chapter include the method list, state and elements, the stack index, caching, runtime typing, and using extender macros. Most of the topics introduced in this chapter are discussed in detail in later chapters. Figure 1-1 shows a summary of key SoEXTENDER classes.

Adding Nodes and Actions to Inventor

If it weren't for extensibility, all actions could have been implemented simply as virtual functions on nodes. For example, GL rendering could have been a virtual function on the SoNode class. Adding a new node class would require implementing virtual functions for those actions that could not be inherited from the base class.

However, adding a new action would be considerably harder. It is impossible for you to add a new virtual function to the SoNode class or to any of the existing Inventor node classes. The only way to add a new action would be to derive classes from all nodes that would support it and to define the new method for those classes.

Figure 1-1. Summary of SoEXTENDER Classes


Inventor implements actions as separate classes to solve this problem. Each action maintains a list of static methods, one for each node class (see Figure 1-2). When an action is applied to the root of a scene graph, Inventor uses the method list to look up the method for each node in the scene graph, based on the type identifier for the node class. See “Runtime Types” for more information on class type identifiers.

Figure 1-2. Method List


For convenience, the base SoNode class registers static methods for all built-in Inventor actions; each of these methods calls a corresponding virtual function for that action. For example, SoNode registers a static method for SoGetBoundingBoxAction that calls the virtual getBoundingBox() method. All classes derived from SoNode can redefine getBoundingBox() in standard object-oriented fashion. If a class does not redefine a method, it inherits the method from its parent class

Most of the virtual methods on SoNode do nothing and must be redefined by the individual node classes. A few, however, such as the SoNode virtual methods for search() and write(), actually implement the action, since most nodes perform the action in the same way. In most cases, nodes can simply inherit these methods without redefining them.

Details on how to add a new node class are given in Chapter 2. Adding a new field class is described in Chapter 3.

If you add a new action class to Inventor, you need to set up the list of methods for the nodes that support the action (shown in Figure 1-2). This list contains one pointer to the static action method for each node class that supports the action. Adding a new action is described in Chapter 4.

Actions, State, and Elements

The Inventor database maintains a traversal state that is used when an action is applied to a scene graph. As described in The Inventor Mentor, the traversal state is a class (SoState) used by Inventor to store state information during execution of an action. Typically, the scene graph is traversed from top to bottom and from left to right. The traversal state is modified by the nodes encountered during this traversal. State is the way nodes in the scene graph communicate with each other. For example, shape nodes need to know whether the draw style is INVISIBLE in order to know whether to draw themselves. They use the draw-style element in the state to find out.

For simplicity and extensibility, each integral piece of information in the state is stored in a separate element. For example, the current diffuse color of the material is stored in an instance of the SoDiffuseColorElement class.

Enabling Elements

Each action class has its own notion of enabling certain elements. These elements are enabled when an instance of the action is created. By default, all elements in the state are disabled, which means that they can't be set or inquired. Both nodes and actions can enable the elements they require in the state for a particular action class. The list of elements to enable is set up ahead of time, typically in the initClass() method for the node or action before instances of either are constructed. The macro SO_ENABLE() provides a convenient way for nodes to enable elements for a particular action class. For example, the SoPickStyle node requires the pick-style element when picking, so its initClass() method enables this element in the pick action as follows:

SO_ENABLE(SoPickAction, SoPickStyleElement);

A side effect of this call is that SoPickStyleElement is also enabled in the SoRayPickAction class, which is derived from SoPickAction. Each action class inherits the enabled elements of its parent.

The SoGLRenderAction enables the SoViewportRegionElement, because the action is responsible for setting up this element in the state (the viewport region is specified in the action's constructor). This is how the SoGLRenderAction enables the SoViewportRegionElement:

enableElement(SoViewportRegionElement::getClassTypeId());

(Nodes typically use the SO_ENABLE() macro, and actions use the enableElement() method, since it's simple.) It doesn't hurt to enable an element in the state more than once (for example, a node and an action might enable the same element). If you are using the debugging library and you try to set or inquire an element that has not been enabled, an error message is printed. (See Appendix C of The Inventor Mentor.)

When an action is applied to a scene graph, the action builds an instance of SoState and passes a list of enabled elements to the state constructor. (If the list changes after an action is constructed, the action automatically rebuilds its state to include the newly enabled elements the next time the action is applied.)

Adding new element classes to Inventor is described in Chapter 5.

Setting Elements in the State

When a node is traversed, it may need to change the value of certain elements in the state. For the SoDrawStyle node, the code to change the current value of the lineWidth element looks like this:

if (! lineWidth.isIgnored())
   SoLineWidthElement::set(action->state, this,
                           lineWidth.getValue());

In this fragment, this is the node that is setting the value, and lineWidth.getValue() is the new value for the element.

Each enabled element has its own stack in the state with a unique stack index. Separators save and restore the state by pushing and popping these element stacks. The top element in the stack contains the current value for that element during traversal. Here is a brief summary of what happens behind the scenes when a node sets the value of an element in the state:

  1. The element class asks the state for a modifiable instance of the element, passing in its stack index.

  2. The state looks into the appropriate stack and creates a new instance on top of the stack, if necessary. It first checks whether the current value of the element was set by a node with its Override flag set to TRUE. If this flag is set, it returns NULL, since you can't modify the value.

    If the Override flag is not set, the new top instance of the element is returned to the element class.

  3. The element changes the value in this instance.

Figure 1-3 summarizes the relationship among nodes, actions, elements, and the state.

Figure 1-3. Nodes, Actions, Elements, and State


Getting Elements in the State

Each element provides a static get() method that returns its value from the top element in the stack. For example,

width = SoLineWidthElement::get(state);

returns the line width from the top element in the stack. Elements with multiple values provide a different sequence of get() methods, as described in Chapter 2.

State Elements

Supported element classes are listed here. The class name of each listed element is preceded by So and followed by Element. For brevity, the names are abbreviated in this list. Thus, the first item is SoAmbientColorElement.

These elements store information about the current set of materials:

AmbientColor 

ambient color of current material

DiffuseColor 

diffuse color of current material

EmissiveColor 

emissive color of current material

Shininess 

shininess value of current material

SpecularColor 

specular color of current material

Transparency 

transparency of current material

These elements store information about the current lighting model and light source:

LightAttenuation 


amount of attenuation of light sources over distance

LightModel 

current lighting model

These elements store information relevant to textures:

TextureBlendColor  


color used when blending texture

TextureCoordinateBinding  


how to bind texture coordinates to shapes

TextureCoordinate 


current set of texture coordinates

TextureImage 

current texture image

TextureMatrix 

current cumulative texture transformation matrix

TextureModel 

how texture is applied to material component

TextureWrapS 

how image wraps in s (horizontal) direction

TextureWrapT 

how image wraps in t (vertical) direction

These elements contain information about how to draw and interact with shapes:

ClipPlane 

current set of clipping planes

Complexity 

current complexity

ComplexityType 


how to interpret complexity: screen-space metric, bounding box, and so on

Coordinate 

current set of coordinates

CreaseAngle 

cutoff crease angle for smoothing when computing default normals

DrawStyle 

current drawing style (filled, lines, and so on)

MaterialBinding 


how to bind materials to shapes

NormalBinding 


how to bind surface normals to shapes

Normal 

current set of surface normals

PickStyle 

current pick style

These elements store profile information (for 3D text and NURBS surfaces):

ProfileCoordinate 


current coordinates used in profiles

Profile 

set of current profiles

These elements store information about transformations:

BBoxModelMatrix 


current cumulative object transformation matrix

LocalBBoxMatrix 


transformation matrix from object space to local coordinate space during application of an SoGetBoundingBoxAction

ModelMatrix 

current cumulative object transformation matrix

ProjectionMatrix 


current projection matrix

Units 

current factor for converting between units of measure

ViewingMatrix 

current camera viewing transformation matrix

These elements are related to text objects:

FontName 

name of current font

FontSize 

size of current font

These elements hold information about current GL modes:

LinePattern 

current dash pattern for drawing lines

LineWidth 

current width for drawing lines

PointSize 

current point size

ShapeHints 

current shape hints

These elements hold information about viewing:

CullVolume 

current culling volume

FocalDistance 

current focal distance of the camera

PickRay 

current ray to use for picking

ViewVolume 

current viewing volume

ViewportRegion 


current viewport region

These elements hold traversal information:

Cache 

whether caching is active, whether cache is valid, and currently open caches (see “Caching”)

Switch 

current child to traverse in switch nodes

Many of the elements named above have GL-specific versions that are used only for rendering. The following elements have only GL versions:

GLCacheContext 


cache context

GLColorIndex 

index into the current GL color map of the base color of the current surface material

CurrentGLMateria  

l
current state of GL materials (indices of last values sent to GL plus a flag indicating whether lighting is currently active); used to optimize the calls made to OpenGL

GLLightId 

integer identifier of current source (counts from 0)

GLMaterialIndex 


indices into the current GL material map of the ambient, diffuse, and specular components of the current surface material, as defined by GL's color index lighting model

GLRenderPass 

current rendering pass for multi-pass rendering (an integer)

GLTextureEnabled  


whether textures are currently enabled

GLTextureQuality 


current shape GLTextureQuality

GLUpdateArea 


rectangular area within the current viewport region that needs to be updated when rendering

Elements and Actions

Table 1-1 lists all elements and the actions for which they are enabled by default. The elements enabled for the line highlight and box highlight render actions are the same as those for the GL render action.

Table 1-1. Elements Enabled for Each Action

CB = Callback
GLR = GLRender
BB = BoundingBox
GM = GetMatrix
HE = HandleEvent
P = Pick
RP = RayPick
S = Search

 

 

 

CB

 

 

 

GLR

 

 

 

BB

 

 

 

GM

 

 

 

HE

 

 

 

P

 

 

 

RP

 

 

 

S

AmbientColor

X

X

 

 

 

 

 

 

BBoxModelMatrix

 

 

X

 

 

 

 

 

Cache

 

X

X

 

 

 

 

 

ClipPlane

X

X

 

 

 

X

X

 

Complexity

X

X

X

 

 

X

X

 

ComplexityType

X

X

X

 

 

X

X

 

Coordinate

X

X

X

 

 

X

X

 

CreaseAngle

X

X

X

 

 

X

X

 

CullVolume

 

X

 

 

 

 

 

 

CurrentGLMaterial

 

X

 

 

 

 

 

 

DiffuseColor

X

X

 

 

 

 

 

 

DrawStyle

X

X

 

 

 

 

 

 

EmissiveColor

X

X

 

 

 

 

 

 

FocalDistance

X

X

X

 

 

 

X

 

FontName

X

X

X

 

 

X

X

 

FontSize

X

X

X

 

 

X

X

 

GLAmbientColor

 

X

 

 

 

 

 

 

GLCacheContext

 

X

 

 

 

 

 

 

GLClipPlane

 

X

 

 

 

 

 

 

GLColorIndex

 

X

 

 

 

 

 

 

GLCoordinate

 

X

 

 

 

 

 

 

GLDiffuseColor

 

X

 

 

 

 

 

 

GLDrawStyle

 

X

 

 

 

 

 

 

GLEmissiveColor

 

X

 

 

 

 

 

 

GLLightId

 

X

 

 

 

 

 

 

GLLightModel

 

X

 

 

 

 

 

 

GLLinePattern

 

X

 

 

 

 

 

 

GLLineWidth

 

X

 

 

 

 

 

 

GLMaterialIndex

 

X

 

 

 

 

 

 

GLModelMatrix

 

X

 

 

 

 

 

 

GLNormal

 

X

 

 

 

 

 

 

GLPointSize

 

X

 

 

 

 

 

 

GLPolygonStipple

 

X

 

 

 

 

 

 

GLProjectionMatrix

 

X

 

 

 

 

 

 

GLRenderPass

 

X

 

 

 

 

 

 

GLShapeHints

 

X

 

 

 

 

 

 

GLShininess

 

X

 

 

 

 

 

 

GLSpecularColor

 

X

 

 

 

 

 

 

GLTextureBlend-
Color

 

X

 

 

 

 

 

 

GLTextureCoordi-
nate

 

X

 

 

 

 

 

 

GLTextureEnabled

 

X

 

 

 

 

 

 

GLTextureImage

 

X

 

 

 

 

 

 

GLTextureMatrix

 

X

 

 

 

 

 

 

GLTextureModel

 

X

 

 

 

 

 

 

GLTextureQuality

 

X

 

 

 

 

 

 

GLTextureWrapS

 

X

 

 

 

 

 

 

GLTextureWrapT

 

X

 

 

 

 

 

 

GLUpdateArea

 

X

 

 

 

 

 

 

GLViewingMatrix

 

X

 

 

 

 

 

 

GLViewportRegion

 

X

 

 

 

 

 

 

LightAttenuation

 

X

 

 

 

 

 

 

LightModel

X

X

 

 

 

 

 

 

LinePattern

X

X

 

 

 

 

 

 

LineWidth

X

X

 

 

 

 

 

 

LocalBBoxMatrix

 

 

X

 

 

 

 

 

MaterialBinding

X

X

 

 

 

X

X

 

ModelMatrix

X

X

 

 

 

X

X

 

NormalBinding

X

X

 

 

 

X

X

 

Normal

X

X

 

 

 

X

X

 

PickRay

 

 

 

 

 

 

X

 

PickStyle

X

 

 

 

 

X

X

 

PointSize

X

X

 

 

 

 

 

 

ProfileCoordinate

X

X

X

 

 

X

X

 

Profile

X

X

X

 

 

X

X

 

ProjectionMatrix

X

X

X

 

 

 

X

 

ShapeHints

X

X

X

 

 

X

X

 

Shininess

X

X

 

 

 

 

 

 

SpecularColor

X

X

 

 

 

 

 

 

Switch

X

X

X

X

X

X

X

X

TextureBlendColor

X

X

 

 

 

 

 

 

TextureCoordinate-
Binding

X

X

 

 

 

X

X

 

TextureCoordinate

X

X

 

 

 

X

X

 

TextureImage

X

X

 

 

 

 

 

 

TextureMatrix

X

X

 

 

 

X

X

 

TextureModel

X

X

 

 

 

 

 

 

TextureWrapS

X

X

 

 

 

 

 

 

TextureWrapT

X

X

 

 

 

 

 

 

Transparency

X

X

 

 

 

 

 

 

Units

X

X

X

X

 

X

X

 

ViewVolume

X

X

X

 

X

 

X

 

ViewingMatrix

X

X

X

 

 

 

X

 

ViewportRegion

X

X

X

X

X

X

X

 


Caching

The Inventor Mentor offers an introduction to caching. This section provides additional background information on how the Inventor caching mechanism works. For more information, see also the discussion of matches() in Chapter 5.

Elements provide the mechanism in Inventor for keeping track of scene graph dependencies. When a node uses Inventor elements, information is automatically stored that enables Inventor to determine whether a given cache is still valid, or whether values in the cache or values affecting the cache have changed, requiring a new cache to be built.

In cases where an element's value is stored as a simple floating point or integer value, Inventor simply compares the value of the element in the state with the value of the element stored in the cache. If the values are the same, the cache is still valid.

In other cases, where an element's value may be composed of a large number of values, Inventor checks to see which node or nodes set the value, as follows. (The SoCoordinateElement is an example of an element that uses this mechanism.) Every node instance in a scene graph is automatically assigned a unique identification number, referred to as its node ID. This node ID is updated when any field in the node changes. Whenever a node sets an element value, its node ID is stored in the state along with the value. When a cache is built, this node ID is also stored in the cache along with the element value. When Inventor needs to determine whether a given cache is valid, it compares the node ID in the state with the node ID in the cache. If both node IDs match, then the cache is still valid.

For elements that accumulate values in the state, such as transformations, a list of all node IDs that have modified the element is stored along with the element's value in the cache and in the state. If both lists of node IDs match, the cache is valid.

If you are creating a new node class that uses Inventor elements, caching will work automatically, since the necessary information will be stored in the state and compared appropriately with those in the cache. However, if you create a new node that depends on something that is not an element (for example, it might depend on a global variable), then you need to be sure that your new node is never cached, since it does not store the correct dependency information in the state. Use the SoCacheElement:: invalidate() method to specify that this new node should not be cached. The more versatile solution is, of course, to use elements so that they can automatically set up the caching dependencies for you. You can derive your own element class if necessary (see Chapter 5).

Runtime Types

The SoType class keeps track of runtime type information in Inventor. When initialized, many classes request a unique SoType. You can then use this type to find out the actual class of an instance when only its base class is known, or to create an instance of a particular class, given its type or name. Useful type-related methods, which are provided by the macros for implementing most classes, include

getClassTypeId() 


a static method that returns the type identifier for a specific class

getTypeId() 

a virtual method that returns the type identifier for an entity (for example, a node, field, or action) whose class is unknown

isOfType() 

returns TRUE if this entity (for example, node, field, or action) is of the specified type or is derived from that type

All nodes, node kits, manipulators, actions, elements, fields, engines, events, and details in Inventor must have a static method to initialize the class. This method, which must be called initClass(), sets up the type-identifier and file-format name information for the class. Standard Inventor classes are initialized during SoDB::init(), SoInteraction::init(), SoNodeKit::init(), or SoXt::init(). For extender classes, the initClass() method must be called after the database is initialized and before any instance of the class is constructed. The order for initializing is elements first, actions, then nodes.

For any new class that supports runtime typing, the initClass() method must be defined in the header file and implemented in the source file. Most classes have associated macros that can be used within the initClass() method.

Extender Macros

A number of macros are provided to make subclassing easier for you. Each chapter lists the relevant macros and where they are defined. For example, SoNode provides SO_NODE_INIT_CLASS() and SO_NODE_INIT_-
ABSTRACT_CLASS() in the SoSubNode.h file. SoField provides SO_SFIELD_INIT() and SO_MFIELD_INIT() in SoSubField.h. The chapter examples illustrate how to use the macros.

Most classes provide macros for use in header files, source files, and class initialization routines. Within these general categories, macros are provided for both abstract and nonabstract classes. The include file for the macros generally is the name of the base class, prefixed by Sub—for example, SoSubNode.h contains the macros for defining new node classes, and SoSubAction.h contains the macros for defining new action classes.