Class SoVertexShape

  • All Implemented Interfaces:
    Direct Known Subclasses:
    SoIndexedShape, SoNonIndexedShape

    public abstract class SoVertexShape
    extends SoShape
    Abstract base class for all vertex-based shape nodes. This node is the abstract base class for all vertex-based shape (geometry) nodes. It is used as a repository for convenience functions for subclasses and to provide a type identifier to make it easy to determine whether a shape is vertex-based. It contains one public field, the SoVertexProperty field.

    All subclasses of this node draw objects constructed from vertices. If the vertexProperty field is non-null and there are coordinates in the associated vertex property node, then those coordinates are used. Otherwise the objects are drawn using the current coordinates in the state. The coordinates of the shape are transformed by the current transformation matrix and are drawn with the current light model and drawing style.

    Subclasses that construct polygons from vertices may not render or pick correctly if any of their polygons are self-intersecting or non-planar.

    All vertex shape subclasses use the bounding box of the shape to determine default texture coordinates. The longest dimension of the bounding box defines the S coordinates, and the next longest defines the T coordinates. The value of the S coordinate ranges from 0 to 1, from one end of the bounding box to the other. The T coordinate ranges between 0 and the ratio of the second greatest dimension of the bounding box to the greatest dimension.

    When a vertex-based shape is picked with an SoRayPickAction, a detail is always returned. If the shape is composed of faces (such as SoFaceSet or SoTriangleStripSet), an SoFaceDetail is returned. If the shape is composed of line segments (such as SoLineSet), an SoLineDetail is returned. If the shape is composed of points (such as SoPointSet), an SoPointDetail is returned. Note that the type of detail returned is not affected by the current drawing style.

    Similarly, each class of vertex-based shape invokes appropriate callbacks if those callbacks are registered with the SoCallbackAction. Shapes made of faces invoke triangle callbacks for each generated triangle. (Faces may be triangulated to create these triangles.) Shapes made of line segments invoke line segment callbacks for each segment, and shapes made of points invoke point callbacks.

    The subclass SoIndexedShape is a base class for vertex-based shapes that index into the current set of coordinates. The subclass SoNonIndexedShape is a base class for vertex-based shapes that use the current coordinates in order.

    Rendering Performance and OpenGL rendering

    It is not necessary for Open Inventor developers to have a deep understanding of OpenGL rendering. However we use the names of some OpenGL features throughout the Open Inventor documentation. So it can be helpful to have a basic understanding of what these names mean.

    Immediate mode:
    In the original OpenGL, geometry was always passed in "immediate mode" (glBegin mode). In this mode geometry data is passed to OpenGL one vertex at a time, using functions like glBegin(), glVertex() and glNormal(). This was a breakthrough in terms of application flexibility because the graphics API no longer imposed its own data structures on the application. However this interface has significant drawbacks. The data remains stored on the CPU, the data must be passed to OpenGL every frame and a lot of CPU time is spent in function call overhead. Immediate mode is no longer part of the OpenGL "core" profile, but is still supported by all graphics hardware vendors through the "compatibility" profile. Open Inventor supports rendering using OpenGL immediate mode as a "last resort" fallback strategy for certain cases. This is transparent to the application.

    Display lists:
    To offset the limitations of immediate mode, the original OpenGL provided a retained mode called "display lists". These allow a series of graphics commands to be grouped together and later invoked with a single function call. Display lists also allow the graphics driver to analyze and optimize the geometry contained inside, resulting in much higher performance. The main drawbacks of display lists are that they cannot be edited, making them inefficient for geometry that changes, and that the geometry data is duplicated inside the display list. Display lists are no longer part of the OpenGL core profile, but are still supported and are still very useful in some cases. Part of Open Inventor's render caching mechanism is the automatic creation of display lists that encapsulate portions of the scene graph. During a render traversal it is often possible to invoke a display list and completely avoid the CPU overhead of traversing that portion of the scene graph. However Open Inventor does not store large geometries in display lists. This avoids duplicating large data and is still efficient because a large geometry can be effectively rendered using one of the mechanisms we will discuss next.

    Vertex Arrays:
    To improve the efficiency of passing geometry to the graphics hardware (compared to using glBegin, glVertex, etc), OpenGL added "Vertex Arrays". Vertex arrays allow complete arrays of coordinates and vertex attribute data (normals, colors, etc) to be passed by specifying the address of each array. The graphics driver can then transfer data from the CPU to the GPU in (potentially) large blocks. It is also possible for the application to efficiently modify the values in the vertex arrays. Some drawbacks of this mechanism are that vertex arrays are application allocated CPU memory, so the data must be transferred to the GPU each time the geometry is rendered (i.e. on each render traversal) and the driver cannot use efficient DMA transfers. Another is that the graphics driver has no way to know if the data has been changed since the last transfer.

    Vertex Buffer Objects (VBO):
    Vertex Buffer Objects, commonly called VBOs, were added in OpenGL version 1.5 to provide many of the benefits of immediate mode, display lists and vertex arrays, while avoiding some of their limitations. You can think of VBOs as a "special case" of vertex arrays. If vertex arrays cannot be used, then VBOs cannot be used. Because VBO memory is created and managed by OpenGL, the geometry data may be stored directly in GPU memory, avoiding the overhead of transfer from the CPU (after the initial transfer). If storing in GPU memory is not possible, the data can still be stored in CPU memory locations that allow fast DMA (direct memory access), so the transfer to the GPU is much faster. VBO memory can also be modified without (necessarily) transferring the entire buffer again. Open Inventor uses VBOs to render geometry whenever possible. In the unusual case that VBOs cannot be used, Open Inventor will still use vertex arrays if possible. The SoBufferedShape node and SoBufferObject classes allow the application to directly manage VBOs, but in most cases Open Inventor's automatic management is sufficient and much more convenient. In the documentation and the following discussions, vertex array, vertex buffer and VBO can generally be considered the same thing unless specifically stated. In order to use vertex buffers, Open Inventor may have to copy and/or reorganize the application's geometry data. This is convenient, works well and is a good solution for many cases. In a few cases the application may want to understand the issues and organize its geometry data to minimize this extra work. In order to explain this, we'll need to understand a few more details about OpenGL rendering.

    OpenGL rendering:

    OpenGL supports both indexed and non-indexed definition of geometry. This is exactly the difference between, for example, SoIndexedFaceSet and SoFaceSet. If vertices are shared between faces, then we can provide an additional array containing a list of indices that specify which vertices to use for each face. If all the faces are defined separately, then no vertices are shared and only the array of vertices is needed. But in most cases your geometry will have shared vertices. An index value is typically four bytes (or less), but a vertex value is twelve bytes. So even with the additional memory for indices, the total memory required for geometry with shared vertices is usually much less using an indexed representation.

    To maximize performance and minimize driver/hardware complexity, OpenGL imposes some limitations on the definition of geometry. You will probably begin to see already some of the mismatch with the more general model provided by Open Inventor. Some of the important limitations are: Only one type of primitive per draw call, only one list of indices per draw call and only per-vertex attributes (for example normal vectors).

    Note that it's not always possible to share vertices at the OpenGL level. To illustrate the problem, imagine you want to render a cube using OpenGL. Clearly each vertex (corner of the cube) is shared by three faces. That works OK for the coordinates. But if you need normal vectors for lighting, then you need a different normal for each face of the cube and each shared vertex can only have one associated normal. Therefore the only way to render a cube with normal vectors is to use multiple copies of each vertex, each copy having a different normal vector. So in some cases it can be more efficient to use a non-indexed representation. This issue comes up any time you need per-face normal vectors. Open Inventor handles these cases automatically (although it may be necessary to reorganize the geometry data internally.

    Controlling use of VBOs and Vertex Arrays:

    Use of Vertex Buffers and Vertex Arrays is controlled by the SoShapeHints node and by some environment variables, which can be set using SoPreferences or a configuration file.

    SoShapeHints useVBO field:
    You can use this field to prohibit using VBOs for a specific shape or group of shapes. The default is true, meaning that all shapes should use VBO (or at least VA) when possible. In most cases there is no reason to set this field. Open Inventor will automatically choose the best rendering strategy. For example, VBOs generally provide the best performance for large shapes (many vertices), but not necessarily for small shapes. In specific cases it may be useful to influence these decisions using this field and/or the related environment variables (discussed next). For testing (for example) you can change the default value of this field to false by setting the environment variable OIV_FORCE_USE_VBO.

    Despite the name, this environment variable just sets the default value for the useVBO field of SoShapeHints nodes created by the application. The default is true. Setting the variable to true or false does not guarantee VBOs will (or will not) be used, because the application might set the useVBO field on some instances of SoShapeHints. Note that vertex arrays will still be used unless specifically prohibited.

    You can use this environment variable to globally prohibit use of vertex arrays and VBOs. The default is false. Note the "double negative" name. False means that vertex arrays and VBOs are allowed. In most cases there is no reason to set this variable except possibly to test performance without vertex arrays or in the unlikely event that there is a serious bug with the VBO implementation in the current driver/GPU.

    For small shapes (i.e. a small number of vertices) it might be more efficient to use vertex arrays but not VBOs. This environment variable allows you to specify the minimum number of vertices that a shape must have in order to render using VBOs. The default is 0, meaning that all shapes are allowed to use VBOs. You can also disable VBOs for a specific shape, or group of shapes, using the SoShapeHints useVBO field.

    For small shapes (i.e. a small number of vertices) it may be more efficient to use immediate mode (glBegin) instead of doing all the setup to use vertex arrays and VBOs. This environment variable allows you to specify the minimum number of vertices that a shape must have in order to render using vertex arrays. The default is 0, meaning that all shapes are allowed to use vertex arrays. If vertex arrays are prohibited, then VBOs are also prohibited.

    In general, render caches (display lists) improve rendering performance for small(ish) shapes because the graphics driver will automatically optimize the geometry. And render caches are valuable because they reduce CPU overhead by avoiding traversal of the cached sub-scene graph. However if a large shape can be rendered using vertex arrays or VBOs, then the render performance probably will not be improved by including the shape in a render cache. (The reduced CPU overhead advantage of the render cache might still be useful in some cases. This environment variable basically allows you to specify the threshold number of vertices that defines a "large shape". If a render cache is already open (being built) when the shape is traversed, and the number of vertices is less than this value, then the shape will not use VA/VBO. Conversely, if no render cache is being built when the shape is traversed, and the number of vertices is greater than or equal to this value, then the shape will be rendered using VA/VBO (if possible) and render caching will not be allowed for the current SoSeparator. The default value is 10000 vertices. The application should be aware that Separators containing large shapes may not build a render cache and arrange the scene graph so that other shapes can be render cached.

    Inconsistent Shapes:

    An inconsistent shape is a shape that contains inconsistent data, for example, a SoIndexedTriangleSet with a coordIndex containing out of range indices. Inconsistent shapes are NEVER rendered. An error message is posted if using a debug build.

    File format/default:

    This is an abstract class. See the reference page of a derived class for the format and default values.

    See Also:
    SoIndexedShape, SoNonIndexedShape, SoVertexProperty
    • Field Detail

      • vertexProperty

        public final SoSFNode vertexProperty
        vertex property node.
    • Method Detail

      • getNormalCache

        public SoNormalCache getNormalCache()
        Returns the current normal cache, or NULL if there is none. Normal cache will only exist if the node has been traversed by a render action and Inventor needed to compute normals.
      • getTangentCache

        public SoTangentCache getTangentCache()
        Returns the current tangent cache, or NULL if there is none. Tangent cache will only exist if the node has been traversed by a render action and Inventor needed to compute tangents.