007 3078 001

User Manual: 007-3078-001

Open the PDF directly: View PDF PDF.
Page Count: 144 [warning: Documents this large are best viewed by clicking the View PDF Link!]

Open Inventor 2.1 Porting and
Performance Tips
Document Number 007-3078-001
Open Inventor™ 2.1 Porting and Performance Tips
Document Number 007-3078-001
CONTRIBUTORS
Written by Renate Kempf and Josie Wernecke
Edited by Cindy Kleinfeld
Production by Gloria Ackley
Engineering contributions by Gavin Bell, Alan Norton, Helga Thorvaldsdóttir.
Cover design and illustration by Rob Aguilar, Rikk Carey, Dean Hodgkinson,
Erik Lindholm, and Kay Maitz
© Copyright 1995, Silicon Graphics, Inc.— All Rights Reserved
This document contains proprietary and confidential information of Silicon
Graphics, Inc. The contents of this document may not be disclosed to third parties,
copied, or duplicated in any form, in whole or in part, without the prior written
permission of Silicon Graphics, Inc.
RESTRICTED RIGHTS LEGEND
Use, duplication, or disclosure of the technical data contained in this document by
the Government is subject to restrictions as set forth in subdivision (c) (1) (ii) of the
Rights in Technical Data and Computer Software clause at DFARS 52.227-7013
and/or in similar or successor clauses in the FAR, or in the DOD or NASA FAR
Supplement. Unpublished rights reserved under the Copyright Laws of the United
States. Contractor/manufacturer is Silicon Graphics, Inc., 2011 N. Shoreline Blvd.,
Mountain View, CA 94043-1389.
Silicon Graphics, the Silicon Graphics logo, and OpenGL are registered trademarks
and Open Inventor, Indigo2, Extreme, IRIX, IRIS Annotator, IRIS InSight, CaseVision,
and WebSpace Author are trademarks of Silicon Graphics, Inc. Extreme is a
trademark used under license by Silicon Graphics Inc.
iii
Contents
About This Guide ix
What This Guide Contains ix
What You Should Know Before Reading This Manual x
Background Reading x
Conventions Used in This Guide x
1. Porting to Open Inventor 2.1: Getting Started 1
Porting Applications With No Custom Classes 1
Porting Custom Classes 3
2. Incompatible API Changes 5
Overview of Changes By Class Name 6
Changes to Shape Hints 8
Changes to Complexity 9
Changes to Materials and Colors 10
Changes to Normals 12
Changes to Texture Coordinates 12
Changes to Vertex-Based Shapes 13
Changes to Viewers 14
Miscellaneous Changes 16
3. Scene Appearance Changes in Inventor 2.1 19
iv
Contents
4. New Features 21
Version-Related Changes 21
File Version 21
Inventor Revision Symbols 22
DSO Directories and Versions 22
File Reading and Writing 23
Support for VRML Files 23
User-Defined File Headers 23
SoOutput::setFloatPrecision 23
New Nodes 23
SoVertexProperty Node 24
SoLOD Node 26
SoLocateHighlight Node 26
VRML Nodes 26
New Fields 27
The fields Field 27
The isA Field 27
Miscellaneous Additions 28
SoGLRenderAction Render Abort Callback Changes 28
New Manipulator for Transformations 29
OpenGL Texture Object Extension 29
SoXtViewer Changes 29
New and Updated Utilities 30
File Downgrade Utility 30
Program for Converting Files to Use Vertex Properties 30
Program for Optimizing Scene Graphics 31
Program for Analyzing Rendering Performance 31
Contents
v
5. Incompatible Extender API Changes 33
Methods 33
The GLRender() Method 34
readInstance() Method 35
copy() Method 36
Elements 37
Changes in Shape Hints Element Methods 37
Changes in Binding Elements 37
Changes in Texture Elements 37
Changes in Material Elements 38
Implementation of Node Override 39
Shape Nodes 40
Generating Normals 40
Managing the Material State 40
Using the Material Bundle 44
Getting a Bounding Box 45
Material Property Nodes 45
Engine Evaluation 46
6. Optimizing Open Inventor Applications 47
Benchmarking Tips 48
Setting Performance Goals 48
Measuring Performance 49
Determining Bottlenecks 49
Modifying Your Application to Reduce Bottlenecks 50
Are You Finished Yet? 51
The Five Performance Commandments 52
vi
Contents
Optimizing Rendering 52
Determining Whether Rendering Is the Problem 52
Isolating Rendering 53
Using the ivperf Utility to Analyze Rendering Performance 53
Correcting Window Clear Bottlenecks 55
Improving Traversal Performance 55
Organizing the Scene for Caching 56
Improving Material Change Bottlenecks 57
Optimizing Transformations 58
Performance Tip for Face Sets 58
Optimizing Textures 59
Optimizing Texture Management 59
Using Lights Efficiently 61
Optimizing Vertex Transformations 61
Optimizing Pixel Fill Operations 63
Correcting Problems ivperf Does Not Measure 63
Optimizing Everything Else 68
Useful Tools 69
Optimizing Memory Usage 69
Looking at CPU Usage 70
Optimizing Action Construction and Setup 70
Decreasing Notification Overhead 71
Picking and Handling Events 72
A. Creating a Node 73
Overview 74
Initializing the Node Class 75
Enabling Elements in the State 75
Inheritance Within the Element Stack 76
Defining the Constructor 76
Setting Up the Nodes Fields 77
Defining Enumerated Values for a Field 77
Contents
vii
Implementing Actions 78
The doAction() Method 78
Changing and Examining State Elements 80
Element Bundles 81
The SoLazyElement 82
Creating a Property Node 82
Creating a Shape Node 88
Generating Primitives 88
Rendering 90
Picking 91
Getting a Bounding Box 93
Pyramid Node 94
Creating a Group Node 108
Child List 108
Hidden Children 108
Using the Path Code 109
What Happens If an Action Is Terminated? 111
Alternate Node 111
Using New Node Classes 116
Creating an Abstract Node Class 120
The copyContents() Method 120
The affectsState() Method 121
Uncacheable Nodes 121
Creating an Alternate Representation 122
Generating Default Normals 122
Index 123
ix
About This Guide
Open Inventor 2.1 Porting and Performance Tips explains how to port your Open Inventor
2.0 application to Open Inventor 2.1. Emphasis is on making the porting process go
smoothly and on helping you make your application run as efciently as possible, using
the various performance enhancements provided by Open Inventor 2.1.
What This Guide Contains
This guide consists of six chapters and one appendix:
Chapter 1, Porting to Open Inventor 2.1: Getting Started, steps you through the
porting process, pointing to other relevant information as appropriate.
Chapter 2, Incompatible API Changes, helps you nd and correct for incompatible API
changes by rst providing an overview list, then explaining each change in more detail.
Chapter 3, Scene Appearance Changes in Inventor 2.1, is useful if you run your Open
Inventor application after it has compiled successfully and nd that the scene
appearance isnt the same it was in Inventor 2.0. The chapter provides a table that
correlates scene appearance changes and the underlying API changes.
Chapter 4, New Features, describes the major new features in Open Inventor 2.1.
Chapter 5, Incompatible Extender API Changes, is for users who have customized
existing Inventor 2.0 classes or created subclasses of them. It explains the new features
and changes to the protected and extender methods.
Chapter 6, Optimizing Open Inventor Applications, explains how to determine what
is limiting the performance of your Open Inventor application, and provides suggestions
for improving its performance.
Appendix A, Creating a Node, provides an update to Chapter 2 of The Inventor
Toolmaker based on changes to Inventor in 2.1.
x
About This Guide
What You Should Know Before Reading This Manual
To work successfully with this manual, you should know how to write and debug Open
Inventor applications. Most readers of this book probably port applications they wrote
themselves. If you havent worked with Open Inventor before, you are urged to read the
materials listed here and to gain some experience programming with Open Inventor
before you attempt to port an application.
Background Reading
The following books provide background and complementary information for this
manual. Books available in hard copy as well the IRIS InSight online viewer are
marked with (I):
Wernecke, Josie, and the Open Inventor Architecture Group. The Inventor Mentor:
Programming Object-Oriented 3D Graphics with Open Inventor, Release 2, Menlo Park:
Addison-Wesley Publishing Company, 1994.(I)
Open Inventor Architecture Group. Open Inventor C++ Reference Manual. Menlo
Park: Addison-Wesley Publishing Company, 1994.
Wernecke, Josie, and the Open Inventor Architecture Group. The Inventor Toolmaker.
Extending Open Inventor, Release 2. Menlo Park: Addison-Wesley Publishing
Company, 1994.(I)
Conventions Used in This Guide
This guide uses the following typographical conventions:
Italics Filenames, IRIX command names, command-line options, function
parameters, ags, and book titles.
Fixed-width Code examples and system output.
Bold C++ class names and elds. Function or method names with
parentheses following the name, for example getPackedValue().
1
Chapter 1
1. Porting to Open Inventor 2.1: Getting Started
This chapter gets you started porting to Open Inventor 2.1. It steps you
through the porting process, pointing to other relevant information as
appropriate.
The process of porting to Open Inventor 2.1 is much simpler if your
application has no custom classes derived from the Inventor classes. This
chapter therefore discusses the process in two separate sections:
•“Porting Applications With No Custom Classes on page 1
•“Porting Custom Classes on page 3
Porting Applications With No Custom Classes
To port an application without custom classes, follow these steps:
1. Compile your application under Inventor 2.1.
If you have no errors, go to Step 4. Otherwise, continue with Step 2.
2. If you have compile or link problems, go to Overview of Changes By
Class Name on page 6 and from there to the more detailed description.
3. Fix all your problems in sequence and recompile, or recompile after
each x. The approach you take depends on your application and your
personal preference.
4. Run the application with the debug version of the library as follows:
Set the environment variable LD_LIBRARY_PATH to
/usr/lib/libInventorDebug to run your application with the debug
version of the library.
Note any debug messages that occur while running your
application and x problems. (The messages should be
self-explanatory.)
2
Chapter 1: Porting to Open Inventor 2.1: Getting Started
5. When your application compiles and links without errors, run it and
watch carefully whether there are changes to the appearance of your
scenes. Because some defaults changed, things may not look exactly as
they did before.
6. If you nd problems, go to Chapter 3, Scene Appearance Changes in
Inventor 2.1. The chapter provides a table that lets you access the API
problem description based on the appearance problem description.
After the application compiles and runs satisfactorily, you are urged to also
prepare it for the future by removing obsolete nodes, elds, and so on, that
are supported for compatibility only.
In some cases, the old version of the API is supported in addition to the new
version. This is done only to make the transition from 2.0 to 2.1 easier. To
ush out cases where your code is still using the old version, compile your
code with -DIV_STRICT as an option to cc.
Finally, optimize the application to really get the most out of Inventor 2.1 as
follows:
1. Change the program and the scene graphs to take advantage of the new
vertexProperty eld of vertex-based shapes.
2. Look at the other new features discussed in Chapter 4, New Features,
and make sure youre taking advantage of the available enhancements
and optimizations.
3. Look at the information in Chapter 6, Optimizing Open Inventor
Applications. It has been updated for 2.1 from the booklet of the same
name (which was not previously available online). Optimize your
program using that information as appropriate.
Porting Custom Classes
3
Porting Custom Classes
If your application has custom classes, porting becomes more complex.
Consider doing the following:
1. Attempt to update your classes as appropriate before you compile the
application for the rst time. See Chapter 5, Incompatible Extender
API Changes, for information thats useful when updating custom
classes.
2. When you believe youve xed most problems, go through the steps in
Porting Applications With No Custom Classes on page 1.
3. If compiling or linking uncovers further problems with your custom
classes, see also the include les in /usr/include/Inventor.
5
Chapter 2
2. Incompatible API Changes
The 2.1 release of Open Inventor provides signicant performance
enhancements over previous releases. In some cases an infrequently used
feature was slowing down rendering, even when the feature wasnt being
used. In Open Inventor 2.1, some of those features have therefore been
slightly modied and some have been removed altogether.
This chapter helps you nd these incompatible changes, providing the
following information:
•“Overview of Changes By Class Name on page 6
•“Changes to Shape Hints on page 8
•“Changes to Complexity on page 9
•“Changes to Materials and Colors on page 10
•“Changes to Normals on page 12
•“Changes to Texture Coordinates on page 12
•“Changes to Complexity on page 9
•“Changes to Viewers on page 14
•“Miscellaneous Changes on page 16
Note: This chapter only lists incompatible API changes. For additions to the
API, see Chapter 4, New Features.
6
Chapter 2: Incompatible API Changes
Overview of Changes By Class Name
This section provides an overview of the incompatible API changes. If youre
using the online version of this manual, you can access a change description
directly by clicking on it:
SoShapeHintscreaseAngle default value changed.
SoShapeHints, SoDrawStyle, SoClipPlaneWireframe and clipped
objects behavior changed.
SoShapeHints, SoCube, SoCone, SoSphere, SoCylinderPrimitive
shapes behavior changed.
SoComplexity, nodes derived from SoVertexShapeGeometry is no
longer dropped for complexity values smaller than 0.5.
SoComplexity, SoTexture2textureQuality eld behavior changed.
SoComplexity, SoTexture2Default texture quality is point sampling
on some systems.
SoComplexity, SoTexture2Textures sometimes use only 12 bits of
color.
SoMaterialMultiple materials support changed.
SoMaterialIndexObsolete.
SoColorIndex, SoLightModelIndexed colors with PHONG light
model no longer supported.
SoPackedColorField for storing packed colors changed.
SoPackedColorgetPackedValue(), setPackedValue() methods
changed.
SoMaterialBinding, SoMaterialCorrect number of diffuse colors
required.
SoMaterialBinding, SoMaterialCorrect number of transparencies
required.
SoMaterialBindingDEFAULT and NONE bindings obsolete.
SoNormalDefault normal no longer provided.
SoNormalBinding, SoNormalCorrect number of normals required.
SoNormalBinding, SoNormalNormals generated automatically.
Overview of Changes By Class Name
7
SoNormalBindingDEFAULT and NONE bindings obsolete.
SoTextureCoordinate2Default texture coordinate no longer provided.
SoTextureCoordinate2Automatic generation of texture coordinates as
needed.
SoTextureCoordinate2Correct number of texture coordinates
required.
SoTextureCoordinateBindingDEFAULT binding obsolete.
Shapes derived from SoNonIndexedShapestartIndex eld obsolete.
Shapes derived from SoNonIndexedShape
USE_REST_OF_VERTICES not fully supported.
SoXtViewerTexture mapping interactive draw style change.
SoXtPlaneViewer, SoXtExamineViewer, SoXtWalkViewerKey
bindings changed.
Shapes derived from SoNonIndexedShape
USE_REST_OF_VERTICES not fully supported.
All nodesLimited support for Override ag.
SoOutputisASCIIHeader() and isBinaryHeader() no longer
supported.
SoCallback, SoEventCallbackReset required after making direct
OpenGL calls.
SoLineHighlightRenderAction, SoBoxHighlightRenderAction,
SoGLRenderActionConstructor arguments changed.
SoSFLong, SoMFLong, SoSFULong, SoMFULonglong changed to
int32.
All draggers and manipulatorsControl key change.
8
Chapter 2: Incompatible API Changes
Changes to Shape Hints
SoShapeHintscreaseAngle default value changed.
The creaseAngle eld is used when default normals are generated. The
default value has been changed from 0.5 radians to 0.0 radians. As a
result, default normals are generated faster and use less memory.
However, objects that were smoothly shaded by default by previous
versions of Inventor will have faceted normals in Inventor 2.1.
SoShapeHints,SoDrawStyle,SoClipPlaneWireframe and clipped
objects behavior changed.
Drawing in wireframe mode (using the SoDrawStyle node) or clipped
mode (using SoClipPlane nodes) no longer turns off SOLID
SoShapeHints; clipped or wireframe objects have backfaces removed if
the appropriate SoShapeHints value is specied.
SoShapeHints,SoCube,SoCone,SoSphere,SoCylinderPrimitive
shapes behavior changed.
The primitive shapes (SoCube,SoCone,SoSphere,SoCylinder) no
longer automatically turn on shapeType = SOLID and vertexOrdering =
COUNTERCLOCKWISE on SoShapeHints nodes. You must insert an
SoShapeHints node at the top of your scenes to get the old primitive
behavior. Note that if you dont insert an SoShapeHints node, backface
culling is disabled. This may slow down the frame rate.
On the other hand, you can now specify an SoShapeHints node of
shapeType = UNKNOWN_SHAPE_TYPE and vertexOrdering =
COUNTERCLOCKWISE if you want both the interior and exterior
surfaces of primitive shapes to be lit. If you only want the inside of the
object lit, for example if the viewer is always inside a sphere, you can
specify an SoShapeHints node with shapeType = SOLID and
vertexOrdering = CLOCKWISE.
Changes to Complexity
9
Changes to Complexity
SoComplexity, nodes derived from SoVertexShapeGeometry is no
longer dropped for complexity values smaller than 0.5.
When you applied complexity values of less than 0.5 to triangle strips
and other vertex-based shapes in Inventor 2.0, some of the geometry
was dropped (for example, alternate strips were deleted from a triangle
strip set). In Inventor 2.1 this no longer happens. No geometry is
deleted from such shapes because this was not an effective way of
speeding up the rendering of complex shapes.
SoComplexity, SoTexture2textureQuality eld behavior changed.
The textureQuality eld of the SoComplexity node now takes effect
only when the next SoTexture2 node is traversed, not immediately as in
Inventor 2.0. As a result, mipmap creation for an SoTexture2 node can
be avoided if the texture quality is low enough. If you arranged your
scene graphs so that the textureQuality eld of SoComplexity
followed the SoTexture2 nodes, re-arrange them so that the
SoComplexity node is traversed rst.
SoComplexity,SoTexture2Default texture quality is point sampling
on some systems.
On systems that do not provide hardware support for texture mapping,
the default textureQuality eld is interpreted to mean point sampling
of the texture, and mipmaps are not used.
SoComplexity,SoTexture2Textures sometimes use only 12 bits of
color.
If textureQuality is less than 0.8, only 12 bits of texture color is used
(instead of 24) on systems that support the OpenGL texture extension
(EXT_texture). This increases the ll rate but decreases visual quality.
10
Chapter 2: Incompatible API Changes
Changes to Materials and Colors
SoMaterialMultiple materials support changed.
Open Inventor 2.1 no longer supports multiple values for the following
properties specied in an SoMaterial node:
– ambientColor
– specularColor
– emissiveColor
– shininess
Open Inventor 2.1 continues to support multiple values for:
diffuseColor
– transparency
If you provide multiple transparencies, you must provide as many
transparencies as diffuse colors. If you provide just one transparency, it
applies to all materials in the shape.
For backward compatibility, the eld types for ambientColor,
specularColor,emissiveColor, and shininess, have not changed. They
are still SoMF- elds.
Note that the new SoVertexProperty node provides explicit support
(and accelerated performance) for changing diffuse color and
transparency within a shape.
SoMaterialIndexObsolete.
The SoMaterialIndex node is no longer supported. For indexed color
rendering, use the SoColorIndex node instead.
SoColorIndex,SoLightModelIndexed colors with PHONG light
model no longer supported.
The PHONG light model is no longer supported with indexed colors.
Indexed colors can still be used with BASE_COLOR lighting.
Changes to Materials and Colors
11
SoPackedColorField for storing packed colors changed.
The eld for storing packed colors in the SoPackedColor node has been
changed to be more compatible with OpenGL. The eld name is now
orderedRGBA, and the packing order is 0xrrggbbaa, where aa
represents the alpha value, and rr, gg, and bb represent the red, green,
and blue components of the color, respectively. In previous releases, the
eld name was rgba and the packing order was 0xaabbggrr.
SoPackedColorgetPackedValue(),setPackedValue() methods
changed.
The getPackedValue() and setPackedValue() methods on the SbColor
class have changed.
getPackedValue() now takes a transparency value to pack with the
color.
setPackedValue() returns a transparency value along with the
SbColor. The transparency value, like the transparency eld in
SoMaterial, ranges from 0.0 to 1.0, where 0.0 is fully opaque, and
1.0 is fully transparent.
SoMaterialBinding,SoMaterialCorrect number of diffuse colors
required.
Open Inventor 2.1 assumes the correct number of diffuse colors is
specied. When the material binding of a vertex-based shape is not
OVERALL, provide one diffuse color for each vertex, face, or part,
depending on whether PER_VERTEX, PER_FACE, or PER_PART
binding is used. If you dont provide enough diffuse colors, you may
see unexpected results. Note that in previous releases, Inventor
cyclically reused the colors provided.
SoMaterialBinding,SoMaterialCorrect number of transparencies
required.
If not enough transparencies are provided, the rst one is used.
SoMaterialBindingDEFAULT and NONE bindings obsolete.
The DEFAULT and NONE material bindings are obsolete. Files
containing these bindings are transparently upgraded as if they
specied OVERALL materials. Applications that use DEFAULT or
NONE continue to work, unless they are compiled with -DIV_STRICT.
12
Chapter 2: Incompatible API Changes
Changes to Normals
SoNormalDefault normal no longer provided.
In previous releases, an SoNormal node contained one normal by
default. In Open Inventor 2.1 the default is not to provide any normals.
SoNormalBinding, SoNormalCorrect number of normals required.
If any normals are provided in a scene, Open Inventor 2.1 assumes the
correct number is specied. There should be one normal for each shape,
part, face, or vertex, depending on whether the normal binding is
specied as OVERALL, PER_PART, PER_FACE, or PER_VERTEX.
SoNormalBinding,SoNormalNormals generated automatically.
In previous releases, normals were automatically generated only if the
normal binding was DEFAULT. In Open Inventor 2.1, normals are
always automatically generated if they are needed and if the user does
not provide any normals in the scene. The correct number of normals is
generated, depending on the normal binding.
SoNormalBindingDEFAULT and NONE bindings obsolete.
The DEFAULT and NONE normal bindings are obsolete. Files
containing these bindings are transparently upgraded as if they
specied PER_VERTEX_INDEXED normals. Applications that use the
DEFAULT or NONE enums continue to work, unless they are compiled
with -DIV_STRICT.
Changes to Texture Coordinates
SoTextureCoordinate2Default texture coordinate no longer
provided.
In previous releases, an SoTextureCoordinate2 node contained one
texture coordinate by default. In Open Inventor 2.1 the default is not to
provide any texture coordinates at all.
SoTextureCoordinate2Automatic generation of texture coordinates
as needed.
In Open Inventor 2.1, texture coordinates are always automatically
generated if they are needed and no texture coordinates are provided in
the scene.
Changes to Vertex-Based Shapes
13
SoTextureCoordinate2Correct number of texture coordinates
required.
If any texture coordinates are provided in a scene, Open Inventor 2.1
assumes there is one for each vertex. You will see unexpected results if
there are fewer texture coordinates than Inventor expected.
SoTextureCoordinateBindingDEFAULT binding obsolete.
The DEFAULT for the SoTextureCoordinateBinding node is obsolete.
You can still specify PER_VERTEX or PER_VERTEX_INDEXED.
Changes to Vertex-Based Shapes
Shapes derived from SoNonIndexedShapestartIndex eld obsolete.
The startIndex eld of a vertex-based shape is replaced by the new
SoVertexProperty node.
Previously you could specify the vertex properties for multiple shape
nodes with one set of property nodes by having a different startIndex
for each shape. However, a nonzero startIndex eld may not work as
expected when used with coordinates in an SoVertexProperty node.
Furthermore, confusion arises if some of the vertex properties come
from the vertexProperty eld of the shape and others are inherited
from nodes in the scene.
To facilitate transition from Inventor 2.0, Inventor 2.1 continues to
support use of startIndex when all vertex properties are specied in the
state. You are urged to remove dependencies on this capability.
Shapes derived from SoNonIndexedShape
USE_REST_OF_VERTICES not fully supported.
In previous releases, when a value of -1 was found in the numVertices
eld of non-indexed shapesfor example, SoTriangleStripSetall the
remaining values in the current coordinates were used. In Inventor 2.1,
this is not supported when the coordinates are specied through the
new vertexProperty eld of the shape. Inventor 2.1 continues to
support the use of -1 for numVertices when the coordinates are
specied in the inherited state. You are urged to remove dependencies
on the old behavior.
14
Chapter 2: Incompatible API Changes
Changes to Viewers
The following changes to viewers were made in Inventor 2.1:
SoXtViewerTexture mapping interactive draw style change.
On systems that do not have hardware support for texture mapping,
the viewers now by default set the interactive draw style to move
without textures. In other words, by default, the viewers show the
texture on these systems when the camera is not moving, but when you
interact with the camera, the texture is disabled. Use the
setDrawStyle() method of the SoXtViewer class to change the viewer
draw style. End users of the viewer can change the draw style using the
right-mouse popup menu.
SoXtPlaneViewer,SoXtExamineViewer,SoXtWalkViewerKey
bindings changed.
The key bindings for the three viewers have changed to make them
more consistent with each other and to match WebSpace Navigator
Viewers.
The new bindings for SoXtPlaneViewer are the following:
Action Result
Left mouse Zoom in and out.
Ctrl + left mouse
Middle mouse
Translate up, down, left, right.
Ctrl + middle mouse Rotate around the viewer in forward direction (roll action).
<s> click Alternative to Seek button. Press (but dont hold) the <s>
key, then click on a target object.
Left mouse Zoom in and out.
Ctrl + left mouse,
Middle mouse
Translate up, down, left, right.
Right mouse Open pop-up menu.
Changes to Viewers
15
The key bindings for SoXtExaminerViewer have changed to be the
following:
The key bindings for SoXtWalkViewer have changed to be the following:
Action Result
Left mouse Tumbles the virtual trackball.
Middle mouse
Ctrl + left mouse
Translate up, down, left, and right
Ctrl + middle mouse
Left and middle mouse
Zoom in and out.
<s> click Alternative to Seek button. Press (but dont hold) the <s>
key, then click on a target object.
Right mouse Open pop-up menu.
Action Result
Left mouse Walk mode. Hold the mouse button and move up/down for
forward/backward motion. Move right and left for turning.
Speed increases exponentially with the distance from the
mouse-down origin.
Ctrl + left mouse
Middle mouse
Translate up, down, left, right.
Ctrl + middle mouse Tilt the camera up/down and right/left. This lets you look
around while stopped.
<s> click Alternative to Seek button. Press (but dont hold) the <s>
key, then click on a target object.
<u> click Press (but dont hold) the <u> key, then click on a target
object to set the up direction to the surface normal. By
default, positive y is the up direction.
Right mouse Open pop-up menu.
16
Chapter 2: Incompatible API Changes
Miscellaneous Changes
All nodesLimited support for Override ag.
In Open Inventor 2.1, not all of the nodes support the Override ag. The
following nodes do support override:
SoColorIndex
SoComplexity
SoDrawStyle
SoFont
SoLightModel
SoMaterial
SoMaterialBinding
SoPackedColor
SoPickStyle
SoShapeHints
SoTexture2
Other node classes ignore the Override ag. Note that you can still set
the ag for those classes, but it has no effect.
An Override ag on an SoMaterial node also overrides the diffuseColor
and transparency elds of SoVertexProperty nodes (in the scene graph
or in the vertexProperty eld of shapes). Likewise an Override ag in a
SoMaterialBinding node overrides the materialBinding eld in a
vertex property node. However, you cannot override just the diffuse
color or just the transparency. If you override one, the other is obtained
from the state when the override occurs.
SoOutputisASCIIHeader() and isBinaryHeader() no longer
supported.
The SoOutput methods isASCIIHeader() and isBinaryHeader() are no
longer supported. Use SoDB::isValidHeader() to verify that a header is
valid, or SoDB::getHeaderData() to see if a particular header is for
ASCII or binary les.
Miscellaneous Changes
17
SoCallback,SoEventCallbackReset required after making direct
OpenGL calls.
You must notify the Inventor material state management mechanism by
calling SoGLLazyElement::reset() if you directly make any of the
following OpenGL calls from a callback node:
glColor()
glMaterial()
glColorMaterial()
glEnable() or glDisable(), with a GL_COLOR_MATERIAL,
GL_BLEND, GL_LIGHTING, or GL_POLYGON_STIPPLE
argument.
glPushAttrib() or glPopAttrib() with GL_ENABLE_BIT,
GL_LIGHTING_BIT, GL_POLYGON_BIT,
GL_POLYGON_STIPPLE_BIT
After the last OpenGL call in your callback node, you must call
SoGLLazyElement::reset(state, bitmask). The value of the bitmask
depends on which of the above OpenGL calls you made.
If you invoke glColor() or glMaterial(), logical-OR one or more of the
following bitmasks as argument to SoGLLazyElement::reset()
(depending on which components of the OpenGL material state you
are altering):
SoLazyElement::DIFFUSE_MASK
SoLazyElement::EMISSIVE_MASK
SoLazyElement::SPECULAR_MASK
SoLazyElement::SHININESS_MASK
SoLazyElement::AMBIENT_MASK
If you invoke glColorMaterial(), or enable or disable
GL_COLOR_MATERIAL, the corresponding mask is
SoLazyElement::COLOR_MATERIAL_MASK.
If you enable or disable GL_BLEND, use
SoLazyElement::BLENDING_MASK.
18
Chapter 2: Incompatible API Changes
If you alter the GL stipple transparency, either with glEnable(),
glDisable(),glPushAttrib(), or glPopAttrib(), use
SoLazyElement::TRANSPARENCY_MASK.
If you alter lighting, either with glEnable(),glDisable(),
glPushAttrib(), or glPopAttrib(), use
SoLazyElement::LIGHT_MODEL_MASK
SoLineHighlightRenderAction, SoBoxHighlightRenderAction,
SoGLRenderActionConstructor arguments changed.
The constructor argument no longer takes useCurrentGLValues as an
argument.
SoSFLong,SoMFLong,SoSFULong,SoMFULonglong changed to
int32.
The So*Long elds have been changed to So*Int32 elds as follows:
SoSFLong -> SoSFInt32
SoSFULong -> SoSFUInt32
SoMFLong -> SoMFInt32
SoMFULong -> SoMFUInt32
You need to change your program to use the new elds, and to use the
types int32_t and uint32_t, dened in inttypes.h in place of long and
unsigned long. Use the longToInt32 utility program provided in the
inventor_dev.src.sample subsystem to help you do this.
To ease the transition, a typedef links the So*Long elds to SoInt32
elds and, where possible and feasible, methods taking arguments of
type long are supported.
Since the C++ compiler cannot distinguish overloaded functions by
return type alone, some methods are no longer supported. If you
compile your program with -DIV_STRICT, the So*Long elds cause a
compile time error.
All draggers and manipulatorsControl key change.
All draggers and manipulators now use the <Ctrl><Meta> key where
they used the <Alt> key in Inventor 2.0.
19
Chapter 3
3. Scene Appearance Changes in Inventor 2.1
Because certain elds in some nodes changed their defaults, and because of
other changes described in Chapter 2, your Inventor 2.0 application may
look different in Inventor 2.1 even if it compiles and runs ne.
Table 3-1 lists changes in appearance and points to information that helps
you x the problem. If youre working with the online version of the manual,
click on any Reason for more detailed information.
Table 3-1 Scene Appearance Changes in Inventor 2.1
Scene Appearance Change Reason
Texture quality looks different. SoComplexity, SoTexture2textureQuality
eld behavior changed.
SoComplexity, SoTexture2Textures
sometimes use only 12 bits of color.
SoComplexity, SoTexture2Default texture
quality is point sampling on some systems.
When you view a texture-mapped
object, the texture seems to
disappear.
SoXtViewerTexture mapping interactive
draw style change.
Shapes that were smooth shaded
look faceted.
SoShapeHintscreaseAngle default value
changed.
When you set the draw style to
wireframe, some of the lines are
missing.
SoShapeHints, SoDrawStyle, SoClipPlane
Wireframe and clipped objects behavior
changed.
A scene that has many spheres
(and/or cones, cubes, cylinders)
seems to render slower than before.
SoShapeHints, SoCube, SoCone, SoSphere,
SoCylinderPrimitive shapes behavior
changed.
20
Chapter 3: Scene Appearance Changes in Inventor 2.1
Vertex-based shapes look the same,
regardless of the SoComplexity
value.
SoComplexity, nodes derived from
SoVertexShapeGeometry is no longer
dropped for complexity values smaller than
0.5.
The material looks different than it
did in Inventor 2.0 for objects that
had a different material for every part
or every vertex specied.
SoMaterialMultiple materials support
changed.
Colors in a shape look wrong. SoMaterialMultiple materials support
changed.
SoMaterialBinding, SoMaterialCorrect
number of diffuse colors required.
A vertex-based shape used several
colors repeatedly for the different
parts and this no longer looks right.
SoMaterialBinding, SoMaterialCorrect
number of diffuse colors required.
In a program that contains direct calls
to OpenGL, the materials look
wrong.
SoCallback, SoEventCallbackReset
required after making direct OpenGL calls.
Table 3-1 Scene Appearance Changes in Inventor 2.1
Scene Appearance Change Reason
21
Chapter 4
4. New Features
This chapter describes the major new features that distinguish Open
Inventor 2.1 from Open Inventor 2.0. It discusses the following topics:
•“Version-Related Changes on page 21
•“File Reading and Writing on page 23
•“New Nodes on page 23
•“New Fields on page 27
•“Miscellaneous Additions on page 28
•“New and Updated Utilities on page 30
Version-Related Changes
This section discusses various issues directly related to the version change:
•“File Version
•“Inventor Revision Symbols
•“DSO Directories and Versions
File Version
The le version has been updated to 2.1; les written out with Open Inventor
2.1 have the le header #Inventor V2.1 ascii or #Inventor V2.1 binary.
Inventor 2.1 programs are still able to read 2.0 and 1.0 les. Older programs
cannot read 2.1 les; the les must be converted to the appropriate version
with the ivdowngrade program discussed in File Downgrade Utility on
page 30.
22
Chapter 4: New Features
Inventor Revision Symbols
C preprocessor symbols SO_VERSION and SO_VERSION_REVISION have
been added to Inventor/SbBasic.h to identify the revision of Inventor being
compiled against:
SO_VERSION is the major version number (for Inventor 2.1, 2)
SO_VERSION_REVISION is the minor revision number (for Inventor
2.1, 1)
If you use parts of the Inventor API that changed between revisions, you can
use these symbols to conditionally compile (#ifdef) your code if you want
the same source to compile against multiple versions of Inventor.
DSO Directories and Versions
To avoid problems with incompatible code, the places Inventor searches for
DSOs (dynamic shared objects) implementing new nodes has been changed.
Inventor 2.1 searches in the following order:
LD_LIBRARY_PATH:/usr/lib:/lib (normal rld rules)
.:/usr/local/lib/InventorDSO:/usr/lib/InventorDSO (new)
If youre running a setuid or setgid program, or if running as root, the order is:
/usr/lib:/lib (normal rld rules)
/usr/lib/InventorDSO (new)
The /usr/lib/Inventor and /usr/local/lib/Inventor directories were used for
Inventor 2.0. For example, if you created a new node of type NewNode and
created NewNode.so DSOs for both Inventor 2.0 and Inventor 2.1, you could
put the Inventor 2.0 NewNode.so in the /usr/local/lib/Inventor/ directory and
put the Inventor 2.1 NewNode.so in the /usr/local/lib/InventorDSO/ directory.
To avoid future problems with incompatible DSOs, Inventor 2.1 and future
releases of Inventor will search for DSOs that are given the same version
number as the Inventor libraries. For Inventor 2.1, DSOs for new nodes and
engines must be tagged with version sgi3.0. This is accomplished by
adding the ag -set_version “sgi3.0” when creating the DSO. See the reference
page for ld for more information on DSO versioning, and the Inventor
release notes for information on creating DSOs for new Inventor classes.
File Reading and Writing
23
File Reading and Writing
This section explains changes related to le reading and writing.
Support for VRML Files
The SoDB read methods now also read les with the VRML 1.0 header.
User-Defined File Headers
The SoDB class now includes methods for registering your own le headers.
This is useful, for example, if you have an application that supports a
superset of the Inventor le format, and you want to use the Inventor le
reading mechanism to parse the le. Similarly, you can specify your own
header for output les using methods of the SoOutput class. See the
reference pages for more details.
SoOutput::setFloatPrecision
You can now specify the precision Inventor uses when writing oating point
numbers in the ASCII le format. The new setFloatPrecision() method on
SoOutput takes one argument, an integer specifying the desired precision.
For example, a precision of 2 limits the output of oating point numbers to
2 signicant digits.
New Nodes
This section discusses new nodes that dont result in incompatible API
changes or scene appearance changes. It provides information about:
•“SoVertexProperty Node
•“SoLOD Node
•“SoLocateHighlight Node
•“VRML Nodes
24
Chapter 4: New Features
SoVertexProperty Node
A new node class, SoVertexProperty, has been added. The
SoVertexProperty node is a performance feature of Inventor 2.1; it does not
add functionality. Open Inventor applications that use this node may show
a speed improvement, particularly for non-cached rendering.
Description
The SoVertexProperty node allows you to specify in one node all the vertex
data for a vertex-based shape (that is, any shape derived from
SoVertexShape). This is more efcient than inheriting the data from several
different nodes. An SoVertexProperty node has the following elds:
vertex the coordinates, specied as SoMFVec3f
normal the normal vectors, specied as SoMFVec3f
normalBinding the normal binding, specied as SoSFEnum
orderedRGBA the packed color and alpha values, specied as
SoMFUInt32
materialBinding
the material binding, specied as SoSFEnum
texCoord the texture coordinates, specied as SoMFVec2f
When the SoVertexProperty node is used, it replaces the SoCoordinate3,
SoNormal,SoNormalBinding,SoTextureCoordinate2,SoMaterial (when
used to specify multiple colors), and SoMaterialBinding nodes.
Defaults
The vertex,normal,orderedRGBA, and texCoord elds are all NULL by
default. If any of these elds are not specied, the shape inherits the values
from other nodes in the scene.
The default normalBinding is PER_VERTEX, and the default
materialBinding is OVERALL. However, the normalBinding is ignored if
the normal eld is not used. Similarly, the materialBinding eld is ignored
if the orderedRGBA eld is not used.
New Nodes
25
Usage
The SoVertexProperty node can be used by the shape in either of two ways:
The SoVertexProperty node is placed in the scene and inherited by the
shape node.
This is the same model used for inheritance for other property nodes in
Inventor.
The vertex-based shapes have a new SoSFNode eld, vertexProperty,
that allows you to directly include the SoVertexProperty node in the
shape.
This is a signicant change from the inheritance model used for other
properties. The properties specied in the node apply only to the shape
in which it was included; they do not affect any subsequent shapes.
Performance Considerations
For maximum performance, observe the following rules of thumb:
Use the SoVertexProperty node to specify properties for the
vertex-based shapes if you have a scene that cannot be cached.
Use the vertexProperty eld of the shapes to directly include the
SoVertexProperty node; dont place the SoVertexProperty node in the
scene and inherit by the shape node.
Use the elds in the SoVertexProperty node to specify all the data you
need for a shape; dont mix and match data from the SoVertexProperty
node and inherited values from other nodes.
Specify normals and texture coordinates if you need them; dont rely on
the automatic generation, which is expensive if the scene cannot be
cached.
Dont use the convenience feature of specifying the number of vertices
as -1 (that is, use all remaining vertices); specifying the actual number is
more efcient.
26
Chapter 4: New Features
SoLOD Node
Open Inventor 2.1 has an improved version of the node that provides
switching between different levels of detail in the scene. The new node is
named SoLOD, to avoid conict with the old node, SoLevelOfDetail. For
compatibility, the old node is still supported.
The new SoLOD node switches between different levels, depending on the
distance of the object from the eye. The old SoLevelOfDetail node used
screen area to determine when to switch levels. Using distance is much faster
than using screen area.
SoLocateHighlight Node
SoLocateHighlight is a new, special separator that performs locate
highlighting. When the window cursor is over the contents of this separator,
it efciently redraws itself in a different color. This is useful for indicating hot
spots in a scene. See the reference page for more details.
VRML Nodes
The Open Inventor le format formed the basis for the Virtual Reality
Modeling Language (VRML), the industry-standard, platform-independent
le format for 3D graphics on the Internet. VRML 1.0 includes a few nodes
that were not in Inventor 2.0; these nodes have been added to Open Inventor
2.1, which is now a superset of VRML 1.0. The new nodes in Open Inventor
2.1 are:
SoWWWAnchor
SoWWWInline
SoAsciiText
SoFontStyle
See the reference pages for more information on these nodes. For more
information on VRML, visit the World Wide Web page at
http://vrml.wired.com.
New Fields
27
New Fields
This section looks at two new elds, The “fields Field and The isA
Field.
The “fields” Field
Inventor writes custom nodes with an extra eld named elds that lists the
names of all elds in the node along with their types. Inventor 2.0 les could
not be read if a elds eld was included in a node that was a standard part
of the library. To be fully compliant with VRML 1.0, Inventor 2.1 les now
support a elds eld in all nodes.
For example, the following code fragment is legal in an Inventor 2.1 le:
Cube {
fields [SFFloat width,
SFFloat height,
SFFloat depth,]
width 10
height 4
depth 3
}
The “isA” Field
When a new node type is a superset of an existing node, and an
implementation for the new node type cannot be found, the new node type
can be treated as the existing node it is based on (with some loss of
functionality).
To support this, new node types can dene an MFString eld called isA
containing the names of the types of which it is a superset.
For example, assume a new subset of Material called ExtendedMaterial
adds the index of refraction as a material property. This type can be dened
as follows:
28
Chapter 4: New Features
ExtendedMaterial {
fields [ MFString isA, MFFloat indexOfRefraction,
MFColor ambientColor, MFColor diffuseColor,
MFColor specularColor, MFColor emissiveColor,
MFFloat shininess, MFFloat transparency ]
isA [ "Material" ]
indexOfRefraction .34
diffuseColor .8 .54 1
}
If the ExtendedMaterial node is not known, an alternateRep eld
containing an SoMaterial node is automatically created.
Miscellaneous Additions
This section briey discusses:
•“SoGLRenderAction Render Abort Callback Changes
•“New Manipulator for Transformations
•“OpenGL Texture Object Extension
•“SoXtViewer Changes
SoGLRenderAction Render Abort Callback Changes
The render abort callback in Open Inventor 2.1 no longer returns a simple
Boolean value. Instead, it returns an abort code (similar to the
SoCallbackAction::Response code) that is one of CONTINUE, ABORT,
PRUNE, or DELAY.
CONTINUE is the same as returning FALSE in previous versions of
Inventor; it means that rendering should continue as usual.
ABORT is the same as returning TRUE in previous versions; it
terminates the current render traversal.
PRUNE indicates that traversal should skip the current node and all
nodes under it.
Miscellaneous Additions
29
DELAY postpones traversal of the current node until after all other
nodes have been traversed, just like the SoAnnotation node.
PRUNE and DELAY are new features that allow applications to modify
Inventors standard rendering order.
New Manipulator for Transformations
A new manipulator, SoTransformerManip, has been added to the set of
Inventor manipulators. It provides a full interface for rotation, translation,
and scale in three dimensions. This new manipulator provides better
feedback than the older manipulators, and uses the new locate highlighting
feature to indicate which of its parts to pick. See the reference page for more
details.
SoTransformerManip is used in IRIS Annotatorand WebSpace Author..
OpenGL Texture Object Extension
Open Inventor 2.1 makes use of the OpenGL extension for texture objects.
Consequently, you see improved texture mapping performance on systems
where this OpenGL extension is available.
SoXtViewer Changes
There are two changes to SoXtViewer:
When a viewer is in pick mode, you can temporarily switch to view
mode by holding down the <ALT> key.
The method SoXtViewer::setCursorEnabled() allows you to specify
whether the viewer is allowed to change the cursor over the render area
window. Disabling the cursor enables the application to set the cursor
directly. This is useful, for example, if you want to set a busy cursor in
the window.
30
Chapter 4: New Features
New and Updated Utilities
The following new and updated utilities are available in Inventor 2.1 and are
discussed in this section:
•“File Downgrade Utility
•“Program for Converting Files to Use Vertex Properties
•“Program for Optimizing Scene Graphics
•“Program for Analyzing Rendering Performance
Note: The utility program ivquicken is no longer supported.
File Downgrade Utility
A new utility program, /usr/sbin/ivdowngrade, takes an Inventor le and
converts it to Inventor version 2.0 or 1.0. See the reference page for more
details.
Program for Converting Files to Use Vertex Properties
The ivAddVP utility program helps convert Inventor les into 2.1 les that
use the vertexProperty eld for the vertex-based shapes. The source code,
including a Makele, is distributed in the inventor_dev.src.sample subsystem.
It is installed in the directory /usr/share/src/Inventor/tools/ivAddVP.
New and Updated Utilities
31
Program for Optimizing Scene Graphics
A new utility program, /usr/sbin/ivfix, restructures Inventor scene graphs for
improved rendering performance. ivfix rst analyzes the organization of the
input scene graph and tries to sort it to take advantage of coherence. For
example, it tries to organize subgraphs by common textures, since switching
textures is expensive. Once sorting is complete, ivfix also tries to combine
subgraphs so that the nal result has fewer nodes. Finally, ivfix “flattens the
subgraphs, tessellating all shapes into triangles that are then organized into
triangle strips. For example, two spheres may be combined into one triangle
strip.
The source code to ivfix, including a Makele, is distributed in the
inventor_dev.src.sample subsystem. It is installed in the directory
/usr/share/src/Inventor/tools/ivfix.
Program for Analyzing Rendering Performance
The utility program ivperf, for analyzing rendering performance, is provided
in source code format in the inventor_dev.src.sample subsystem. It is installed
in the directory /usr/share/src/Inventor/tools/ivperf. For information on ivperf,
see Chapter 6, Optimizing Open Inventor Applications.
33
Chapter 5
5. Incompatible Extender API Changes
This chapter is for developers who have customized existing Inventor 2.0
classes or have created subclasses of them. If you have problems porting an
application with custom classes or subclasses, this chapter helps you solve
them by explaining the new features and changes to the protected and
extender methods.
Note: To make your transition to Inventor 2.1 easier, the old version of the
API is still supported in some cases. To ush out cases where your code is
still using the old version, compile your program with -DIV_STRICT.
This chapter discusses the following topics:
•“Methods on page 33
•“Elements on page 37
•“Shape Nodes on page 40
•“Material Property Nodes on page 45
•“Engine Evaluation on page 46
Methods
This section provides information about changed methods, discussing the
following topics:
•“The GLRender() Method
•“readInstance() Method
•“copy() Method
34
Chapter 5: Incompatible Extender API Changes
The GLRender() Method
There are two potential changes when working with a GLRender() method:
•“Overriding Existing GLRender() Methods
•“Implementing SoSeparator GLRender() Methods Efciently
For additional information, see How to Convert GLRender() Methods on
page 42 in Managing the Material State.
Overriding Existing GLRender() Methods
Overriding an existing nodesGLRender() method by registering a method
using SoGLRenderAction::addMethod() no longer works.
SoGLRenderAction no longer uses the static action/method table for its
traversal, but instead calls GLRender() methods directly (see
Implementing SoSeparator GLRender() Methods Efciently).
Instead of calling SoGLRenderAction::addMethod(), implement a subclass
of the node(s) with the GLRender() methods you wish to override and use
the SoType::overrideType() method in its initClass() routine instead of the
normal SO_NODE_INIT_CLASS() macro. See the comments in SoType.h for
more information on SoType::overrideType().
Implementing SoSeparator GLRender() Methods Efciently
To make rendering traversal faster, SoNode now denes four different
rendering methods: GLRender(),GLRenderBelowPath(),
GLRenderInPath(), and GLRenderOffPath(). By default, the three new
methods just call GLRender(). However, for optimal rendering speed, group
nodes can redene GLRender() to call one of the other methods based on the
current path code (the SoAction::getPathCode() method returns the current
path code).
For example, SoSeparator denes a GLRender() method that calls
SoSeparator::GLRenderBelowPath() or SoSeparator::GLRenderInPath(),
based on the path code. Once GLRenderBelowPath() is called, traversal is
faster, since the path code is guaranteed not to change underneath the
separator, and the separator can just call its childrens
GLRenderBelowPath() methods.
Methods
35
If you have a class derived from SoSeparator and you implemented a
GLRender() method for that class, you need to implement
GLRenderBelowPath() and GLRenderInPath() methods; otherwise, the
methods of the SoSeparator are called and your special rendering code is
not executed.
readInstance() Method
This section discusses two changes to the readInstance() method:
Additional Argument to readInstance() and Implementation of
readInstance() for Group Nodes Changed.
Additional Argument to readInstance()
If you have node or engine subclasses with readInstance() methods, you
have to add an extra argument to your readInstance() method;
SoFieldContainer::readInstance() now takes the following arguments:
SoInput *in, unsigned short flags
If you call your base classsreadInstance() method, you must pass it the ags
argument. Otherwise, you can simply ignore this argument.
Implementation of readInstance() for Group Nodes Changed
If a group node reads children directly, you should have readInstance() code
that emulates the readInstance() code of SoGroup and checks to see if
children were written in the binary le format, as follows:
// Reads stuff into instance of SoGroup. Returns FALSE on
// error. Also deals with field data (if any), so this method
// should also be useful for most subclasses of SoGroup.
//
// Use: protected
SbBool
SoGroup::readInstance(SoInput *in, unsigned short flags)
{
SbBool readOK = TRUE;
// First, turn off notification for this node
SbBool saveNotify = enableNotify(FALSE);
// Read field info. We can’t just call
// SoNode::readInstance() to read the fields here
36
Chapter 5: Incompatible Extender API Changes
// because we need to tell the SoFieldData that it’s ok
// if a name is found that is not a valid field name -
// it could be the name of a child node.
SbBool notBuiltIn; // Not used
readOK = getFieldData()->read(in, this, FALSE,
notBuiltIn);
if (!readOK) return readOK;
// If binary BUT was written without children (which can
// happen if it was read as an unknown node and then
// written out in binary), don’t try to read children:
if (!in->isBinary() || (flags & IS_GROUP))
readOK = readChildren(in);
// Re-enable notification
enableNotify(saveNotify);
return readOK;
}
This change was made so that binary les containing unknown group nodes
with no children are correctly read.
copy() Method
Node copying now works correctly. For example, multiple references to a
node (through children, elds, or connections) under the node to be copied
are no longer copied individually; multiple instances of a single copy are
created instead.
These changes required a change to the extender API. The changes affect you
only if you have created a new node class and have implemented a copy()
method for it that differs from that of its base class.
In Inventor 2.1, the SoNode::copy() method is no longer virtual. Instead, the
copy() method creates an empty node of the correct type and then calls the
virtual copyContents() method on it to copy from the existing node. The
default implementation of copyContents() for SoNode copies the eld
values. The implementation for SoGroup copies the children as well. If your
node has other (non-eld, non-child) information that needs to be copied,
redene copyContents() for it. See The copyContents() Method on
page 120 for more information.
Elements
37
Elements
This section looks at element changes and related information in Inventor
2.1, discussing the following topics:
•“Changes in Shape Hints Element Methods
•“Changes in Binding Elements
•“Changes in Texture Elements
•“Changes in Material Elements
•“Implementation of Node Override
Changes in Shape Hints Element Methods
The SoShapeHintsElement methods setFilled(),setClipped(),isFilled(),
isClipped(),getDefaultIsFilled(), and getDefaultIsClipped() are no longer
supported.
Changes in Binding Elements
The DEFAULT and NONE values are obsolete for the nodes
SoMaterialBinding and SoNormalBinding. The corresponding elements
contain enum values for DEFAULT and NONE that make them the same as
OVERALL for materials and PER_VERTEX_INDEXED for normals. If the
code is compiled with -DIV_STRICT, these elements do not include values
for DEFAULT and NONE, and any reference to them causes a compile-time
error.
Changes in Texture Elements
The element SoGLTextureQualityElement is obsolete. Use
SoTextureQualityElement instead.
38
Chapter 5: Incompatible Extender API Changes
The following elements (and their OpenGL counterparts) are replaced by
SoTextureImageElement (and its OpenGL counterpart):
SoTextureBlendColorElement
SoTextureModelElement
SoTextureWrapSElement
SoTextureWrapTElement
Changes in Material Elements
Several elements that were responsible for material properties have been
combined into two elements: SoLazyElement and its derived OpenGL
version SoGLLazyElement.
SoLazyElement is responsible for setting and getting material state.
SoGLLazyElement is responsible for tracking the OpenGL state, and
issuing send commands, invalidating caches, and so on.
Both elements are required, because some actions (for example callback
actions) do not involve OpenGL. The following elements and their OpenGL
counterparts are replaced by the lazy elements:
SoDiffuseColorElement
SoSpecularColorElement
SoAmbientColorElement
SoEmissiveColorElement
SoTransparencyElement
SoShininessElement
SoLightModelElement
SoGLPolygonStippleElement
SoCurrentGLMaterialElement
SoGLColorIndexElement
Elements
39
Implementation of Node Override
In Inventor 2.0, the SoElement base class automatically implemented
override for all elements. Setting override on a node automatically caused an
override ag to be set on every element that node overrode.
In Inventor 2.1, a node has to explicitly implement override. A new element,
SoOverrideElement, has methods for getting and setting a bit for each
element that can be overridden. Nodes must cooperate with one another by
not doing anything if the bit for a particular element is set and by setting the
bit for an element if the nodesOverride ag is set.
Many of the set methods on Inventors built-in elements no longer take
SoNode * parameters. In cases where the only reason for the SoNode *
parameter was to test whether the node had its override set, the SoNode *
parameter was removed. Elements that are subclasses of
SoReplacedElement still take an SoNode * parameter in their set routines for
cache dependencies.
This change improves performance and cache dependencies on override
elements. Performance is improved because many nodes dont need
override and they no longer have to pay for it. Cache dependencies are
improved because the setting of the SoOverrideElement sets up the right
dependencies.
If you created your own element and want to implement override behavior
for it, you can either add and set override methods to the element (this is
easier to implement) or implement another element that stores override
information (this is better for caching).
40
Chapter 5: Incompatible Extender API Changes
Shape Nodes
This section provides information about implementing shape nodes with
Inventor 2.1, discussing the following topics:
•“Generating Normals
•“Managing the Material State
•“Using the Material Bundle
•“Getting a Bounding Box
Generating Normals
The SoNormalGenerator class constructors take an argument that
determines whether or not the polygons passed are in clockwise or
counterclockwise order. Inventor now correctly generates normals for
built-in shapes when the SoShapeHints node sets the vertex ordering to
CLOCKWISE.
You dont have to change extender shapes that use SoNormalBundle to
generate normals for them. You do have to modify extender shapes that use
the SoNormalGenerator class directly to get the vertex ordering out of the
ShapeHintsElement and pass TRUE or FALSE to the SoNormalGenerator
constructor.
Managing the Material State
Inventor 2.1 has a new, more efcient mechanism for managing the material
state during scene traversal. This section rst discusses the new
SoLazyElement and SoGLLazyElement, then provides instructions for
converting GLRender() methods that use the lazy elements.
Shape Nodes
41
Using SoLazyElement and SoGLLazyElement
The SoMaterialBundle is still supported for backward compatibility, but
you should use SoLazyElement and SoGLLazyElement for improved
performance. You also need to use the lazy elements if you are issuing any
of the following OpenGL calls:
glColor()
glMaterial()
glColorMaterial()
glEnable() or glDisable(), with the following arguments:
GL_COLOR_MATERIAL
GL_BLEND
GL_LIGHTING
GL_POLYGON_STIPPLE
glPushAttrib() or glPopAttrib(), with the following bits:
GL_ENABLE_BIT
GL_LIGHTING_BIT
GL_POLYGON_BIT
GL_POLYGON_STIPPLE_BIT
The lazy element tracks the OpenGL state for all components of the lazy
element. If you issue OpenGL calls that change that state, you must call
SoGLLazyElement::reset() to inform the lazy element that the OpenGL state
has changed.
It is important to keep track of state::push() and pop() calls that occur while
rendering, because the identity of the current lazy element will probably
change during a push and pop. This can cause problems if
SoMaterialBundle is used as in the Inventor 2.0 example code
/usr/share/src/inventor/examples/Toolmaker/02.Nodes/Pyramid.c++.
42
Chapter 5: Incompatible Extender API Changes
The SoShape::beginSolidShape() and SoShape::endSolidShape() methods
are unnecessary and actually can cause problems in that example, because
the destructor for the material bundle is invoked after a pop(). This
potentially invalidates state that is no longer current. The example code has
been revised to work correctly in Inventor 2.1 (see also Appendix A,
Creating a Node).
How to Convert GLRender() Methods
This section provides step-by-step instructions for converting a shape nodes
GLRender() method to use the lazy elements.
The shape nodes are responsible for managing transparency state, managing
glShadeModel, issuing glColorMaterial(), and issuing OpenGL calls to
send graphics state.
In the simplest case (one color per shape), you need only call
SoGLLazyElement::sendAllMaterial(), once, prior to rendering the
shape.
If there are multiple colors or transparencies in the shape, you may
need to do more.
In the most general case, follow these steps:
1. If your node is setting a new value in the lazy element state, call
SoLazyElement::setColorMaterial(),setBlending(), and so on.
When ColorMaterial is TRUE, glColor() calls modify the current
GL_DIFFUSE color. By default, ColorMaterial is FALSE. You should
only set it if it needs to be TRUE, and be sure to set it back to FALSE at
the end of rendering.
2. After youve set all values, but before any geometry has been sent to
OpenGL, issue one of the following calls:
SoGLLazyElement::sendAllMaterial()
SoGLLazyElement::sendNoMaterial()
SoGLLazyElement::sendOnlyDiffuseColor()
Shape Nodes
43
The sendAllMaterial() call ensures that all of the state managed by the
lazy element is current in OpenGL. If the light model is BASE_COLOR,
use SendOnlyDiffuseColor() instead, to avoid unnecessary OpenGL
calls to set ambient colors, specular colors, and so on. If you are
providing your own glMaterial() or glColor() calls, use
sendNoMaterial(). This ensures that the OpenGL state is current with
respect to transparency, light model, color material, and so on.
3. To send additional materials, for example, PER_VERTEX or PER_FACE,
to OpenGL, call SoGLLazyElement::sendDiffuseByIndex() or issue
OpenGL commands.
4. The OpenGL shade model is by default GL_SMOOTH. If you need to
have at shading, invoke glShadeModel(GL_FLAT) before rendering,
but be sure to set it back to GL_SMOOTH at completion of GLRender().
5. If you are managing transparency, it may be necessary to call
glEnable() or glDisable() with arguments GL_BLENDING or
GL_POLYGON_STIPPLE. Be sure to issue SoGLLazyElement::reset()
on the transparency components of the SoGLLazyElement, namely
TRANSPARENCY and BLENDING, if you called glEnable() or
glDisable() to affect transparency.
6. If you use stipple transparency, only the rst transparency value in the
state is used to determine the transparency value in the stipple pattern.
If you use other forms (additive or blending transparency), and if you
provide as many transparency values as diffuse colors, then
transparency varies across the shape, with a transparency being
associated with each diffuse color.
7. If you do not want to issue OpenGL calls but need to have multiple
colors sent while rendering a shape, use the sendDiffuseByIndex()
routine for all color send calls after the rst. You can use this routine to
send a specic diffuse color and/or transparency from the state. When
you invoke lazyElt::sendDiffuseByIndex(), make sure the lazy element
that you are using is current, that is, that there is no push() or pop()
between:
44
Chapter 5: Incompatible Extender API Changes
SoGLLazyElement* lazyElt =
(SoGLLazyElement*)SoLazyElement::getInstance(state);
and
lazyElt->sendDiffuseByIndex();
8. If you issued glColor() or glMaterial() calls, or call
sendDiffuseByIndex() during rendering, you have to inform the lazy
element that the OpenGL state is not the same as the Inventor state. Call
SoGLLazyElement::reset(state, bitmask)at the completion of rendering,
with bitmask the logical OR of one or more of the following bitmasks,
depending on which material you sent:
SoLazyElement::DIFFUSE_MASK
SoLazyElement::AMBIENT_MASK
SoLazyElement::EMISSIVE_MASK
SoLazyElement::SPECULAR_MASK
SoLazyElement::SHININESS_MASK
Make sure that the lazy element to which reset() is applied is current.
Dont issue a push() or pop() between obtaining the lazy element and
applying reset().
After rendering, call SoLazyElement::setColorMaterial(FALSE) to
disable glColorMaterial(), if color material was enabled.
Using the Material Bundle
For backward compatibility, the material state of a shape can be managed (as
in Inventor 2.0) by use of SoMaterialBundle. You have to set up the
GLRender() method of the shape as follows:
Constructor: SoMaterialBundle mb(action);
Prior to rst rendering: mb.sendFirst()
For each new material: mb.send(index)
Destructor: (implicitly invoked at the end of GLRender())
Material Property Nodes
45
For correct use of the material bundle, there should not be a push() or pop()
of state between mb.sendFirst() and the invocation of the destructor, for
example, consider the following code fragment:
beginSolidShape(action)
{//Scope material bundle
SoMaterialBundle mb;
...
}
endSolidShape(action);
Do not, for example, invoke SoShape::endSolidShape() until after the end
of the scope of the material bundle because it pops the state, for example:
{//Don’t do it this way
SoMaterialBundle mb;
beginSolidShape(action)
...
endSolidShape(action)
}
Getting a Bounding Box
If an Inventor 2.1 shape consists of lines and/or points, you must implement
agetBoundingBox() method that calls rst the parents method and then:
SoBoundingBoxCache::setHasLinesOrPoints(action -> getState())
See Getting a Bounding Box on page 93 for more detailed information.
Material Property Nodes
If you implement a property node to manage material state (colors, light
model, or transparency), note that in the doAction(action) method (that is,
when the node is traversed), material properties are set in the state by
SoLazyElement methods (see SoLazyElement.h):
setTransparency
setDiffuse
setAmbient
46
Chapter 5: Incompatible Extender API Changes
setSpecular
setShininess
setEmissive
setPacked
setLightModel
setColorIndices
It is not necessary send materials to OpenGL during traversal of property
nodes in Inventor 2.1. For efciency, the lazy element delays such sends until
they are required by the shape node.
If you set either the diffuse color or the transparency, but not both, use
SoColorPacker to pack the material you are setting into a packed color in the
state. If you are setting both diffuse color and transparency, you should pack
them both into a packed color and use SoLazyElement::setPackedColor to
set them in the state. See the Glow example (Example A-2 on page 84) for an
illustration of how SoColorPacker is used.
Before issuing a set() on an element, if that element can be overridden, check
SoOverrideElement for the status of the override on that element. It is now
the responsibility of the application to test for override, and to not set the
corresponding property in the state if override is set.
Engine Evaluation
If youve written engines that do not use the SO_ENGINE_OUTPUT macro
but instead directly write into the elds they are connected to then you have
to make the following change: before writing to a connected eld, you must
check if the eld is not read only (!eld::isReadOnly()) instead of checking
if the eld is engine modifying (eld::isEngineModifying()). This change
was made as part of the optimization of engine notication and evaluation.
47
Chapter 6
6. Optimizing Open Inventor Applications
This chapter explains how to determine what is limiting the performance of
your Open Inventor application, and provides suggestions on how to
improve its performance.
Note: This chapter was previously available as a small booklet. The
information has been updated to Inventor 2.1 as appropriate.
The chapter discusses the following topics:
•“Benchmarking Tips on page 48Describes a process for effectively
investigating and improving performancediscovering where an
application is spending its time is not a matter of random trial and
error.
•“Optimizing Rendering on page 52Provides for each step in the
rendering process:
Information about that step.
A method for determining how much time your application is
spending in that step.
Techniques for reducing that time.
•“Optimizing Everything Else on page 68Suggests ways of
structuring your scene and application for maximum performance
when doing tasks other than rendering (for example, picking or
modifying the scene).
For more information on Open Inventor programming in general and on
specic nodes, see The Inventor Mentor.
48
Chapter 6: Optimizing Open Inventor Applications
Benchmarking Tips
Like xing bugs or eliminating memory leaks, performance tuning is a
necessary chore during application development. Proper organization and
planning can speed up this chore and make it more pleasant.
This section looks at the steps to take when optimizing your application.
discussing these topics:
1. Setting Performance Goals
2. Measuring Performance
3. Determining Bottlenecks
4. Modifying Your Application to Reduce Bottlenecks
5. Finally, measuring your application against your goal again and
repeating steps 2, 3 and 4 above until performance meets your goal (see
Are You Finished Yet? on page 51).
Setting Performance Goals
Setting a performance goal helps you use your time wisely. Typically, you
should decide on a desired frame rate, such as running at 20 frames per
second with a particular scene. A reasonable performance goal for
interactive programs is a frame rate of at least 10 frames per second. Most
users nd that frame rate acceptable for most tasks (more is always better, of
course).
When setting a goal, keep in mind the capabilities of your hardware. If the
absolute top speed for drawing polygons on your system is 60,000 unlit,
single-color triangles per second, dont try to get 10 frames per second while
drawing 6,000 lit, color-per-vertex triangles. Write short OpenGL
benchmark programs, or feed test scene graphs to ivview -p to help set your
expectations.
Benchmarking Tips
49
Measuring Performance
It is important to have an objective way of measuring your applications
performance. Youre likely to waste time on insignicant optimizations if
you just watch your application run and try to see if it seems faster.
Adding code to your application that measures the number of polygons in
your scene and how fast they are being rendered is fairly simple; see, for
example, the source code for ivview in /usr/share/src/Inventor/tools/ivview.
The osview utility can also be useful. The swapbuf number in the Graphics
section tells you how many frames per second your application is getting
(assuming that your application is double-buffered, and that it is the only
double-buffered application running).
Note: Dont confuse osview with its graphical counterpart, gr_osview, which
doesnt have this feature.
Be sure to keep good records of your applications performance before you
start optimization. Comparing before and after performance numbers
ensures that you are not making things worse.
Determining Bottlenecks
Most applications spend most of their time executing a small part of the
code. Optimizing a procedure that is taking up only 5% of the total time is
probably not worthwhile: even if you manage to double the performance of
the procedure, the application will speed up by only 2.5%. In fact, on some
systems graphical operations can occur in parallel. For example, lling in
polygons and transforming polygon vertices might occur at the same time.
If the bottleneck is in the vertex transformation stage, increasing the pixel ll
time may not increase performance at all! Find the bottlenecks rst, and then
work on improving them.
Finding bottlenecks is an experimental science. You should rst come up
with a theory on where the bottleneck might be, then devise an experiment
that proves or disproves that theory. Create experiments that isolate one
small part of your applications performance, and make sure you
understand what you are measuring every time you run an experiment.
50
Chapter 6: Optimizing Open Inventor Applications
Optimizing Rendering and Optimizing Everything Else describe
frequently encountered bottlenecks, show how to determine the amount of
time your application is spending in each of them, and give suggestions on
improving them. The following topics are discussed:
•“Correcting Window Clear Bottlenecks
•“Improving Traversal Performance
•“Optimizing Pixel Fill Operations
•“Correcting Problems ivperf Does Not Measure
•“Correcting Culling Bottlenecks
•“Correcting Level of Detail Bottlenecks
•“Optimizing Memory Usage
•“Optimizing Action Construction and Setup
•“Decreasing Notication Overhead
•“Picking and Handling Events
Feel free to start with bottlenecks you suspect are responsible for the most
noticeable slowdown. You can look at the sections in any order; just make
sure you always know what you are measuring and keep good records of
your experiments.
Modifying Your Application to Reduce Bottlenecks
When you apply a performance optimization, make sure that the
modication is really an improvement: dont assume that all the suggestions
made in this (or any other) document automatically apply to your
application. For example, render culling usually increases performance.
However, if you have an application in which all objects are always visible,
render culling actually hurts performance.
Benchmarking Tips
51
Again, keep good records. Record what you do and how much it improves
performance. Try to minimize the number of things you change at any one
time; for example, if you make two optimizations and performance goes
up by 10%, the speedup might be caused by a 5% improvement for each
optimization, or might be caused by a 100% speedup caused by one
optimization and a 90% slowdown caused by the other! It is tempting to read
a document like this, make lots of changes, then see if the application gets
faster. This not only wastes time, it can also be counter-productive.
Are You Finished Yet?
One of the most frustrating things about optimizing an applications
performance is that it can be difcult to determine when you are done. Once
you have successfully eliminated one bottleneck, something else becomes
the factor limiting performance. Before spending more time on optimization,
you should ask yourself:
Did you meet your performance goal? If you did, go home early. If not,
try to nd other bottlenecks, consider eliminating features that hurt
performance, or reexamine your goals.
Is your application running at 60 or 72 frames per second?
Double-buffered programs never render faster than the refresh rate of
your monitor.
Do you need to experiment on different systems? Different systems
have different bottlenecks; on a system with very fast graphics, the
bottleneck is more likely to be either in the applications code or in
Inventors code. On a system with slow graphics and a fast CPU, the
bottleneck is more likely to be inside the OpenGL calls. If your
application will be used on different types of systems, make sure
performance is acceptable on all of them.
52
Chapter 6: Optimizing Open Inventor Applications
The Five Performance Commandments
1. Be scientic.
2. Keep good records.
3. Find bottlenecks.
4. Change one thing at a time.
5. Test all the types of systems your application supports.
Optimizing Rendering
The main goal of performance tuning is to make the application look and feel
faster. However, just because the goal is to make the application render
faster, dont assume that rendering is the bottleneck.
Determining Whether Rendering Is the Problem
To nd out whether rendering is the problem, modify your application so
that it does everything it normally does except render, and then measure its
performance. An easy way of getting your application to do everything but
rendering is to insert an SoSwitch node with its whichChild eld set to
SO_SWITCH_NONE (the default) above your scene. So, for example,
modify your applications code from:
myViewer->setSceneGraph(root);
To:
SoSwitch *renderOff = new SoSwitch;
renderOff->ref();
renderOff->addChild(root);
myViewer->setSceneGraph(renderOff);
This experiment gives an upper limit on how much you can improve your
applications performance by increasing rendering performance. If your
application doesnt run much faster after this change, then rendering is not
your bottleneck. See Optimizing Everything Else on page 68 for
information on optimizing the rest of your application.
Optimizing Rendering
53
Isolating Rendering
If you have determined that your application is spending a signicant
amount of time rendering the scene, the next step is to isolate rendering from
the rest of the things your application does. This makes it easier to nd out
where the bottleneck in rendering occurs. The easiest way to isolate
rendering is to write your scene to a le and then use the ivperf program to
perform a series of rendering experiments.
The code for writing your scene may look like the following:
SoOutput out;
if (!out.openFile(myScene.iv)) { ... error ... };
SoWriteAction wa(&out);
wa.apply(root);
Using the ivperf Utility to Analyze Rendering Performance
The ivperf utility reads in a scene graph and analyzes its rendering
performance. It estimates the time spent in each stage of the rendering
process while rendering the scene graph.
The process of rendering a single frame can be decomposed into ve main
stages:
1. Clearing the graphics window
2. Traversing the Inventor scene graph
3. Changing the graphics state (including materials, transformations, and
textures)
4. Transforming vertices in the graphics pipeline
5. Filling polygons
54
Chapter 6: Optimizing Open Inventor Applications
The sum of the times spent in these stages does not, in general, equal the
total time it takes to render the scene. Depending on the underlying
hardware platform and graphics pipeline, some or all of the above can
overlap with each other. Thus, completely eliminating one of the stages does
not necessarily speed up the application by the time taken by that stage.
ivperf takes this into account; it answers questions of the type if I could
completely eliminate xxx from my scene, how much faster would rendering
be? For example, if ivperf indicates that 50% of your time is spent changing
the material graphics state, then making your entire scene a single material
would make it render twice as fast. Knowing that materials are taking up a
signicant part of your rendering time, you can then concentrate on
minimizing the number of material changes made by your scene.
If you have created your own node classes, either create DSOs for them (see
DSO Directories and Versions on page 22) or call their initClass() methods
just after the call to SoInteraction:init() in the ivperf source and link their .o
les into ivperf.
The camera control used by ivperf is simplistic: it calls viewAll() for the scene
and just spins the scene around in front of the camera when benchmarking.
If you have a sophisticated walk-through or y-through application that
uses level of detail and/or render culling, modify ivperf so that its camera
motion is more appropriate for your application. For example, have ivperf
use the following little scene instead of just SoPerspectiveCamera:
TransformSeparator {
Rotor { rotation 0 1 0 .1 speed .1 }
Translation { translation 100 0 0 }
PerspectiveCamera { nearDistance .1 farDistance 600 }
}
ivperf correctly reports the performance of changing scenes, as long as you
give it enough information. It automatically deals with scenes containing
engines and animation nodes, but if you are using an SoSensor to modify
the scene, you should mark nodes that your application frequently changes
by giving them the special name NoCache. For example, if your
application is frequently changing a transformation in the scene, the
transformation should appear in the le given to ivperf as:
DEF NoCache Transform { }
Optimizing Rendering
55
Correcting Window Clear Bottlenecks
The rst step in the rendering process is clearing the window. It is easy to
forget this step, but depending on the size of your applications window and
the type of system you are running on, clearing the window can take a
surprisingly long time. If your applications main window is typically 1000
by 1000 pixels, run ivperf like this:
ivperf -w 1000,1000 myScene.iv
ivperf performs many different rendering experiments, and eventually prints
information on each rendering stage.
For example, on an Indigo2 Extreme running IRIX 5.3, ivperf reports that
for rendering a simple cube in a 1000 by 1000 pixel window 46% of the time
is spent clearing the window.
Unfortunately, if clearing the window takes too much time, there is not a lot
you can do. One possibility is to make the windows default size smaller
(while still allowing users to resize the window if necessary).
Improving Traversal Performance
After running ivperf, you know how much time your application spends
clearing the color and depth buffers. The next experiment is designed to nd
out how much time Inventor spends traversing your scene. Traversal is the
process of walking through the scene graph, deciding which render method
needs to be called at each node. Inventor automatically caches the parts of
your scene that arent changing and that are expensive to traverse, building
an OpenGL display list and eliminating the traversal overhead.
If most of your scene is changing, or if your scene is not organized for
efcient caching, Inventor may not be able to build render caches, and
traversal might be the bottleneck in your application. ivperf measures the
difference between rendering your scene with nothing changing, and
rendering with the camera, engines, and nodes named NoCache
changing.
56
Chapter 6: Optimizing Open Inventor Applications
If traversing the scene is a bottleneck in your program, there are several
ways of reducing the traversal overhead:
Reduce the number of nodes in the scene. For example, eliminate
SoSeparator and SoGroup nodes that have only one child by replacing
them with the child.
Use the vertexProperty eld of the vertex-based shapes to specify
coordinates, normals, texture coordinates, and colors. (See
SoVertexProperty Node on page 24).
Beware of features that require multiple traversals of the scene graph
for each render update. For example, avoid using accumulation buffer
antialiasing and SoAnnotation nodes, and use the transparency type
SoGLRenderAction::SCREEN_DOOR.
Organize your scene graph for caching. The next section discusses ways
of doing this. If you are using SoLOD nodes or render culling, also see
Correcting Culling Bottlenecks on page 66 and Correcting Level of
Detail Bottlenecks on page 67 for hints on optimizing those features,
which ivperf also reports as part of caching behavior.
Organizing the Scene for Caching
You may be able to organize your scene so that Inventor can build and use
render caches even if part of the scene is changing. Note that the following
things inhibit caching:
Changing elds in the scene destroys caches inside all SoSeparator
nodes above the node that changed. Even elds that do not affect
rendering, such as elds in the SoLabel or SoPickStyle nodes, destroy
caches if they are changed.
Tip: You can disable notication on these nodes using the
SoNode::enableNotify() method to keep changes to them from
destroying caches.
The SoLOD node (and the older SoLevelOfDetail node) breaks caches
above it whenever either the camera or any of the matrix nodes
affecting it change. Make the children of the SoLOD node SoSeparator
nodes, so that they will be cached. See Correcting Level of Detail
Bottlenecks for more information on efcient use of the SoLOD node.
Optimizing Rendering
57
Any shape using SCREEN_SPACE complexity breaks caches above it
whenever the camera or any of the matrix nodes affecting it change.
The SoText2 node breaks caches above it whenever the camera changes
(in order to correctly position and justify each line of text, it must
perform a calculation based on the camera). Since most applications
change the camera frequently, try to separate SoText2 nodes from the
other objects in your scene, to allow the other objects to be cached.
Tip: In Inventor 2.1, single-line, left-justied SoText2 nodes do not break
render caches.
Changing the override status of properties at the top of the scene, or
changing global properties such as SoDrawStyle or SoComplexity that
affect the rest of the scene, inhibits efcient caching. SoSeparator nodes
build multiple render caches (by default, a maximum of two) to handle
cases in which a small set of global properties are changed back and
forth, but you should avoid continuously changing a global property;
for example, putting an engine on the value eld of an SoComplexity
node at the top of your scene is bad for caching.
For more information on Inventors render caching, see Chapter 9 of The
Inventor Mentor.
Improving Material Change Bottlenecks
If ivperf reports that material changes are the rendering bottleneck, try the
following:
Use fewer material nodes. Group objects by material, and use one
material node for several objects.
Tip: When the ivx utility rearranges scene graphs, it groups objects by
material.
Changing between materials with different shininess values is much
more expensive than changing any of the other material properties.
If you are using shapes with a materialIndex eld, try to sort their parts
by material index to minimize material changes. For example, try to
change:
58
Chapter 6: Optimizing Open Inventor Applications
IndexedFaceSet { materialIndex [ 0,1,0,1,0,1,0,1 ] ... }
to:
IndexedFaceSet { materialIndex [ 0,0,0,0,1,1,1,1 ] ... }
This works only for PER_PART_INDEXED or PER_FACE_INDEXED
material bindings.
Optimizing Transformations
For transformation, ivperf reports two numbers: the overhead of changing
the OpenGL transformation matrix between rendering shapes and the time
it takes to transform the vertices in your scene through that matrix. This
section helps with the former, giving suggestions on how to make Inventor
execute fewer OpenGL matrix operations. See Optimizing Vertex
Transformations on page 61 for hints on optimizing the transformation of
vertices.
To measure how much time transformations might be taking, ivperf
temporarily removes all transformations from your scene and then
measures how much faster it runs. Beware! This sometimes gives
unreliable results; for example, if all your objects become very large or
very small without the transformations, then more (or less) time may be
spent lling in pixels. If your scene uses render culling, removing the
transformations makes more (or fewer) of the objects culled, distorting
the results reported by ivperf.
Use SoRotation, SoRotationXYZ,SoScale, or SoTranslation nodes
instead of the general SoTransform node. However, dont bother doing
this if you have to replace the SoTransform node with more than one of
the simpler nodes to get the same transformation.
Performance Tip for Face Sets
For best performance when creating SoFaceSet and SoIndexedFaceSet
shapes, arrange all the triangles rst, then quads, and then other faces.
Optimizing Rendering
59
Optimizing Textures
If your scene contains textures, ivperf reports two numbers: the time you
would save if you could turn off textures completely, and the time you
would save if you could make your scene use only one texture. On systems
with texturing hardware, the number of textures used can dramatically
affect performance; see Optimizing Texture Management on page 59 for
hints on optimizing texture management. On systems without texture
mapping hardware, the bottleneck is probably lling in the textured
polygons.
Inventor 2.1 automatically does two things to speed up rendering on
systems without texture mapping hardware:
Inventors viewers display the scene untextured during interaction by
default.
Inventor uses lower-quality lters for minifying or magnifying
textures.
Optimizing Texture Management
If ivperf reports a lot of time is spent in texture management, then you are
running out of hardware texture memory. Try the following:
Use smaller textures. Use the izoom utility to scale down the images you
are using; inadvertently using one big image can easily ll up texture
memory on many systems.
Make textures a power of 2 wide and high. Textures of those
dimensions (for example 128 x 64 instead of 129 x 70) make startup
faster.
Reuse nodes. Inventor allows you to modify a texture once it has been
read into your application (using the image eld of SoTexture2), and to
change the search path for textures (using methods on SoInput). It
therefore does not use the same texture memory for two different
SoTexture2 nodes with the same lename eld. Be sure to reuse the
same SoTexture2 node instead of creating another node with the same
lename.
60
Chapter 6: Optimizing Open Inventor Applications
For example, this scene is inefcient:
Separator {
Texture2 { filename foo.rgb }
Cube { }
}
Sphere { }
Separator {
Texture2 { filename foo.rgb }
Text3 { string Hello }
}
This scene uses texture-memory efciently:
Separator {
DEF foo Texture Texture2 { filename foo.rgb }
Cube { }
}
Sphere { }
Separator {
USE foo
Text3 { string Hello }
}
Textures are not shared if they are below an SoSeparator with
renderCaching turned on. Textures use less texture memory by
building an OpenGL display list or texture object containing the
OpenGL texture commands. However, if an OpenGL display list is
already created the rst time an SoTexture2 node is traversed, it must
add the texture commands to the already open display list. Inventors
automatic caching algorithm handles this correctly; it is only a problem
if you turn caching on explicitly. For example, this scene uses twice as
much texture memory with the renderCaching eld of the SoSeparator
set to ON:
Separator { renderCaching ON # BAD
DEF TEX Texture2 { filename foo.rgb }
Cube { }
... more stuff, other textures, etc...
USE TEX
Cube { }
}
Optimizing Rendering
61
Use SoLOD nodes to create simpler versions of your objects that are
not textured or use smaller texture images when the objects are far
away.
Use render culling so the textures for textured objects outside the view
volume are not used. For example, imagine a scene that contains 100
textured objects (each with a unique texture), but only 10 of them are in
the view volume at any given time. When the scene is rendered, only 10
of the textures need to be in texture memory at any given time,
resulting in much better texture management performance.
Using Lights Efciently
If the scene given to ivperf contains light sources, ivperf informs you how
expensive they are compared to rendering your scene with just a single
directional light. If ivperf reports that lights are a signicant performance
bottleneck, try to use fewer light sources, and use simpler lights (a
DirectionalLight is simpler than a PointLight, which is simpler than a
SpotLight). If possible, put lights inside separators so that they affect only
part of the scene, increasing performance for the rest of the scene.
Optimizing Vertex Transformations
If ivperf reports that vertex transformations (which include per-vertex
lighting calculations) take up a signicant portion of the time it takes to
render a frame, you can do the following to optimize per-vertex operations:
Use fewer vertices in your objects. Use SoComplexity to turn down
complexity for Inventors primitive objects. If you are using a system
with hardware-accelerated texturing, texturing can be used to add
visual complexity with very few vertices.
62
Chapter 6: Optimizing Open Inventor Applications
Create less detailed versions of your objects and use SoLOD nodes so
that fewer vertices are drawn when objects are small. Use an empty
SoInfo node as the lowest level of detail so that objects disappear when
they get very small. A good rule of thumb for choosing levels of detail
is that the switch between levels of detail should be fairly obvious if
you are concentrating on the object; for most applications, the user
concentrates on objects in the foreground and does not notice
background objects popping between levels of detail. Beware that
SoLOD nodes cause smaller caches to be built, which may slow down
traversal. See Correcting Level of Detail Bottlenecks for more
information on efcient use of level of detail.
Make your vertices simpler. Try to use OVERALL rather than
PER_VERTEX material binding. Turn off fog. Note that these
suggestions are system-specic; on systems with a lot of hardware for
accelerated rendering, fogged vertices may be no slower than plain
vertices. Be sure to do a quick ivperf test before spending time
modifying your application.
Make sure you are not turning on two-sided lighting unnecessarily;
avoid SoShapeHints nodes that:
set vertexOrdering elds to COUNTERCLOCKWISE or
CLOCKWISE and
set shapeType elds to UNKNOWN_SHAPE_TYPE
If parts of your scene do not require lighting, use an SoLightModel
node set to model BASE_COLOR to turn off lighting for those parts of
the scene. However, be aware that turning lighting on and off can itself
become a bottleneck if done too often.
If you are using SoFaceSet or SoIndexedFaceSet, try using ivx to
convert them into SoIndexedTriangleStripSet, which draws more
triangles with fewer vertices. Note that ivx cannot create a mesh if
your objects have sharp facets or PER_FACE material or normal
bindings.
Watch out for expensive primitives with lots of vertices, like SoText3
and SoSphere. ivperf reports the number of triangles in your scene;
make sure the number is reasonable for your desired performance.
Optimizing Rendering
63
Organize your scene graph so that objects that are close to each other
spatially are under the same SoSeparator, and turn on render culling so
that Inventor wont send those objects vertices when the objects are not
in view. See The Inventor Mentor, Chapter 9, for more information on
render culling.
See Making Inventor Produce Efcient OpenGL on page 64 for hints
on making Inventor produce more efcient OpenGL calls.
Optimizing Pixel Fill Operations
A common bottleneck on low-end systems is drawing the pixels in lled
polygons. This is especially common for applications that have just a few
large polygons, as opposed to applications that have lots of little polygons.
If ivperf reports that a large percentage of each frame is spent lling in pixels,
try to optimize your scene as follows:
Render your scene, or parts of your scene, in wireframe or as points
when possible. Viewers have move wireframe and move points
modes built in for exactly this case.
Some systems can ll at-shaded polygons faster that Gouraud-shaded
polygons. Triangle strips and quad meshes set shademodel(FLAT) if
they have PER_FACE normals and dont have PER_VERTEX materials
(and vice versa).
SCREEN_DOOR transparency (the default) is faster than blended
transparency on some systems (it is slower on other systems). Use the
setTransparencyType() method on either SoXtRenderArea or
SoGLRenderAction to change the transparency type.
Correcting Problems ivperf Does Not Measure
There are several performance problems that ivperf doesnt catch. The
following sections describe them, and give hints on how to improve them.
64
Chapter 6: Optimizing Open Inventor Applications
Making Inventor Produce Efcient OpenGL
If your application is rendering only 10 frames per second with 1,000
triangles per frame, and you know that your graphics hardware is capable
of rendering 100,000 triangles per second (10,000 triangles per frame at 10
frames/second), and ivperf reports that your bottleneck is vertex
transformations, then your problem might be that Inventor is not making
efcient OpenGL calls.
Inventor is much more efcient at rendering multiple triangles if they are all
part of one node. For example, you can create a multifaceted polygonal
shape using a number of different coordinate and face set nodes, as shown
in the lower half of Figure 6-1. A much better technique is to put all the
coordinates for the polygonal shape into one SoCoordinate or
SoVertexProperty node, and the description of all the face sets into a second
SoFaceSet node, as shown in the upper half of Figure 6-1.
Tip: The ivx utility program collapses multiple shapes into single triangle
strip sets.
Optimizing Rendering
65
Figure 6-1 Condensing Face Sets Into Fewer Nodes
faceset2
faceset1
coords1 coords2 faceset3
coords3
all_coords faceset
Not Efficent
Efficent
66
Chapter 6: Optimizing Open Inventor Applications
Using fewer nodes to get the same picture reduces traversal overhead for
scenes that cannot be cached. Note also that Inventor optimizes on a node by
node basis and generally cant optimize across nodes.
An SoFaceSet or SoIndexedFaceSet has special code for drawing 3 and
4-vertex polygons. To take advantage of that, you must arrange the polygons
so that the 3-vertex polygons (if any) are rst in the coordIndex array,
followed by the 4-vertex polygons, followed by the polygons with more than
4 vertices.
For some applications, consider implementing your own nodes that
implement the functionality of a subgraph of your scene. For example, a
molecular modeling application might implement a BallAndStick node
with elds specifying the atoms and bonds in a molecule, instead of using
the more general SoSphere,SoCylinder,SoMaterial,SoTransform, and
SoGroup nodes. If the molecular modeling application changes the
molecule frequently so Inventor cannot cache the scene, using a specialized
node could make traversal orders of magnitude faster (for example, a simple
water molecule scene graph with three atoms and two bonds might consist
of 20 nodes; replacing this with a single BallAndStick node would make
traversal 20 times faster). The BallAndStick node could also perform
application-specic optimizations not done by Inventor, such as not
drawing bonds between spheres whose radii were large enough that they
intersected, sorting the spheres and cylinders by color, and so on. See The
Inventor Toolmaker for complete information on implementing your own
nodes.
Correcting Culling Bottlenecks
If your application uses render culling, it may be spending most of its time
deciding whether or not objects should be culled. ivperf lumps this in with
bad caching behavior. To nd out whether this is the case, use prof,pixie, or
the CaseVision/WorkShop Performance Analyzer tools to look for a lot of
CPU time being spent in the SoSeparator::cullTest() or
SoBoundingBoxAction::apply() routines. See the reference pages for prof,
pixie, or cvspeed for information on using these tools.
Optimizing Rendering
67
If a large percentage of the rendering time is spent doing cull tests, try to
reorganize your scene so that more triangles are culled for each culling
SoSeparator. For example, if you have a city scene with thousands of
buildings, it may be better to perform one cull test for each city block rather
than the thousands of cull tests needed to decide whether or not each
individual building is visible. Doing this also allows Inventor to build larger
render caches, which may increase traversal speed.
Also, remember that render culling breaks render caches when the camera
or transformation matrices change, so double-check to make sure that no
SoSeparator nodes above an SoSeparator doing render culling have their
renderCaching elds set to ON.
If a lot of time is being spent inside SoGetBoundingBoxAction::apply(),
something is breaking bounding box caches.
Correcting Level of Detail Bottlenecks
If your application uses SoLOD nodes, it might be spending a signicant
amount of time deciding which level of detail should be drawn. One way of
testing to see if this is the case is to temporarily replace all of the SoLOD
nodes in your scene with SoSwitch nodes set to traverse the highest level of
detail. Then run ivperf again and compare the results. If the SoSwitch node
scene is much faster, try doing the following:
Try to group objects so that one level of detail test determines the level
of detail for several objects. For example, if you have a group of 10
buildings that are near each other, use one level of detail node instead
of 10 level of detail nodes. Doing this also makes it easier for Inventor
to build larger render caches, which may increase performance by
increasing traversal speed.
Remember that level of detail nodes break render caches when the
camera or transformation matrices change, so make sure that no
SoSeparator nodes above an SoLOD have their renderCaching elds
set to ON.
Make sure you use the SoLOD node introduced in Inventor 2.1 instead
of the SoLevelOfDetail node. The SoLOD node is more efcient
because it uses the distance to a point as the switching criterion. See the
reference page for more detail.
68
Chapter 6: Optimizing Open Inventor Applications
Making Your Application Feel Faster
Sometimes it is worthwhile to sacrice features temporarily to make your
application seem faster to the user. Inventor has several features that make
this easier:
Use the SoGLRenderAction::setAbortCallback() method to interrupt
rendering before the entire scene has been drawn. For this to be most
effective, you must organize your scene so that the most important
objects are drawn rst, and you should abort only when it is important
that rendering happen quickly, even if the rendering is not complete,
such as when the user is interactively manipulating the scene.
Use one of the Move ... draw styles if you are using a viewer, so that a
simpler version of the scene is drawn when the user is interacting with
the viewer.
Use the start and nish callbacks of manipulators and components to
temporarily modify the scene to make it simpler while the user is
interacting with it.
Optimizing Everything Else
If you have determined that rendering is not your bottleneck, or if you have
already optimized rendering as much as possible and a signicant amount
of time is still being spent doing something other than rendering, its time to
look for other bottlenecks. This section helps you nd other bottlenecks, and
suggests Inventor-specic things to look for by discussing the following:
•“Useful Tools
•“Optimizing Memory Usage
•“Looking at CPU Usage
•“Optimizing Action Construction and Setup
•“Decreasing Notication Overhead
•“Picking and Handling Events
Optimizing Everything Else
69
Useful Tools
The standard performance analysis tools (prof,pixie, or the
CaseVision/WorkShop Performance Analyzer) make performance analysis
of the non-graphics part of your application easy. See the reference pages for
prof,pixie, or cvspeed for information on using these tools.
Optimizing Memory Usage
First, make sure your application isnt running out of physical memory by
running gr_osview with the -a ag and looking for swap in the CPU Wait
usage bar. If your application is swapping, try to reduce its memory usage
as follows:
Turn off render caching. Call SoSeparator::setNumRenderCaches(0)
just after initializing Inventor to globally turn off automatic render
caching. You can also turn off render caching for parts of your scene
using the renderCaching eld of SoSeparator.
The automatic caching algorithm in Inventor 2.1 avoids caching notes
that contain a large number of polygons.
If you are using caching, avoid using PER_FACE or
PER_FACE_INDEXED materials or normal bindings for
SoTriangleStripSet,SoIndexedTriangleStripSet, and SoQuadMesh
nodes. FACE bindings force Inventor to break each triangle or quad
into an individual triangle or quad, more than doubling the space the
node takes in the render cache.
If you have SoBaseColor or SoMaterial nodes containing just diffuse
colors, change them to SoPackedColor nodes, which use less memory.
Use instancing instead of duplicating geometry or properties wherever
possible. Instancing makes your scene graph take up less memory and
enables Inventor to build OpenGL display lists that are used more than
once. This is especially important for SoTexture2 nodes.
70
Chapter 6: Optimizing Open Inventor Applications
Looking at CPU Usage
If memory is not the problem, start by looking at inclusive CPU times for
your procedures (inclusive times include time spent in that procedure and
all procedures it calls; exclusive times are just the time spent in that
procedure). Ignore the very highest level routines like main() or
SoXt::mainLoop(); look for Inventor beginTraversal() methods or for
application routines that are taking a signicant percentage of time. If a lot
of time is spent in SoGLRenderAction::beginTraversal(), see Optimizing
Rendering on page 52 for information on improving rendering
performance.
If your application is spending a lot of time in code written by you, you are
on your own! The rest of this section describes Inventor routines that often
show up on prole traces, describes what these routines do, and suggests
ways of using them more efciently.
Optimizing Action Construction and Setup
Inventor actions perform a lot of work the rst time they are applied to a
scene (subsequent traversals are very fast). Therefore, if your performance
traces show a lot of time being spent inside an actions constructor or the
SoAction::setUpState() method, try to create an action once and reapply it
instead of constructing a new action. For example, if you often compute the
bounding boxes of some objects in the scene, keep an instance of an
SoBoundingBoxAction around that is reused:
static SoGetBoundingBoxAction *bbAction = NULL;
if (bbAction == NULL) bbAction = new SoGetBoundingBoxAction;
bbAction->apply(myScene);
instead of the much less efcient:
SoGetBoundingBoxAction bbAction;
// inefficient if called a lot!
bbAction.apply(myScene);
Optimizing Everything Else
71
Decreasing Notication Overhead
Every time you change a eld in the scene, Inventor performs a process
called notication. A notication message travels up the scene graph to the
nodes parents, scheduling sensors, causing caches to be destroyed, and
marking any connections to engines or other elds as needing evaluation.
If your performance traces show a lot of time is being spent in a startNotify()
method, try the following to decrease notication overhead:
If you are modifying several values in a multiple-valued eld, use the
setValues() methods or the startEditing()/nishEditing() methods
instead of repeatedly calling the set1Value() method.
Build scenes from the bottom up. Set leaf nodes elds rst, then add
them to their parents, then add the parents to their parents, and so on.
For example, do this:
SoCube *myCube = new SoCube;
c->width = 10.0;
SoCylinder *myCylinder = new SoCylinder;
myCylinder->radius = 4.0;
SoSwitch *mySwitch = new SoSwitch;
mySwitch->whichChild = 0;
mySwitch->addChild(cube);
mySwitch->addChild(cylinder);
SoSeparator *root = new SoSeparator;
root->ref();
root->addChild(mySwitch);
instead of the less efcient:
SoSeparator *root = new SoSeparator;
root->ref();
SoSwitch *mySwitch = new SoSwitch;
root->addChild(mySwitch);
mySwitch->whichChild = 1;
SoCube *myCube = new SoCube;
mySwitch->addChild(myCube);
myCube->width = 4.0;
SoCylinder *myCylinder = new SoCylinder;
mySwitch->addChild(myCylinder);
myCylinder->radius = 4.0;
72
Chapter 6: Optimizing Open Inventor Applications
Using many path sensors can cause notication to become slow, since
an SoPathSensor is notied whenever any change happens underneath
the head node of the SoPath monitored by the SoPathSensor.
Note: SoPath itself does not have this problem in Inventor 2.X (but did
in Inventor 1.X).
Notication can be enabled or disabled on a per-node or per-engine
basis. Beware that because caching, sensors, and connections rely on
notication for proper operation, you must be very careful when using
this feature. See the SoFieldContainer reference page for information
on the enableNotify() method.
Picking and Handling Events
If your application proles show a lot of time is spent inside the methods
SoHandleEventAction::beginTraversal() or
SoPickAction::beginTraversal(), try the following to improve picking
and/or event handling performance:
Insert SoPickStyle::UNPICKABLE nodes in your scene to turn off
picking for objects that should never be picked (for example dead
background graphics).
Insert SoPickStyle::BOUNDING_BOX nodes in your scene if you do
not need detailed picking information. This helps most for complicated
objects like SoText3 or SoTriangleStripSets with many triangles.
If you have objects with a lot (thousands) of polygons in them, break
them up into several objects under different separators, grouping
polygons that are close to each other. This allows SoSeparator pick
culling to quickly reject many of the triangles.
To speed up event handling, try to put active objects that respond to
events toward the left and top of the scene graph. An
SoHandleEventAction ends traversal as soon as a node reports that it
has handled the event.
If you write your own event callback node, or implement a node that
responds to events, be sure to use the grabEvents() method when
appropriate. Because grabbing short-circuits traversal of the scene, it is
a useful way to speed up event distribution.
73
Appendix A
A. Creating a Node
This appendix explains how you can create new subclasses of SoNode. It
discusses enabling elements in the state, constructing a node, and
implementing actions for it. Chapter 1 in The Inventor Toolmaker provides
important background material on these concepts, and this appendix
assumes you are familiar with the material presented there.
Note: This appendix provides an update of Chapter 2, Creating a Node,
of The Inventor Toolmaker. Its main purpose is to help developers who are new
to Inventor work with Inventor 2.1 without being hindered by outdated
information. Developers familiar with Inventor may prefer to look at
Chapter 5, Incompatible Extender API Changes and at the revised
example programs instead of reading this complete appendix.
The rst part of this appendix offers an overview of the steps required to
create a new node. When necessary, additional sections explain key concepts
in further detail and list the relevant macros. Next, the appendix examples
show how to create three new node classes:
•“Creating a Property Node on page 82
•“Creating a Shape Node on page 88
•“Creating a Group Node on page 108
Sections at the end of the chapter discuss the following:
•“Using New Node Classes on page 116
•“Creating an Abstract Node Class on page 120
•“The copyContents() Method on page 120
•“The affectsState() Method on page 121
•“Uncacheable Nodes on page 121
•“Generating Default Normals on page 122
•“Creating an Alternate Representation on page 122
74
Appendix A: Creating a Node
Overview
The le SoSubNode.h contains the macros for dening new node classes. The
SO_NODE_HEADER() macro declares type identier and naming variables
and methods that all node classes must support. The SO_NODE_SOURCE()
macro denes the static variables and methods declared in the
SO_NODE_HEADER() macro. Other macros useful in creating new node
classes are mentioned in the following sections.
Creating a new node requires these steps:
1. Select a name for the new node class and determine what class it is
derived from.
2. Dene and name each eld in the node.
3. Dene an initClass() method to initialize the type information and to
ensure that the required elements are enabled in the state (see
Initializing the Node Class on page 75).
4. Dene a constructor (see Dening the Constructor on page 76).
5. Implement the actions supported by the node (see Implementing
Actions on page 78).
For a property node, you usually need to implement the
GLRender() and callback() methods (see Creating a Property
Node on page 82). You may also need to implement
getBoundingBox(),getMatrix(), and other methods.
For a shape node, you need to implement the generatePrimitives()
method for the SoCallbackAction as well as the getBoundingBox()
method. You may want to implement a specicGLRender() or
rayPick() method as well (see Creating a Shape Node on
page 88). For vertex-based shapes, you may need to implement a
generateDefaultNormals() method.
For a group node, you need to implement all actions to ensure that
the children are traversed (see Creating a Group Node on
page 108).
6. Implement a copyContents() method if the node contains any non-eld
instance data (see The copyContents() Method on page 120).
7. Implement an affectsState() method if it cannot be inherited from the
parent class (see The affectsState() Method on page 121).
Initializing the Node Class
75
Initializing the Node Class
As discussed in Chapter 1 of The Inventor Toolmaker, the initClass() method
sets up the type identier and le format name information for the class. The
initialization macro for nodes, SO_NODE_INIT_CLASS(), does most of the
work for you. One additional task for you as the node writer is to enable each
of the elements in the state for each action the node supports. The following
subsections provide additional information about enabling elements in the
state.
Enabling Elements in the State
In the initClass() method, use the SO_ENABLE() macro (dened in
SoAction.h) to enable the elements required by your node in the state. To use
a simple example, SoDrawStyle enables these elements in the
SoGLRenderAction:
SO_ENABLE(SoGLRenderAction, SoGLDrawStyleElement);
SO_ENABLE(SoGLRenderAction, SoGLLinePatternElement);
SO_ENABLE(SoGLRenderAction, SoGLLineWidthElement);
SO_ENABLE(SoGLRenderAction, SoGLPointSizeElement);
SoDrawStyle also implements the SoCallbackAction. It enables these
elements in the SoCallbackAction:
SO_ENABLE(SoCallbackAction, SoDrawStyleElement);
SO_ENABLE(SoCallbackAction, SoLinePatternElement);
SO_ENABLE(SoCallbackAction, SoLineWidthElement);
SO_ENABLE(SoCallbackAction, SoPointSizeElement);
Tip: If you know that the element is already enabled by another node or
action, you can skip this step.
Now that these elements have been enabled, their values can be set and
queried. (The debugging version of Inventor generates an error if you try to
access or set an element that has not been enabled.)
76
Appendix A: Creating a Node
Inheritance Within the Element Stack
The example using SoDrawStyle elements in the previous section brings up
another feature of the element stack: Some elements have corresponding GL
versions that are derived from them. The SoGL version of an element
typically sends its value to OpenGL when it is set. SoGLDrawStyleElement
is derived from SoDrawStyleElement, and SoGLLinePatternElement is
derived from SoLinePatternElement. The parent element class and its
derived class share the same stack index.
If you try to enable two classes that share a stack index (for example,
SoGLDrawStyleElement and SoDrawStyleElement), only the more
derived class is actually enabled (in this case, SoGLDrawStyleElement).
However, you can always use the base class static method to set or get the
value for either the parent or the derived class. (You cannot, however, enable
only the parent version and then try to treat it as the derived GL version.)
Dening the Constructor
The constructor denes the elds for the node and sets up their default
values. If the elds contain enumerated values, their names and values are
dened in the constructor as well. Use the SO_NODE_CONSTRUCTOR()
macro to perform the basic work.
The SO_NODE_IS_FIRST_INSTANCE() macro returns a Boolean value that
can be tested in constructors. If your class requires considerable overhead
when it is initialized, you may want to perform this work only once when
the rst instance of the class is created. For example, the SoCube class sets
up the coordinates and normals of the cube faces during construction of its
rst instance. (You could put this code in the initClass() method, but putting
it in the constructor guarantees that someone is actually using your node
class rst.)
Defining the Constructor
77
Setting Up the Nodes Fields
The SO_NODE_ADD_FIELD() macro denes the elds in the node and sets
up their default values. The rst parameter is the name of the eld. The
second parameter is the default eld value, in parentheses. Using
SoDrawStyle as an example:
SO_NODE_ADD_FIELD(style, (SoDrawStyleElement::getDefault()));
SO_NODE_ADD_FIELD(lineWidth,
(SoLineWidthElement::getDefault()));
SO_NODE_ADD_FIELD(linePattern,
(SoLinePatternElement::getDefault()));
SO_NODE_ADD_FIELD(pointSize,
(SoPointSizeElement::getDefault()));
To add a eld with a vector value, the syntax is as follows:
SO_NODE_ADD_FIELD(translation, (0.0, 0.0, 0.0));
Dening Enumerated Values for a Field
In the preceding example, the style eld contains an enumerated value:
FILLED, LINES, POINTS, or INVISIBLE. Use the SO_NODE_DEFINE_-
ENUM_VALUE() macro to dene the enumerated values. The rst
parameter is the type of the enumerated value. The second parameter is its
value, as shown here:
SO_NODE_DEFINE_ENUM_VALUE(Style, FILLED);
SO_NODE_DEFINE_ENUM_VALUE(Style, LINES);
SO_NODE_DEFINE_ENUM_VALUE(Style, POINTS);
SO_NODE_DEFINE_ENUM_VALUE(Style, INVISIBLE);
Then, to specify that these enumerated values can be used in the style eld
of the SoDrawStyle node, use the SO_NODE_SET_SF_ENUM_TYPE()
macro:
O_NODE_SET_SF_ENUM_TYPE(style, Style);
78
Appendix A: Creating a Node
Implementing Actions
Your next task is to implement each of the actions your new node supports.
The SoDrawStyle node, as you have already seen, supports two actions, the
SoGLRenderAction and the SoCallbackAction, in addition to the
SoSearchAction and the SoWriteAction, which it inherits from SoNode.
Tip: Do not apply a new action within another action (because caching will
not function properly). Also, if you are creating a new node, do not modify
the node (for example, call setValue() on a eld) within an action method.
The doAction() Method
For the GL render action, the SoDrawStyle node changes the values of 4
elements in the state based on the value of the corresponding elds. For
example, if its style eld has a value of INVISIBLE, it changes the value of
the SoGLDrawStyleElement in the state to INVISIBLE. First it must check if
another node has overridden the draw style. The code to set the elements
value is:
if (! style.isIgnored() &&
! SoOverrideElement::getDrawStyleOverride(state)) {
if (isOverride()) {
SoOverrideElement::setDrawStyleOverride(state, this,
TRUE);
}
SoDrawStyleElement::set(state,
(SoDrawStyleElement::Style) style.getValue());
}
For the callback action, SoDrawStyle does the same thing: It sets the value
of the element based on the value of the corresponding eld in the node.
Since the two actions perform exactly the same tasks, the common code is
put into a separate method that both the GL render and the callback actions
can call. By convention, this shared method used by property nodes is called
doAction(), which is a virtual method on SoNode. The code for the
draw-style nodes callback action followed by the nodes GL render action:
void
SoDrawStyle::callback(SoCallbackAction *action)
(
Implementing Actions
79
doAction(action);
}
void
SoDrawStyle::GLRender(SoGLRenderAction *action)
(
doAction(action);
}
This is the draw-style nodesdoAction() method:
SoDrawStyle::doAction(SoAction *action)
{
SoState *state = action->getState();
if (! style.isIgnored() &&
! SoOverrideElement::getDrawStyleOverride(state)) {
if (isOverride()) {
SoOverrideElement::setDrawStyleOverride(
state, this, TRUE);}
SoDrawStyleElement::set(state,
(SoDrawStyleElement::Style)
style.getValue());
}
if (! pointSize.isIgnored() &&
! SoOverrideElement::getPointSizeOverride(state)) {
if (isOverride()) {
SoOverrideElement::setPointSizeOverride(
state, this, TRUE);}
SoPointSizeElement::set(state, pointSize.getValue());
}
if (! lineWidth.isIgnored() &&
! SoOverrideElement::getLineWidthOverride(state)) {
if (isOverride()) {
SoOverrideElement::setLineWidthOverride(state,
state, this, TRUE);}
SoLineWidthElement::set(state, lineWidth.getValue());
}
80
Appendix A: Creating a Node
if (! linePattern.isIgnored() &&
!SoOverrideElement::getLinePatternOverride(state)) {
if (isOverride()) {
SoOverrideElement::setLinePatternOverride(
state, this, TRUE);}
SoLinePatternElement::set(state,
linePattern.getValue());
}
}
The advantage of this scheme becomes apparent when you consider
extending the set of actions (see Chapter 4 of The Inventor Toolmaker). You can
dene a new action class and implement a static method for SoNode that
calls doAction(). Then all properties that implement doAction() will
perform the appropriate operation without needing any static methods for
them.
Changing and Examining State Elements
As discussed in Chapter 1 of The Inventor Toolmaker, each element class
provides methods for setting and querying its value. The static set() method
usually has two parameters, as shown in the previous section:
The state from which to retrieve the element (which the element obtains
from the given action)
The new value for the element
Most element classes also dene a static get() method that returns the
current value stored in an element instance. For example, to obtain the
current draw style, use:
style = SoDrawStyleElement::get(action->getState());
Elements that have multiple values may dene a different sequence of get()
methods. For example, the material color elements and coordinate element
can contain many values. In these cases, the element class denes three
methods:
getInstance() returns the top instance of the element in the state as a const
pointer
Implementing Actions
81
getNum() returns the number of values in the element
get(n)returns the nth value in the element
Element Bundles
Elements are designed to be small and specic, for two reasons. The rst is
that it should be possible for a node to change one aspect of the state without
having to change any of the rest, including related elements. For example,
the SoMaterialBinding node changes only the SoMaterialBindingElement
without affecting any other material elements. The second reason has to do
with caching. It is easy to determine when any elements value has changed,
since (typically) the whole element changes at once. Therefore, determining
which nodes affect a cache is a straightforward task.
However, some elements are related to each other, and its good to deal with
them together for convenience and efciency. Classes called bundles provide
simple interfaces to collections of related elements.
Supported Inventor bundle classes are:
SoMaterialBundle
SoNormalBundle
SoTextureCoordinateBundle
The SoMaterialBundle class accesses all elements having to do with surface
materials. In Inventor 2.1, these functions are more efciently performed by
using the SoLazyElement (see the next section for more information). The
following examples use the SoLazyElement instead of material bundles.
Methods on the SoMaterialBundle allow shapes to step easily through
sequential materials and to send the current material to OpenGL.
SoNormalBundle lets you step easily through sequential normals and
provides functions for generating default normals.
SoTextureCoordinateBundle lets you step through texture coordinates and
provides methods for generating texture coordinates if the shape is using
SoTextureCoordinatePlane or SoTextureCoordinateEnvironment.
82
Appendix A: Creating a Node
The SoLazyElement
Several elements that were responsible for material properties in Inventor
2.0 have been combined into two elements: SoLazyElement and
SoGLLazyElement, its derived OpenGL version.
SoLazyElement is responsible for setting and getting material state.
SoGLLazyElement is responsible for tracking the OpenGL state,
invalidating caches, and so on.
See Changes in Material Elements on page 38 for more information.
Creating a Property Node
The easiest way to learn how to add a node class is by example. The rst
example creates a new property class called Glow, which modies the
emissive color of the current material to make objects appear to glow. It has
a eld called color, which is the color of the glow, and a oat eld called
brightness, ranging from 0 to 1, indicating how much the object should glow
and a transparency eld, indicating the transparency of the glowing
material. Its value is between 0 (opaque) and 1 (invisible).
In Inventor 2.1, the transparency and diffuse color are combined in the state
into a packed color. To facilitate dealing with diffuse colors and transparency
independently, a class SoColorPacker is provided. In the example that
follows, the SoColorPacker is constructed in the Glow node constructor,
and is used whenever the transparency is set in the state.
The doAction() method of the Glow node must check whether the emissive
color or transparency is already being overridden by another material node.
The SoOverrideElement determines what is overridden.
For this class, the program needs to implement actions that deal with
materials: GLRender() and callback(). It uses doAction() (see The
doAction() Method on page 78) since it performs the same operation for
both actions. The doAction() method for the Glow class updates the lazy
color element based on the values of the color,transparency, and brightness
elds of the node.
Creating a Property Node
83
The class header for the new node is shown in Example A-1.
Example A-1 Glow.h
#include <Inventor/SbColor.h>
#include <Inventor/fields/SoSFColor.h>
#include <Inventor/fields/SoSFFloat.h>
#include <Inventor/nodes/SoSubNode.h>
// Need SoColorPacker to put the transparency into the state:
class SoColorPacker;
class Glow : public SoNode {
SO_NODE_HEADER(Glow);
public:
// Fields:
SoSFColor color; // Color of glow
SoSFFloat brightness; // Amount of glow (0-1)
SoSFFloat transparency; // transparency of glowing object
// Initializes this class for use in scene graphs. This
// should be called after database initialization and
// before any instance of this node is constructed.
static void initClass();
// Constructor
Glow();
protected:
// These implement supported actions. The only actions
// dealing with materials are the callback and GL render
// actions. We will inherit all other action methods from
// SoNode.
virtual void GLRender(SoGLRenderAction *action);
virtual void callback(SoCallbackAction *action);
// This implements generic traversal of Glow node, used in
// both of the above methods.
virtual void doAction(SoAction *action);
private:
// Destructor. Private to keep people from trying to
// delete nodes instead of using reference count
84
Appendix A: Creating a Node
// mechanism.
virtual ~Glow();
// A pointer to the color packer is required in nodes
// that set diffuse or transparency. This will
// be initialized in constructor, deleted in destructor:
SoColorPacker *colorPacker;
// Keep a copy of the transparency that this node puts
// into the state:
float transpValue;
The Glow node is representative of most property nodes in that it is
concerned solely with editing the current traversal state, regardless of the
action being performed.
The source code for the Glow class is shown in Example A-2.
Example A-2 Glow.c++
#include <Inventor/actions/SoCallbackAction.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/elements/SoLazyElement.h>
#include <Inventor/elements/SoOverrideElement.h>
#include "Glow.h"
SO_NODE_SOURCE(Glow);
//
// Initializes the Glow class. This is a one-time thing that
// is done after database initialization and before any
// instance of this class is constructed.
//
void
Glow::initClass()
{
// Initialize type id variables. Arguments to the macro
// are: the name of the node class, the class this is
// derived from, and the name registered with the type of
// the parent class.
SO_NODE_INIT_CLASS(Glow, SoNode, "Node");
}
Creating a Property Node
85
//
// Constructor
//
Glow::Glow()
{
// Do standard constructor stuff:
SO_NODE_CONSTRUCTOR(Glow);
// Add "color" field to the field data. The default value
// for this field is R=G=B=1, which is white.
SO_NODE_ADD_FIELD(color, (1.0, 1.0, 1.0));
// Add "brightness" field to the field data. The default
// value for this field is 0.
SO_NODE_ADD_FIELD(brightness, (0.0));
// Add "transparency" field to the field data. Default
// value is 0 (opaque).
SO_NODE_ADD_FIELD(transparency, (0.0));
// Initialize the color Packer (required of any property
// node that uses an SoColorPacker to set diffuse color
// or transparency):
colorPacker = new SoColorPacker;
}
//
// Destructor
//
Glow::~Glow()
{
// Delete the color Packer:
delete colorPacker;
}
//
// Implements GL render action.
//
void
Glow::GLRender(SoGLRenderAction *action)
86
Appendix A: Creating a Node
{
// Set the elements in the state correctly. Note that we
// prefix the call to doAction() with the class name. This
// avoids problems if someone derives a new class from the
// Glow node and inherits the GLRender() method; Glow's
// doAction() will still be called in that case.
Glow::doAction(action);
// Note that in Inventor 2.1 the GLRender method only
// sets the color in the lazy element; it does not send
// it to GL. This is for efficiency, since there is no
// need to send it until it is really used.
}
//
// Implements callback action.
//
void
Glow::callback(SoCallbackAction *action)
{
// Set the elements in the state correctly.
Glow::doAction(action);
}
//
// Typical action implementation; it sets the correct element
// in the action's traversal state. We assume that the lazy
// element has been enabled.
//
void
Glow::doAction(SoAction *action)
{
// Make sure the "brightness" field is not ignored. If it
// is, then we don't need to change anything in the state.
// Also check that the emissive color is not being
// overridden; if it is, this node should not set it.
if (! brightness.isIgnored() &&
! SoOverrideElement::getEmissiveColorOverride
(action->getState())) {
Creating a Property Node
87
SbColor emissiveColor = color.getValue() *
brightness.getValue();
//Use the Lazy element to set emissive color.
//Note that this wont actually send the color to GL.
SoLazyElement::setEmissive(action->getState(),
&emissiveColor);
}
// To send transparency we again check ignore flag and
// override element.
if (! transparency.isIgnored() &&
! SoOverrideElement::getTransparencyOverride
(action->getState())) {
// Keep a copy of the transparency that we are
// putting in the state:
transpValue = transparency.getValue();
// The color packer must be provided when
// transparency is set, so that the transparency
// will be merged with current diffuse color
// in the state:
SoLazyElement::setTransparency(action->getState(),
this, 1, &transpValue, colorPacker);
}
}
88
Appendix A: Creating a Node
Creating a Shape Node
This next example is more complicated than the property-node example,
because shape nodes need to access more of the state and implement
different behaviors for different actions. For example, a shape needs to draw
geometry during rendering, return intersection information during picking,
and compute its extent when getting a bounding box.
All shapes need to dene at least two methods: generatePrimitives() and
getBoundingBox(). When you dene the generatePrimitives() method for
your new class, you can inherit the GLRender() and rayPick() methods from
the base class, SoShape, because they use the generated primitives. This
feature saves time at the prototyping stage, since you need to implement
only the generatePrimitives() method, and rendering and picking are
provided at no extra cost. When you are ready for ne-tuning, you can
redene these two methods to improve performance.
Generating Primitives
When a shape node is traversed to generate primitives for the
SoCallbackAction, it generates triangles, line segments, or points. The
information for each vertex of the triangle, line segment, or point is stored in
an instance of SoPrimitiveVertex. The shape lls in the information for each
vertex. Then, for each primitive generated (that is, triangle, line segment, or
point), an appropriate callback function is invoked by a method on
SoShape. For example, if the shape generates triangles, the triangle callback
function is invoked for every triangle generated. Filled shapes, such as
SoCone and SoQuadMesh, generate triangles (regardless of draw style),
line shapes (such as SoLineSet and SoIndexedLineSet) generate line
segments, and point shapes (such as SoPointSet) generate points.
Creating a Shape Node
89
SoPrimitiveVertex
The SoPrimitiveVertex contains all information for that vertex:
Point (coordinates, in object space)
Normal
Texture coordinates
Material index
A pointer to an instance of an SoDetail subclass (may be NULL)
The shapesgeneratePrimitives() method sets each of these values.
The appropriate callback function can be invoked either automatically or
explicitly. If you want explicit control over when the callback function is
invoked, you can use the following methods provided by the SoShape class:
invokeTriangleCallbacks()
invokeLineSegmentCallbacks()
invokePointCallbacks()
To take advantage of the automatic mechanism, use these three methods,
provided by the SoShape base class as a convenience:
beginShape(action,shapeType)
shapeVertex(&vertex)
endShape()
The shapeType parameter is TRIANGLE_FAN, TRIANGLE_STRIP,
TRIANGLES, or POLYGON. For example, if you choose TRIANGLE_FAN,
this method performs the necessary triangulation and invokes the
appropriate callbacks for each successive triangle of the shape. This
mechanism is similar to OpenGLs geometry calls.
90
Appendix A: Creating a Node
Creating Details
You may want your shape to store additional information in an SoDetail
for example, what part of the shape each vertex belongs to. In this case, you
can use an existing subclass of SoDetail (see The Inventor Mentor, Chapter 9),
or you can create a new SoDetail subclass to hold the appropriate
information. By default, the pointer to the detail in SoPrimitiveVertex is
NULL.
If you decide to store information in an SoDetail, you create an instance of
the subclass and store a pointer to it in the SoPrimitiveVertex by calling
setDetail().
Rendering
For rendering, you may be able to inherit the GLRender() method from the
SoShape class. In this case, you dene a generatePrimitives() method as
described in the previous sections. Each primitive is generated and then
rendered separately.
In other cases, you may want to write your own render method for the new
shape class, especially if it would be more efcient to send the vertex
information to OpenGL in some other form, such as triangle strips. The
Pyramid node created later in this appendix implements its own
GLRender() method. Before rendering, the shape should test whether it
needs to be rendered. You can use the SoShape::shouldGLRender()
method, which checks for INVISIBLE draw style, BOUNDING_BOX
complexity, delayed transparency, and render abort.
Inventor takes care of sending the draw-style value to OpenGL (where it is
handled by glPolygonMode()). This means that lled shapes are drawn
automatically as lines or points if the draw style indicates such. Note that if
your object is composed of lines, but the draw style is POINTS, you need to
handle that case explicitly. You need to check whether the draw-style
element in the state is points or lines and render the shape accordingly.
The lazy elements are used in the GLRender() method of the Pyramid to
send the colors to OpenGL. This has to be set up as follows:
Creating a Shape Node
91
SoLazyElement::getLightModel() is used to tell if lighting is on. If so, it
is necessary to send normals to OpenGL.
SoLazyGLElement::sendAllMaterial() is needed prior to rendering the
shape. This ensures that the OpenGL material state is in agreement
with the current Inventor state.
SoGLLazyElement::sendDiffuseByIndex() is required if more than
one diffuse color or transparency is sent. This issues the appropriate
OpenGL commands to send the specied color and transparency to
OpenGL.
SoGLLazyElement::reset() is required at the end of rendering, if colors
other than the rst color were sent to OpenGL. This informs the lazy
element that the current color in the state is not the last one sent to GL.
Picking
For picking, you may also be able to inherit the rayPick() method from the
SoShape class. In this case, you dene a generatePrimitives() method, and
the parent class rayPick() method tests the picking ray against each
primitive that has been generated. If the ray intersects the primitive, it
creates an SoPickedPoint.SoShape provides three virtual methods for
creating details:
createTriangleDetail()
createLineDetail()
createPointDetail()
The default methods return NULL, but your shape can override this to set
up and return a detail instance.
The Pyramid node created later in this appendix inherits the rayPick()
method from SoShape in this manner.
For some shapes, such as spheres and cylinders, it is more efcient to check
whether the picking ray intersects the object without tessellating the object
into primitives. In such cases, you can implement your own rayPick()
method and use the SoShape::shouldRayPick() method, which rst checks
to see if the object is pickable.
92
Appendix A: Creating a Node
The following excerpt from the SoSphere class shows how to implement
your own rayPick() method:
void
SoSphere::rayPick(SoRayPickAction *action)
{
SbVec3f enterPoint, exitPoint, normal;
SbVec4f texCoord(0.0, 0.0, 0.0, 1.0);
SoPickedPoint *pp;
// First see if the object is pickable.
if (! shouldRayPick(action))
return;
// Compute the picking ray in our current object space.
computeObjectSpaceRay(action);
// Create SbSphere with correct radius, centered at zero.
float rad = (radius.isIgnored() ? 1.0 :
radius.getValue());
SbSphere sph(SbVec3f(0., 0., 0.), rad);
// Intersect with pick ray. If found, set up picked
// point(s).
if (sph.intersect(action->getLine(), enterPoint,
exitPoint)) {
if (action->isBetweenPlanes(enterPoint) &&
(pp = action->addIntersection(enterPoint)) !=
NULL) {
normal = enterPoint;
normal.normalize();
pp->setObjectNormal(normal);
// This macro computes the s and t texture
// coordinates for the shape.
COMPUTE_S_T(enterPoint, texCoord[0], texCoord[1]);
pp->setObjectTextureCoords(texCoord);
}
if (action->isBetweenPlanes(exitPoint) &&
(pp = action->addIntersection(exitPoint)) != NULL){
normal = exitPoint;
normal.normalize();
pp->setObjectNormal(normal);
Creating a Shape Node
93
COMPUTE_S_T(exitPoint, texCoord[0], texCoord[1]);
texCoord[2] = texCoord[3] = 0.0;
pp->setObjectTextureCoords(texCoord);
}
}
}
Getting a Bounding Box
SoShape provides a getBoundingBox() method that your new shape class
can inherit. This method calls a virtual computeBBox() method, which you
need to dene. (The computeBBox() method is also used during rendering
when bounding-box complexity is specied.)
If you are deriving a class from SoNonIndexedShape, you can use the
computeCoordBBox() method within your computeBBox() routine. This
method computes the bounding box by looking at the specied number of
vertices, starting at startIndex. It uses the minimum and maximum
coordinate values to form the diagonal for the bounding box and uses the
average of the vertices as the center of the object.
If you are deriving a class from SoIndexedShape, you can inherit
computeBBox() from the base SoIndexedShape class. This method uses all
nonnegative indices in the coordinates list to nd the minimum and
maximum coordinate values. It uses the average of the coordinate values as
the center of the object.
For improved picking performance, the rayPick() action assumes that the
bounding boxes include only surfaces; not lines or points. If your shape
object is composed of lines and/or points, you must implement a
getBoundingBox() method similar to the following method from the
SoLineSet implementation:
void
SoLineSet::getBoundingBox(SoGetBoundingBoxAction *action)
{
// Let our parent class do the real work
SoNonIndexedShape::getBoundingBox(action);
94
Appendix A: Creating a Node
// If there are any open bounding box caches, tell them
// that they contain lines
SoBoundingBoxCache::setHasLinesOrPoints
(action->getState());
}
Pyramid Node
This example creates a Pyramid node, which has a square base at y = 1 and
its apex at (0.0, 1.0, 0.0). The code presented here is similar to that used for
other primitive (nonvertex-based) shapes, such as cones and cylinders. The
pyramid behaves like an SoCone, except that it always has four sides. And,
instead of a bottomRadius eld, the Pyramid class has baseWidth and
baseDepth elds in addition to the parts and height elds.
Some of the work for all shapes can be done by methods on the base shape
class, SoShape. For example, SoShape::shouldGLRender() checks for
INVISIBLE draw style when rendering. SoShape::shouldRayPick() checks
for UNPICKABLE pick style when picking. This means that shape
subclasses can concentrate on their specic behaviors.
To dene a vertex-based shape subclass, you probably want to derive your
class from either SoNonIndexedShape or SoIndexedShape. These classes
dene some methods and macros that can make your job easier.
You may notice in this example that there are macros (dened in
SoSFEnum.h) that make it easy to deal with elds containing enumerated
types, such as the parts eld of our node. Similar macros are found in
SoMFEnum.h and in the header les for the bitmask elds.
The class header for the Pyramid node is shown in Example A-3.
Example A-3 Pyramid.h
#include <Inventor/SbLinear.h>
#include <Inventor/fields/SoSFBitMask.h>
#include <Inventor/fields/SoSFFloat.h>
#include <Inventor/nodes/SoShape.h>
// SoShape.h includes SoSubNode.h; no need to include it here
// Pyramid texture coordinates are defined on the sides so
Creating a Shape Node
95
// that the seam is along the left rear edge, wrapping
// counterclockwise around the sides. The texture
// coordinates on the base are set up so the texture is
// right side up when the pyramid is tilted back.
class Pyramid : public SoShape {
SO_NODE_HEADER(Pyramid);
public:
enum Part { // Pyramid parts:
SIDES = 0x01, // The 4 side faces
BASE = 0x02, // The bottom square face
ALL = 0x03, // All parts
};
// Fields
SoSFBitMask parts; // Visible parts
SoSFFloat baseWidth; // Width of base
SoSFFloat baseDepth; // Depth of base
SoSFFloat height; // Height, base to apex
// Initializes this class
static void initClass();
// Constructor
Pyramid();
// Turns on/off a part of the pyramid. (Convenience)
void addPart(Part part);
void removePart(Part part);
// Returns whether a part is on or off. (Convenience)
SbBool hasPart(Part part) const;
protected:
// This implements the GL rendering action. We inherit
// all other action behavior, including rayPick(), which
// is defined by SoShape to pick against all of the
// triangles created by generatePrimitives.
virtual void GLRender(SoGLRenderAction *action);
// Generates triangles representing a pyramid
virtual void generatePrimitives(SoAction *action);
96
Appendix A: Creating a Node
// This computes the bounding box and center of a pyramid.
// It is used by SoShape for the SoGetBoundingBoxAction;
// also to compute the correct box to render or pick when
// complexity is BOUNDING_BOX. Note that we do not have to
// define a getBoundingBox() method, since SoShape already
// takes care of that (using this method).
virtual void computeBBox(SoAction *action,
SbBox3f &box, SbVec3f &center);
private:
// Face normals. These are static because they are
// computed once and are shared by all instances
static SbVec3f frontNormal, rearNormal;
static SbVec3f leftNormal, rightNormal;
static SbVec3f baseNormal;
// Destructor
virtual ~Pyramid();
// Computes and returns half-width, half-height, and
// half-depth based on current field values
void getSize(float &halfWidth,
float &halfHeight,
float &halfDepth) const;
};
The source code for the Pyramid node is shown in Example A-4.
Example A-4 Pyramid.c++
/*-----------------------------------------------------------
* This is an example from the Inventor Toolmaker,
* chapter 2, example 4.
*
* Source file for "Pyramid" shape node.
*----------------------------------------------------------*/
#include <GL/gl.h>
#include <Inventor/SbBox.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/SoPrimitiveVertex.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/actions/SoRayPickAction.h>
#include <Inventor/elements/SoGLLazyElement.h>
#include <Inventor/elements/SoGLTextureCoordinateElement.h>
#include <Inventor/elements/SoGLTextureEnabledElement.h>
Creating a Shape Node
97
#include <Inventor/elements/SoMaterialBindingElement.h>
#include <Inventor/elements/SoModelMatrixElement.h>
#include <Inventor/misc/SoState.h>
#include "Pyramid.h"
// Shorthand macro for testing whether the current parts
// field value (parts) includes a given part (part)
#define HAS_PART(parts, part) (((parts) & (part)) != 0)
SO_NODE_SOURCE(Pyramid);
// Normals to four side faces and to base
SbVec3f Pyramid::frontNormal, Pyramid::rearNormal;
SbVec3f Pyramid::leftNormal, Pyramid::rightNormal;
SbVec3f Pyramid::baseNormal;
//
// This initializes the Pyramid class.
//
void
Pyramid::initClass()
{
// Initialize type id variables
SO_NODE_INIT_CLASS(Pyramid, SoShape, "Shape");
}
//
// Constructor
//
Pyramid::Pyramid()
{
SO_NODE_CONSTRUCTOR(Pyramid);
SO_NODE_ADD_FIELD(parts, (ALL));
SO_NODE_ADD_FIELD(baseWidth, (2.0));
SO_NODE_ADD_FIELD(baseDepth, (2.0));
SO_NODE_ADD_FIELD(height, (2.0));
// Set up static values and strings for the "parts"
// enumerated type field. This allows the SoSFEnum class
// to read values for this field. For example, the first
// line below says that the first value (index 0) has the
// value SIDES (defined in the header file) and is
// represented in the file format by the string "SIDES".
98
Appendix A: Creating a Node
SO_NODE_DEFINE_ENUM_VALUE(Part, SIDES);
SO_NODE_DEFINE_ENUM_VALUE(Part, BASE);
SO_NODE_DEFINE_ENUM_VALUE(Part, ALL);
// Copy static information for "parts" enumerated type
// field into this instance.
SO_NODE_SET_SF_ENUM_TYPE(parts, Part);
// If this is the first time the constructor is called,
// set up the static normals.
if (SO_NODE_IS_FIRST_INSTANCE()) {
float invRoot5 = 1.0 / sqrt(5.0);
float invRoot5Twice = 2.0 * invRoot5;
frontNormal.setValue(0.0, invRoot5, invRoot5Twice);
rearNormal.setValue( 0.0, invRoot5, -invRoot5Twice);
leftNormal.setValue( -invRoot5Twice, invRoot5, 0.0);
rightNormal.setValue( invRoot5Twice, invRoot5, 0.0);
baseNormal.setValue(0.0, -1.0, 0.0);
}
}
//
// Destructor
//
Pyramid::~Pyramid()
{
}
//
// Turns on a part of the pyramid. (Convenience function.)
//
void
Pyramid::addPart(Part part)
{
parts.setValue(parts.getValue() | part);
}
//
// Turns off a part of the pyramid. (Convenience function.)
//
void
Creating a Shape Node
99
Pyramid::removePart(Part part)
{
parts.setValue(parts.getValue() & ~part);
}
//
// Returns whether a given part is on or off. (Convenience
// function.)
//
SbBool
Pyramid::hasPart(Part part) const
{
return HAS_PART(parts.getValue(), part);
}
//
// Implements the SoGLRenderAction for the Pyramid node.
//
void
Pyramid::GLRender(SoGLRenderAction *action)
{
// Access the state from the action.
SoState *state = action->getState();
// See which parts are enabled.
int curParts = (parts.isIgnored() ? ALL :
parts.getValue());
// First see if the object is visible and should be
// rendered now. This is a method on SoShape that checks
// for INVISIBLE draw style, BOUNDING_BOX complexity, and
// delayed transparency.
if (! shouldGLRender(action))
return;
// Declare a pointer to a GLLazyElement. This will be
// used if we send multiple colors.
SoGLLazyElement* lazyElt;
// In Inventor 2.1 we do not use beginSolidShape and
// endSolidShape. Instead, this information should be
// provided in shape hints.
100
Appendix A: Creating a Node
// Change the current GL matrix to draw the pyramid with
// the correct size. This is easier than modifying all of
// the coordinates and normals of the pyramid. (For extra
// efficiency, you can check if the field values are all
// set to default values - if so, then you can skip this
// step.) Scale world if necessary.
float halfWidth, halfHeight, halfDepth;
getSize(halfWidth, halfHeight, halfDepth);
glPushMatrix();
glScalef(halfWidth, halfHeight, halfDepth);
// See if texturing is enabled. If so, we will have to
// send explicit texture coordinates. The "doTextures"
// flag will indicate if we care about textures at all.
// Note this has changed slightly in Inventor version 2.1.
// The texture coordinate type now is either FUNCTION or
// DEFAULT. Texture coordinates are needed only for
// DEFAULT textures.
SbBool doTextures =
(SoGLTextureEnabledElement::get(state) &&
SoTextureCoordinateElement::getType(state) !=
SoTextureCoordinateElement::FUNCTION);
// Determine if we need to send normals. Normals are
// necessary if we are not doing BASE_COLOR lighting.
// Use the lazy element to get the light model.
SbBool sendNormals = (SoLazyElement::getLightModel(state)
!= SoLazyElement::BASE_COLOR);
// Determine if there's a material bound per part.
SoMaterialBindingElement::Binding binding =
SoMaterialBindingElement::get(state);
SbBool materialPerPart =
(binding == SoMaterialBindingElement::PER_PART ||
binding ==
SoMaterialBindingElement::PER_PART_INDEXED);
// Issue a lazy element send.
// The send ensures all material state in GL is current.
SoGLLazyElement::sendAllMaterial(state);
Creating a Shape Node
101
// Render the parts of the pyramid. We don't have to worry
// about whether to render filled regions, lines, or
// points, since that is already taken care of. We are
// also ignoring complexity, which we could use to render
// a more finely tesselated version of the pyramid.
// We'll use this macro to make the code easier. It uses
// the "point" variable to store the vertex point to send.
SbVec3f point;
#define SEND_VERTEX(x, y, z, s, t) \
point.setValue(x, y, z); \
if (doTextures) \
glTexCoord2f(s, t); \
glVertex3fv(point.getValue())
if (HAS_PART(curParts, SIDES)) {
// Draw each side separately, so that normals are
// correct. If sendNormals is TRUE, send face normals
// with the polygons. Make sure the vertex order obeys
// the right-hand rule.
glBegin(GL_TRIANGLES);
// Front face: left front, right front, apex
if (sendNormals)
glNormal3fv(frontNormal.getValue());
SEND_VERTEX(-1.0, -1.0, 1.0, .25, 0.0);
SEND_VERTEX( 1.0, -1.0, 1.0, .50, 0.0);
SEND_VERTEX( 0.0, 1.0, 0.0, .325, 1.0);
// Right face: right front, right rear, apex
if (sendNormals)
glNormal3fv(rightNormal.getValue());
SEND_VERTEX( 1.0, -1.0, 1.0, .50, 0.0);
SEND_VERTEX( 1.0, -1.0, -1.0, .75, 0.0);
SEND_VERTEX( 0.0, 1.0, 0.0, .625, 1.0);
// Rear face: right rear, left rear, apex
if (sendNormals)
glNormal3fv(rearNormal.getValue());
SEND_VERTEX( 1.0, -1.0, -1.0, .75, 0.0);
SEND_VERTEX(-1.0, -1.0, -1.0, 1.0, 0.0);
SEND_VERTEX( 0.0, 1.0, 0.0, .875, 1.0);
102
Appendix A: Creating a Node
// Left face: left rear, left front, apex
if (sendNormals)
glNormal3fv(leftNormal.getValue());
SEND_VERTEX(-1.0, -1.0, -1.0, 0.0, 0.0);
SEND_VERTEX(-1.0, -1.0, 1.0, .25, 0.0);
SEND_VERTEX( 0.0, 1.0, 0.0, .125, 1.0);
glEnd();
}
if (HAS_PART(curParts, BASE)) {
// Send the next material if it varies per part.
// Use SoGLLazyElement::sendDiffuseByIndex().
// This also sends transparency, so that if
// transparency type is not SCREEN_DOOR, there can be
// a change of transparency across the shape.
if (materialPerPart){
//Obtain a current copy of the SoGLLazyElement;
//use this for the send.
lazyElt =
(SoGLLazyElement*)SoLazyElement::getInstance(state);
lazyElt->sendDiffuseByIndex(1);
}
if (sendNormals)
glNormal3fv(baseNormal.getValue());
// Base: left rear, right rear, right front, left front
glBegin(GL_QUADS);
SEND_VERTEX(-1.0, -1.0, -1.0, 0.0, 0.0);
SEND_VERTEX( 1.0, -1.0, -1.0, 1.0, 0.0);
SEND_VERTEX( 1.0, -1.0, 1.0, 1.0, 1.0);
SEND_VERTEX(-1.0, -1.0, 1.0, 0.0, 1.0);
glEnd();
}
// Restore the GL matrix.
glPopMatrix();
// Reset the diffuse color, if we sent it twice.
if (materialPerPart)
lazyElt->reset(state, SoLazyElement::DIFFUSE_MASK);
Creating a Shape Node
103
}
//
// Generates triangles representing a pyramid.
//
void
Pyramid::generatePrimitives(SoAction *action)
{
// The pyramid will generate 6 triangles: 1 for each side
// and 2 for the base. (Again, ignore complexity.)
// This variable is used to store each vertex.
SoPrimitiveVertex pv;
// Access the state from the action.
SoState *state = action->getState();
// See which parts are enabled.
int curParts = (parts.isIgnored() ? ALL :
parts.getValue());
// We need the size to adjust the coordinates.
float halfWidth, halfHeight, halfDepth;
getSize(halfWidth, halfHeight, halfDepth);
// See if we have to use a texture coordinate function,
// rather than generating explicit texture coordinates.
SbBool useTexFunc =
(SoTextureCoordinateElement::getType(state) ==
SoTextureCoordinateElement::FUNCTION);
// If we need to generate texture coordinates with a
// function, we'll need an SoGLTextureCoordinateElement.
// Otherwise, we'll set up the coordinates directly.
const SoTextureCoordinateElement *tce;
SbVec4f texCoord;
if (useTexFunc)
tce = SoTextureCoordinateElement::getInstance(state);
else {
texCoord[2] = 0.0;
texCoord[3] = 1.0;
}
// Determine if there's a material bound per part.
SoMaterialBindingElement::Binding binding =
104
Appendix A: Creating a Node
SoMaterialBindingElement::get(state);
SbBool materialPerPart =
(binding == SoMaterialBindingElement::PER_PART ||
binding ==
SoMaterialBindingElement::PER_PART_INDEXED);
// We use this macro to make the code easier. It uses the
// "point" variable to store the primitive vertex's point.
SbVec3f point;
#define GEN_VERTEX(pv, x, y, z, s, t, normal) \
point.setValue(halfWidth * x, \
halfHeight * y, \
halfDepth * z); \
if (useTexFunc) \
texCoord = tce->get(point, normal); \
else { \
texCoord[0] = s; \
texCoord[1] = t; \
} \
pv.setPoint(point); \
pv.setNormal(normal); \
pv.setTextureCoords(texCoord); \
shapeVertex(&pv)
if (HAS_PART(curParts, SIDES)) {
// We will generate 4 triangles for the sides of the
// pyramid. We can use the beginShape(), shapeVertex(),
// andendShape() convenience functions on SoShape to
// make the triangle generation easier and clearer.
// The shapeVertex() call is built into the macro.
// Note that there is no detail information for the
// Pyramid. If there was, we would create an instance
// of the correct subclass of SoDetail (such as
// PyramidDetail) and call pv.setDetail(&detail) once.
beginShape(action, TRIANGLES);
// Front face: left front, right front, apex
GEN_VERTEX(pv, -1.0, -1.0, 1.0, .25, 0.0,
frontNormal);
GEN_VERTEX(pv, 1.0, -1.0, 1.0, .50, 0.0,
frontNormal);
Creating a Shape Node
105
GEN_VERTEX(pv, 0.0, 1.0, 0.0, .325, 1.0,
frontNormal);
// Right face: right front, right rear, apex
GEN_VERTEX(pv, 1.0, -1.0, 1.0, .50, 0.0,
rightNormal);
GEN_VERTEX(pv, 1.0, -1.0, -1.0, .75, 0.0,
rightNormal);
GEN_VERTEX(pv, 0.0, 1.0, 0.0, .625, 1.0,
rightNormal);
// Rear face: right rear, left rear, apex
GEN_VERTEX(pv, 1.0, -1.0, -1.0, .75, 0.0,
rearNormal);
GEN_VERTEX(pv, -1.0, -1.0, -1.0, 1.0, 0.0,
rearNormal);
GEN_VERTEX(pv, 0.0, 1.0, 0.0, .875, 1.0,
rearNormal);
// Left face: left rear, left front, apex
GEN_VERTEX(pv, -1.0, -1.0, -1.0, 0.0, 0.0,
leftNormal);
GEN_VERTEX(pv, -1.0, -1.0, 1.0, .25, 0.0,
leftNormal);
GEN_VERTEX(pv, 0.0, 1.0, 0.0, .125, 1.0,
leftNormal);
endShape();
}
if (HAS_PART(curParts, BASE)) {
// Increment the material index in the vertex if
// necessary. (The index is set to 0 by default.)
if (materialPerPart)
pv.setMaterialIndex(1);
// We will generate two triangles for the base, as a
// triangle strip.
beginShape(action, TRIANGLE_STRIP);
106
Appendix A: Creating a Node
// Base: left front, left rear, right front, right rear
GEN_VERTEX(pv, -1.0, -1.0, 1.0, 0.0, 1.0,
baseNormal);
GEN_VERTEX(pv, -1.0, -1.0, -1.0, 0.0, 0.0,
baseNormal);
GEN_VERTEX(pv, 1.0, -1.0, 1.0, 1.0, 1.0,
baseNormal);
GEN_VERTEX(pv, 1.0, -1.0, -1.0, 1.0, 0.0,
baseNormal);
endShape();
}
}
//
// Computes the bounding box and center of a pyramid.
//
void
Pyramid::computeBBox(SoAction *, SbBox3f &box, SbVec3f
&center)
{
// Figure out what parts are active.
int curParts = (parts.isIgnored() ? ALL :
parts.getValue());
// If no part is active, set the bounding box to be tiny.
if (curParts == 0)
box.setBounds(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
else {
// These points define min and max extents of the box.
SbVec3f min, max;
// Compute the half-width, half-height, and half-depth
// of the pyramid. We'll use this info to set the min
// and max points.
float halfWidth, halfHeight, halfDepth;
getSize(halfWidth, halfHeight, halfDepth);
min.setValue(-halfWidth, -halfHeight, -halfDepth);
Creating a Shape Node
107
// The maximum point depends on whether the SIDES are
// active. If not, only the base is present.
if (HAS_PART(curParts, SIDES))
max.setValue(halfWidth, halfHeight, halfDepth);
else
max.setValue(halfWidth, -halfHeight, halfDepth);
// Set the box to bound the two extreme points
box.setBounds(min, max);
}
// This defines the "natural center" of the pyramid. We
// could define it to be the center of the base, if we
// want, but let's just make it the center of the
// bounding box.
center.setValue(0.0, 0.0, 0.0);
}
//
// Computes and returns half-width, half-height, and
// half-depth based on current field values.
//
void
Pyramid::getSize(float &halfWidth,
float &halfHeight,
float &halfDepth) const
{
halfWidth = (baseWidth.isIgnored() ? 1.0 :
baseWidth.getValue() / 2.0);
halfHeight = (height.isIgnored() ? 1.0 :
height.getValue() / 2.0);
halfDepth = (baseDepth.isIgnored() ? 1.0 :
baseDepth.getValue() / 2.0);
}
Tip: The easiest way to make sure your generatePrimitives() method is
working is to use it for rendering, by temporarily commenting out your
shapesGLRender() method (if it has one).
108
Appendix A: Creating a Node
Creating a Group Node
The example discussed in this section illustrates how to create a group node
subclass. (It is unlikely, however, that youll need to create a new group
class.) Our example class, Alternate, traverses every other child (that is,
child 0, then child 2, and so on). Since, like the base SoGroup class, it has no
elds, this example also illustrates how to create a node with no elds.
If you do create a new group class, you probably need to dene a new
traversal behavior for it. You may be able to inherit some of the traversal
behavior from the parent class. Most groups dene a protected
traverseChildren() method that implements their typical traversal
behavior. Your new group can probably inherit the read() and write()
methods from SoGroup.
Child List
SoGroup, and all classes derived from it, store their children in an instance
of SoChildList. This extender class provides useful methods for group
classes, including the traverse() method, which has three forms:
traverse(action) traverses all children in the child list
traverse(action, rstChild, lastChild)
traverses the children from rst child to last child, inclusive
traverse(action, childIndex)
traverses one child with the specied index
Hidden Children
If you want your new node to have children, but you dont want to grant
public access to the child list, you can implement the node to have hidden
children. Node kits are an example of groups within the Inventor library that
have hidden children. Because node kits have a specic internal structure,
access to the children needs to be restricted. If you want the node to have
hidden children, it should not be derived from SoGroup, which has public
children only.
Creating a Group Node
109
SoNode provides a virtual getChildren() method that returns NULL by
default. To implement a new node with hidden children, you need to do the
following:
1. Maintain an SoChildList for the node. This list can be a hierarchy of
nodes.
2. Implement a getChildren() method that returns a pointer to the child
list. (SoPath uses getChildren() to maintain paths.)
Using the Path Code
Recall that an action can be applied to a node, a single path, or a path list.
Before a group can traverse its children, it needs to know what the action has
been applied to. The getPathCode() method of SoAction returns an
enumerated value that indicates whether the action is being applied to a
path and, if so, where this group node is in relation to the path or paths. The
values returned by getPathCode() are as follows:
NO_PATH The action is not applied to a path (that is, the action is
applied to a node).
BELOW_PATH This node is at or below the last node in the path chain.
OFF_PATH This node is not on the path chain (the node is to the left of
the path; it needs to be traversed if it affects the nodes in the
path).
IN_PATH The node is in the chain of the path (but is not the last node).
For SoGroup, if the groups path code is NO_PATH, BELOW_PATH, or
OFF_PATH, it traverses all of its children. (Even if a node is OFF_PATH, you
need to traverse it because it affects the nodes in the path to its right. Note,
though, that if an SoSeparator is OFF_PATH, you do not need to traverse it
because it does not have any effect on the path.) If a node is IN_PATH, you
may not need to traverse all children in the group, since children to the right
of the action path do not affect the nodes in the path. In this case,
getPathCode() returns the indices of the children that need to be traversed.
The traverseChildren() method for SoGroup looks like this:
110
Appendix A: Creating a Node
void
SoGroup::traverseChildren(SoAction *action)
{
int numIndices;
const int *indices;
if (action->getPathCode(numIndices, indices)
== SoAction::IN_PATH)
children.traverse(action, 0, indices[numIndices - 1]);
// Traverse all children up to and including the
// last child to traverse.
else
children.traverse(action); // traverse all children
}
The GL render, callback, handle event, pick, and search methods for
SoGroup all use traverseChildren(). The write method for SoGroup, which
can be inherited by most subclasses, tests each node in the group before
writing it out. The get matrix method does not use traverseChildren()
because it doesnt need to traverse as much. If the path code for a group is
NO_PATH or BELOW_PATH, it does not traverse the children. Here is the
code for SoGroup::getMatrix():
void
SoGroup::getMatrix(SoGetMatrixAction *action)
{
int numIndices;
const int *indices;
switch (action->getPathCode(numIndices, indices)) {
case SoAction::NO_PATH:
case SoAction::BELOW_PATH:
break;
case SoAction::IN_PATH:
children.traverse(action, 0,
indices[numIndices - 1]);
break;
case SoAction::OFF_PATH:
children.traverse(action);
break;
}
}
Creating a Group Node
111
If a node is IN_PATH, the getMatrix() method traverses all the children in
the group up to and including the last child in the action path (but not the
children to the right of the path). If a node is OFF_PATH, the getMatrix()
method traverses all the children in the group, since they can affect what is
in the path.
What Happens If an Action Is Terminated?
Some actions, such as the GL render, handle event, and search actions, can
terminate prematurelyfor example, when the node to search for has been
found. The SoAction class has a ag that indicates whether the action has
terminated. The SoChildList class checks this ag automatically, so this
termination is built into the SoChildList::traverse() methods, and the group
traversal methods do not need to check the ag.
The new Alternate class can inherit the read and write methods from
SoGroup. We just have to dene the traversal behavior for the other actions.
Most of the other actions can be handled by the traverseChildren() method.
If your group subclass is derived from SoSeparator, you must write special
rendering methods that correspond to the different path nodes. See The
GLRender() Method on page 34.
Alternate Node
The class header for the Alternate node is shown in Example A-5.
Example A-5 Alternate.h
#include <Inventor/nodes/SoGroup.h>
// SoGroup.h includes SoSubNode.h; no need to include it.
class Alternate : public SoGroup {
SO_NODE_HEADER(Alternate);
public:
// Initializes this class.
static void initClass();
112
Appendix A: Creating a Node
// Default constructor
Alternate();
// Constructor that takes approximate number of children
// as a hint
Alternate::Alternate(int numChildren);
protected:
// Generic traversal of children for any action.
virtual void doAction(SoAction *action);
// These implement supported actions.
virtual void getBoundingBox(SoGetBoundingBoxAction
*action);
virtual void GLRender(SoGLRenderAction *action);
virtual void handleEvent(SoHandleEventAction *action);
virtual void pick(SoPickAction *action);
virtual void getMatrix(SoGetMatrixAction *action);
virtual void search(SoSearchAction *action);
private:
// Destructor
virtual ~Alternate();
};
The Alternate class source code is shown in Example A-6.
Example A-6 Alternate.c++
#include <Inventor/misc/SoChildList.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/actions/SoGetBoundingBoxAction.h>
#include <Inventor/actions/SoGetMatrixAction.h>
#include <Inventor/actions/SoHandleEventAction.h>
#include <Inventor/actions/SoPickAction.h>
#include <Inventor/actions/SoSearchAction.h>
#include "Alternate.h"
SO_NODE_SOURCE(Alternate);
// This initializes the Alternate class.
void
Alternate::initClass()
{
Creating a Group Node
113
// Initialize type id variables
SO_NODE_INIT_CLASS(Alternate, SoGroup, "Group");
}
// Constructor
Alternate::Alternate()
{
SO_NODE_CONSTRUCTOR(Alternate);
}
// Constructor that takes approximate number of children.
Alternate::Alternate(int numChildren) : SoGroup(numChildren)
{
SO_NODE_CONSTRUCTOR(Alternate);
}
// Destructor
Alternate::~Alternate()
{
}
// Each of these implements an action by calling the standard
// traversal method. Note that (as in the Glow node source)
// we prefix the call to doAction() with the name of the
// class to avoid problems with derived classes.
void
Alternate::getBoundingBox(SoGetBoundingBoxAction *action)
{
Alternate::doAction(action);
}
void
Alternate::GLRender(SoGLRenderAction *action)
{
Alternate::doAction(action);
}
void
Alternate::handleEvent(SoHandleEventAction *action)
{
Alternate::doAction(action);
}
114
Appendix A: Creating a Node
void
Alternate::pick(SoPickAction *action)
{
Alternate::doAction(action);
}
// This implements the traversal for the SoGetMatrixAction,
// which is handled a little differently: it does not
// traverse below the root node or tail of the path it is
// applied to. Therefore, we need to compute the matrix only
// if this group is in the middle of the current path chain
// or is off the path chain (since the only way this could
// be true is if the group is under a group that affects the
// chain).
void
Alternate::getMatrix(SoGetMatrixAction *action)
{
int numIndices;
const int *indices;
// Use SoAction::getPathCode() to determine where this
// group is in relation to the path being applied to (if
// any).
switch (action->getPathCode(numIndices, indices)) {
case SoAction::NO_PATH:
case SoAction::BELOW_PATH:
// If there's no path, or we're off the end, do nothing.
break;
case SoAction::OFF_PATH:
case SoAction::IN_PATH:
// If we are in the path chain or we affect nodes in the
// path chain, traverse the children.
Alternate::doAction(action);
break;
}
}
// This implements the traversal for the SoSearchAction,
// which is also a little different. The search action is
// set to either traverse all nodes in the graph or just
// those that would be traversed during normal traversal. We
// need to check that flag before traversing our children.
Creating a Group Node
115
void
Alternate::search(SoSearchAction *action)
{
// If the action is searching everything, then traverse
// all of our children as SoGroup would.
if (action->isSearchingAll())
SoGroup::search(action);
else {
// First, make sure this node is found if we are
// searching for Alternate (or group) nodes.
SoNode::search(action);
// Traverse the children in our usual way.
Alternate::doAction(action);
}
}
// This implements typical action traversal for an Alternate
// node, skipping every other child.
void
Alternate::doAction(SoAction *action)
{
int numIndices;
const int *indices;
// This will be set to the index of the last (rightmost)
// child to traverse.
int lastChildIndex;
// If this node is in a path, see which of our children
// are in paths, and traverse up to and including the
// rightmost of these nodes (the last one in the
// "indices" array).
if (action->getPathCode(numIndices, indices) ==
SoAction::IN_PATH)
lastChildIndex = indices[numIndices - 1];
// Otherwise, consider all of the children.
else
lastChildIndex = getNumChildren() - 1;
// Now we are ready to traverse the children, skipping
// every other one. For the SoGetBoundingBoxAction, we
116
Appendix A: Creating a Node
// have to do some extra work in between each pair of
// children - we have to make sure the center points get
// averaged correctly.
if (action->isOfType(
SoGetBoundingBoxAction::getClassTypeId())) {
SoGetBoundingBoxAction *bba =
(SoGetBoundingBoxAction *) action;
SbVec3f totalCenter(0.0, 0.0, 0.0);
int numCenters = 0;
for (int i = 0; i <= lastChildIndex; i += 2) {
children->traverse(bba, i);
// If the traversal set a center point in the action,
// add it to the total and reset for the next child.
if (bba->isCenterSet()) {
totalCenter += bba->getCenter();
numCenters++;
bba->resetCenter();
}
}
// Now, set the center to be the average. Since the
// centers were already transformed, there's no need to
// transform the average.
if (numCenters != 0)
bba->setCenter(totalCenter / numCenters, FALSE);
}
// For all other actions, just traverse every other child.
else
for (int i = 0; i <= lastChildIndex; i += 2)
children->traverse(action, i);
}
Using New Node Classes
Node classes you have created must be initialized in every application that
uses them. Example A-7 shows how this is done, using the Glow,Pyramid,
and Alternate node classes dened in the previous examples. The program
reads a le (newNodes.iv, shown in Example A-8) that has a scene graph
containing instances of these nodes. It writes the scene graph to standard
output and then opens an examiner viewer to display the graph.
Using New Node Classes
117
You can see from this example that extender node classes should be
initialized after standard classes, which are initialized by SoDB::init(). In
this program, SoDB::init() is called by SoXt::init(). Also, base classes must
be initialized before any classes derived from them, since the initialization
macros for a node class refer to the parent class.
Notice in Example A-8 that the Pyramid and Glow nodes, because they are
not built into the Inventor library, write out their eld names and types. (The
Alternate class has no elds.) See the discussion of the le format for new
(unknown) nodes in The Inventor Mentor, Chapter 11.
The isBuiltIn ag is a protected variable in SoFieldContainer, from which
SoNode is derived. If this ag is FALSE, eld types are written out along
with the eld values. By default, this ag is FALSE, but all Inventor classes
set it to TRUE. If you are building a toolkit that uses Inventor and want your
new classes to appear the same as Inventor classes, be sure to set this ag to
TRUE.
Example A-7 NewNodes.c++
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/actions/SoWriteAction.h>
#include <Inventor/nodes/SoSeparator.h>
// Header files for new node classes
#include "Glow.h"
#include "Pyramid.h"
#include "Alternate.h"
main(int, char **argv)
{
SoInput myInput;
SoSeparator *root;
// Initialize Inventor and Xt.
Widget myWindow = SoXt::init(argv[0]);
if (myWindow == NULL) exit(1);
// Initialize the new node classes.
Glow::initClass();
Pyramid::initClass();
118
Appendix A: Creating a Node
Alternate::initClass();
if (! myInput.openFile("newNodes.iv")) {
fprintf(stderr, "Can't open \"newNodes.iv\"\n");
return 1;
}
root = SoDB::readAll(&myInput);
if (root == NULL) {
printf("File \"newNodes.iv\" contains bad data\n");
return 2;
}
root->ref();
// Write the graph to stdout.
SoWriteAction wa;
wa.apply(root);
// Render it.
SoXtExaminerViewer *myViewer =
new SoXtExaminerViewer(myWindow);
myViewer->setSceneGraph(root);
//The following results in high-quality transparency and
//will permit PER_PART material binding to vary
//transparency. Delete it and the pyramid will be
//rendered with the same SCREEN_DOOR transparency
//overall.
myViewer->setTransparencyType
(SoGLRenderAction::SORTED_OBJECT_BLEND);
myViewer->setTitle("NewNodes");
myViewer->show();
myViewer->viewAll();
SoXt::show(myWindow);
SoXt::mainLoop();
return 0;
}
Using New Node Classes
119
Example A-8 newNodes.iv
#Inventor V2.0 ascii
#
# Input file for "newNodes" example program
#
Separator {
MaterialBinding { value PER_PART }
Material {
diffuseColor [.3 .6 .3, .9 .3 .2]
transparency [ 0.8 , 0.0]
shininess .5
}
# Skip every other child.
Alternate {
fields []
Pyramid {
fields []
}
Cube {} # This child is skipped
Separator {
MaterialBinding { value OVERALL }
Glow {
fields [ SFColor color , SFFloat brightness ,
SFFloat transparency]
brightness .6
color .8 .3 .3
transparency .9
}
Transform {
translation 3 .6 0
}
Pyramid {
fields [SFFloat height ]
height 3.2
}
}
120
Appendix A: Creating a Node
Sphere {} # This child is skipped.
}
}
Creating an Abstract Node Class
Creating an abstract node class is slightly different from creating a
nonabstract one. Examples of abstract node classes are SoCamera,SoLight,
and SoShape.
First, abstract classes should use the ABSTRACT versions of the macros
described in SoSubNode.h. For example, the SoLight class makes this call in
its initClass() method:
SO_NODE_INIT_ABSTRACT_CLASS(SoLight, "Light", SoNode);
Second, the constructor for an abstract class should be protected, meaning
that it is impossible to create an instance of it.
The copyContents() Method
The copy() method dened for SoNode creates a copy of an instance of a
node. It invokes the virtual copyContents() method. If your node has no
data other than elds and public children, then the copyContents() methods
dened for SoNode and SoGroup should sufce.
However, if you have extra instance data in your node that needs to be
copied, you have to override the copyContents() method. For example, if the
Pyramid node class dened earlier contained a private integer member
variable called count (for some private reason), the copyContents() method
would look like this:
void
Pyramid::copyContents(const SoFieldContainer *fromFC,
SbBool copyConnections)
{
// Copy the usual stuff by calling the base class method.
SoShape::copyContents(fromFC, copyConnections);
The affectsState() Method
121
// Copy the "count" field explicitly.
count = ((Pyramid *)fromFC)->count;
}
The affectsState() Method
The affectsState() method on SoNode indicates whether a node has a net
effect on the state. (For example, SoSeparator changes the state, but it
restores the state, so theres no net effect.) The default value for this method
is TRUE, but some node classes such as SoSeparator,SoShape,SoArray, and
SoMultipleCopy dene it to be FALSE. When you dene a new node class,
you may need to redene its affectsState() method if it differs from that of
the parent class.
Uncacheable Nodes
You may create a new node whose effects should not be cached during
rendering or bounding-box computation. For example, the SoCallback node
allows a user to change the effect of the callback function, such as drawing a
cube instead of a sphere, without ever making an Inventor call.
Uncacheable nodes such as SoCallback should call:
SoCacheElement::invalidate(state)
which aborts construction of the current cache. This call can be made during
the render or bounding box action (the two actions that support caching).
The invalidate() method also turns off auto-caching on any SoSeparator
nodes over the uncacheable node.
122
Appendix A: Creating a Node
Creating an Alternate Representation
When you create a new node, you probably also want to create an alternate
representation for it that can be written to a le. For example, a good
alternate representation for the Glow node would be an SoMaterial node
with all elds ignored except for emissiveColor and transparency. The
alternate representation is in the form of a eld called alternateRep, of type
SoSFNode. If your node is later read into an Inventor application that is not
linked with this new node, Inventor can render the node using this alternate
representation even though the node has not been initialized with the
database. (See Chapter 11 in The Inventor Mentor on reading in extender
nodes and engines.)
Within your program, when a change is made to the original node, you may
want the alternate representation to change as well. In this case, override the
write() method on SoNode to update the alternate representation, and then
have it call the write() method of the base class.
Generating Default Normals
If you dene your own vertex-based shape class and the parent class does
not generate default normals, you need to generate default normals for
rendering with the Phong lighting model and for generating primitives.
SoVertexShape provides the generateDefaultNormals() method. Although
the specics depend on the shape itself, SoNormalBundle provides
methods to facilitate this process.
Tip: If you dene a node class that creates a node sensor attached to itself or
a eld sensor attached to one of its elds, you need to redene
readInstance() so that the sensor doesnt re when the node is read from a
le. Your readInstance() method needs to detach the sensor, call the
readInstance() method of the parent class, and then reattach the sensor.
Node kits provide the setUpConnections() method to make and break these
connections (see Chapter 7 in The Inventor Toolmaker).
123
Index
Numbers
3-vertex polygons, 66
4-vertex polygons, 66
A
abstract node classes, 120
actions
applying, 109
implementing, 78
optimizing, 70
See Also doAction()
terminating, 111
affectsState(), 121
Alternate group class, 111
Alternate group node, 108, 116
alternate representation, 122
AMBIENT_MASK, 44
analyzing rendering performance, 53
applications
optimizing, 47-72
porting, 1
applying actions, 109
automatic normal generation, 12
automatic texture coordinate generation, 12
B
beginShape(), 89
benchmark programs, 48
binding elements, 37
bindings
DEFAULT, 12
NONE, 12
bitmasks, 44
blended transparency, 63
blending, 43
bottlenecks, 49
culling, 66
level of detail, 67
bounding box, 93
C
caching, 81
dependencies, 39
invalidation, 121
normal bindings, 69
optimizing, 55
PER_FACE materials, 69
See also uncacheable nodes
turning off render caching, 69
124
Index
callbacks
nish, 68
generating primitives, 88
start, 68
camera control, 54
CaseVision/Workshop Performance Analyzer, 66
child list, 108
children
hidden, 108
traversing, 109-111
classes
problems caused by changes, 5
clearing windows, 55
clipped objects, 8
CLOCKWISE vertex ordering, 40
colors
changes, 10
diffuse, 46, 69
packed, 46
complexity,9
changes, 9
SCREEN_SPACE, 57
computeBBox(), 93
computeCoordBBox(), 93
constructor
abstract classes, 120
constructors
for actions, 70
nodes, 76
copy(), 36, 120
copyContents(), 36
C pre-processor symbols, 22
creaseAngle default, 8
createLineDetail(), 91
createPointDetail(), 91
createTriangleDetail(), 91
creating details, 90
creating group classes, 108-116
creating nodes, 73-107
creating shape classes, 88-??
culling, 61, 63
bottlenecks, 66
current element, 43
custom classes, 33
for performance improvement, 66
optimizing, 54
porting, 3
cvspeed, 66
D
debugging library, 75
DEFAULT binding, 12, 13
default normal, 12
default texture coordinate, 12
details
creating, 90
DIFFUSE_MASK, 44
diffuse colors, 46, 69
DirectionalLight, 61
disabling notication, 56
-DIV_STRICT, 2, 12
doAction(), 78, 82
double-buffering, 51
downgrading les, 30
DSO, 22, 54
125
Index
E
element bundles, 81
elements
accessing, 80
enabling, 75
setting, 80
EMISSIVE_MASK, 44
enableNotify(), 72
enabling elements, 75
endShape(), 89
engines, 46
enumerated values, 77
event handling performance, 72
examples
group class, 111-116
property class, 83-87
shape class, 94-107
extender API changes, 33
extensions
texture object, 29
F
face set performance, 58
elds, 77
eld types
writing, 117
le format
unknown nodes, 117
le header methods, 23
les
downgrading, 30
reading, 23
writing, 23
le version, 21
nish callback, 68
ags
isBuiltIn, 117
set_version, 22
at-shaded polygons, 63
oating point precision, 23
fog, 62
frame rate, 51
performance goal, 48
G
generatePrimitives(), 88, 89, 91
generating normals, 12, 40
generating primitives, 88
get(), 80, 81
getBoundingBox(), 88, 93
getChildren(), 109
getInstance(), 80
getNum(), 81
getPathCode(), 109
GL_BLENDING, 43
GL_POLYGON_STIPPLE, 43
glColor(), 44
glColorMaterial(), 42
glMaterial, 44
Glow property class, 82, 83
Glow property node, 116
GLRender(), 34, 88, 90
GLRenderBelowPath(), 34
GLRenderInPath(), 34
GLRenderOffPath(), 34
glShadeModel(GL_FLAT), 43
Gouraud-shaded polygons, 63
gr_osview, 49
grabEvents(), 72
126
Index
group classes
creating, 108-116
example, 111-116
groups
hidden children, 108
path code, 109-111
H
hardware-dependent performance, 51
hardware texture mapping, 9
hidden children, 108
I
implementing actions, 78
inheritance
element stack, 76
initClass(), 75, 76
initClass() method
nodes, 120
initialization
node classes, 116
initializing nodes, 75
Internet, 26
invalidate(), 121
invokeLineSegmentCallbacks(), 89
invokePointCallbacks(), 89
invokeTriangleCallbacks(), 89
isBuiltIn ag, 117
isEngineModifying(), 46
isolating rendering, 53
ivAddVP, 30
ivdowngrade, 30
ivx, 31
ivperf, 31, 53
and culling, 66
and OpenGL, 64
camera control, 54
ivview -p, 48
K
keeping records, 51
L
LD_LIBRARY_PATH, 1
level of detail bottlenecks, 67
lights
optimizing, 62
line segment callback function, 89
line segments, 88
LOD node, 26
longToInt32, 18
M
macros
abstract versions, 120
nodes, 74
manipulators, 29
masks, 44
material binding, 62
materialIndex eld, 57
materials
changes, 10
material state elements, 38
measuring performance, 49
127
Index
memory
optimizing, 69
multiple colors, 43
multiple materials, 10
N
node classes
abstract, 120
extender
using, 116-120
initializing, 116, 117
node kits
hidden children, 108
node override, 38, 39
nodes
constructor, 76
creating, 73-107
initializing, 75
macros, 74
uncacheable, 121
NONE binding, 12
normals
changes, 12
default, 12
generating, 40
requirement for SoNormalBinding, 12
notication
disabling, 56
notication overhead, 71
numVertices eld, 13
O
OpenGL, 76, 90
performance, 64
texture object extension, 29
OpenGL benchmark programs, 48
optimizing actions, 70
optimizing applications, 47
optimizing caching, 55
optimizing custom classes, 54
optimizing event handling, 72
optimizing face sets, 58
optimizing level of detail, 67
optimizing lights, 61
optimizing memory usage, 69
optimizing picking, 72
optimizing pixel ll, 63
optimizing rendering, 52
optimizing scene graphs, 31
optimizing textures, 59
optimizing transformations, 58
optimizing vertex transformations, 61
optimizing window clearing, 55
osview
swapbuf number, 49
Override ag, 16
override status
and caching, 57
overriding nodes, 38, 39
P
packed color, 46
path code, 109-111
PER_FACE materials, 69
performance, 47
hardware dependency, 51
hardware limitations, 48
interrupting rendering, 68
keeping records, 51
lights, 61
128
Index
measuring, 49
rendering test, 52
SoTransform, 58
performance goal, 48
picked point, 91
picking, 91
performance, 72
pixel lling
performance, 63
pixie, 66
point callback function, 89
PointLight, 61
points, 88
pop(), 43
porting, 1
custom classes, 3
pre-processor symbols, 22
primitives
generating, 88
performance, 62
primitive shapes, 8
prof, 66
property classes
creating, 82-87
example, 83-84
prototyping, 88
push(), 43
Pyramid shape node, 94, 116
R
rayPick(), 88, 91
read(), 108
reading les, 23
readInstance(), 35
reading children, 35
record-keeping, 51
render caching, 69
renderCaching ON, 60
render culling, 61, 63, 66
rendering, 90
analyzing performance, 53
experiments, 53
interrupt for performance, 68
isolating rendering, 53
optimizing rendering, 52
rendering methods, 34
rendering performance
analyzing with ivperf, 31
reset(), 44
revision symbols, 22
S
scene graphs
optimizing, 31
scene traversal, 55
SCREEN_DOOR transparency, 63
SCREEN_SPACE complexity, 57
sendAllMaterial(), 42
sendDiffuseByIndex(), 43
sending multiple colors, 43
sendNoMaterial(), 42
sendOnlyDiffuseColor(), 42
set(), 80
set_version ag, 22
setDetail(), 90
setDrawStyle(), 14
setFloatPrecision(), 23
setTransparencyType(), 63
shade model, 43
shademodel(FLAT), 63
129
Index
shape classes, 40
creating, 88-107
example, 94-107
SoMaterialBundle, 44
shape hints
changes, 8
element methods, 37
shapes
sending multiple colors, 43
vertex-based, 2
shapeVertex(), 89
shininess, 57
SHININESS_MASK, 44
shouldGLRender()
SoShape method, 90
shouldRayPick()
SoShape method, 91
SO_ENABLE(), 75
SO_ENGINE_OUTPUT, 46
SO_NODE_ADD_FIELD(), 77
SO_NODE_CONSTRUCTOR(), 76
SO_NODE_DEFINE_ENUM_VALUE(), 77
SO_NODE_HEADER(), 74
SO_NODE_INIT_CLASS(), 34, 75
SO_NODE_IS_FIRST_INSTANCE(), 76
SO_NODE_SET_SF_ENUM_TYPE(), 77
SO_NODE_SOURCE(), 74
SO_SWITCH_NODE, 52
SO_VERSION, 22
SO_VERSION_REVISION, 22
SoAction.h, 75
SoAsciiText, 26
SoBoundingBoxAction::apply(), 66
SoCallback, 121
SoCallbackAction, 88
SoChildList, 108
SoClipPlane
clipped objects, 8
wireframe objects, 8
SoColorPacker, 46
SoComplexity, 61
and caching, 57
complexity,9
no hardware texture mapping, 9
textureQuality eld, 9
SoCone
primitive shapes, 8
SoCoordinate, 64
SoCube
primitive shapes, 8
SoCylinder
primitive shapes, 8
SoDB
le header methods, 23
SoDetail, 89
SoDrawStyle
clipped objects, 8
wireframe objects, 8
SoElement, 39
override, 38
SoFaceSet, 64
3-vertex polygons, 66
4-vertex polygons, 66
optimizing, 62
performance, 58
SoFieldContainer, 117
SoFieldContainer enableNotify(), 72
SoFontStyle, 26
SoGLLazyElement, 38
SoGLLazyElement::reset(), 41
SoGLLazyElement::sendAllMaterial(), 42
SoGLLazyElement::sendDiffuseByIndex, 43
SoGLRenderAction::addMethod(), 34
130
Index
SoGLRenderAction::setAbortCallback(), 68
SoGLTextureQualityElement, 37
SoGroup, 108
implementation of copying, 36
SoIndexedFaceSet
3-vertex polygons, 66
4-vertex polygons, 66
performance, 58
SoIndexedShape, 93
SoLabel
caching, 56
SoLazyElement, 38
masks, 44
SoLevelOfDetail, 26
caching, 56
SoLightModel
BASE_COLOR, 62
SoLocateHighlight, 26
SoLOD, 26, 61, 62
caching, 56
optimizing, 67
SoMaterial
multiple materials, 10
override ag, 16
SoMaterialBinding
DEFAULT, 37
NONE, 37
override ag, 16
SoMaterialBundle, 81
new shape nodes, 44
SoNode::copy(), 36
SoNode::enableNotify(), 56
SoNode parameter, 39
SoNonIndexedShape, 13, 93
USE_REST_OF_VERTICES, 13
SoNormal
automatic normal generation, 12
default normal, 12
normal requirement, 12
SoNormalBinding
automatic normal generation, 12
DEFAULT binding, 12
NONE binding, 12
normal requirement, 12
SoNormalBundle, 81
SoNormalGenerator class constructor, 40
SoOutput
le header, 23
isASCIIHeader(), 16
isBinaryHeader(), 16
setFloatPrecision(), 23
SoOverrideElement, 39, 46
SoPickedPoint, 91
SoPickStyle
caching, 56
SoPrimitiveVertex, 89
SoReplacedElement
SoNode parameter, 39
SoSeparator
caching, 56
renderCaching ON, 60
SoSeparator::cullTest(), 66
SoShape, 88
SoShape::beginSolidShape(), 42
SoShape::endSolidShape(), 42
SoShapeHints
clipped objects, 8
creaseAngle default, 8
primitive shapes, 8
shapeType, 62
vertexOrdering, 62
wireframe objects, 8
SoShapeHintsElement
method changes, 37
131
Index
SoSphere
primitive shapes, 8
SoSubNode.h, 74
SoSwitch
whichChild eld, 52
SoText2
and caching, 57
SoTexture2
no hardware texture mapping, 9
textureQuality eld, 9
SoTextureBlendColorElement, 38
SoTextureCoordinate2
automatic coordinate generations, 12
default texture coordinate, 12
texture coordinate requirement, 13
SoTextureCoordinateBinding
DEFAULT binding, 13
SoTextureCoordinateBundle, 81
SoTextureModelElement, 38
SoTextureQualityElement, 37
SoTextureWrapSElement, 38
SoTextureWrapTElement, 38
SoTransform
performance, 58
SoTransformerManip, 29
SoTriangleStripSet
numVertices eld, 13
SoType::overrideType(), 34
SoVertexProperty, 24
SoVertexShape
complexity,9
SoWWWAnchor, 26
SoWWWInline, 26
SoXtViewer
texture mapping, 14
viewers, 14
SPECULAR_MASK, 44
sphere
rayPick(), 92-93
SpotLight, 61
start callback, 68
startNotify(), 71
state elements, 81
stipple transparency, 43
swapbuf in osview, 49
T
terminating actions, 111
texture coordinates
automatic generation, 12
changes, 12
default, 12
requirement, 13
texture mapping
and viewers, 14
texture object extension, 29
texture quality element, 37
textureQuality eld, 9
textures
optimizing, 59
The Inventor Mentor,90, 122
transformations
optimizing, 58
transparency, 43
and diffuse colors, 46
blended, 63
SCREEN_DOOR, 63
traversal, 55
traversal state
enabling elements, 75
traverse(), 108
traverseChildren(), 108
132
Index
triangle callback function, 88
triangle fans, 89
triangles, 88
triangle strips, 89
U
uncacheable nodes, 121
unknown nodes
le format, 117
USE_REST_OF_VERTICES, 13
user-dened le headers, 23
V
version number, 22
vertex-based shapes
changes, 13
vertex property eld, 2
vertex ordering
CLOCKWISE, 40
vertex properties
converting les, 30
vertexProperty eld, 2, 30
vertex property node, 24
vertex transformations
performance, 61
viewAll(), 54
viewers
and texture mapping, 14
Virtual Reality Modeling Language, 26
VRML
nodes, 26
W
wireframe, 63
wireframe objects, 8
write(), 108
writing les, 23
Tell Us About This Manual
As a user of Silicon Graphics products, you can help us to better understand your needs
and to improve the quality of our documentation.
Any information that you provide will be useful. Here is a list of suggested topics:
General impression of the document
Omission of material that you expected to nd
Technical errors
Relevance of the material to the job you had to do
Quality of the printing and binding
Please send the title and part number of the document with your comments. The part
number for this document is 007-3078-001.
Thank you!
Three Ways to Reach Us
To send your comments by electronic mail, use either of these addresses:
On the Internet: techpubs@sgi.com
For UUCP mail (through any backbone site): [your_site]!sgi!techpubs
To fax your comments (or annotated copies of manual pages), use this
fax number: 650-932-0801
To send your comments by traditional mail, use this address:
Technical Publications
Silicon Graphics, Inc.
2011 North Shoreline Boulevard, M/S 535
Mountain View, California 94043-1389

Navigation menu